您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Tracks loot, stats, Cyber Components, with theme switcher, keyboard shortcut, opacity control, click-through toggle, and collapsible controls.
当前为
// ==UserScript== // @name Cybroria Loot Tracker v4.3 (Complete) // @namespace http://tampermonkey.net/ // @version 4.3 // @description Tracks loot, stats, Cyber Components, with theme switcher, keyboard shortcut, opacity control, click-through toggle, and collapsible controls. // @author Skydragon // @match https://cybroria.com/* // @grant none // @license MIT // ==/UserScript== (function () { 'use strict'; const STORAGE_KEY = 'cybroria_loot_stats'; const VALUE_KEY = 'cybroria_loot_values'; const POS_KEY = 'cybroria_tracker_position'; const RESET_TIME_KEY = 'cybroria_reset_time'; const CYBER_BASE_KEY = 'cybroria_cyber_components_base'; const CYBER_GAIN_KEY = 'cybroria_cyber_components_gain'; const OPACITY_KEY = 'cybroria_tracker_opacity'; const THEME_KEY = 'cybroria_tracker_theme'; const CLICKTHRU_KEY = 'cybroria_tracker_clickthrough'; const UI_COLLAPSE_KEY = 'cybroria_tracker_controls_hidden'; let timestamps = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]'); let lootValues = JSON.parse(localStorage.getItem(VALUE_KEY) || '{}'); let viewMode = 'hour'; let panelOpacity = parseFloat(localStorage.getItem(OPACITY_KEY)) || 0.6; let theme = localStorage.getItem(THEME_KEY) || 'dark'; let clickThrough = localStorage.getItem(CLICKTHRU_KEY) === 'true'; let controlsHidden = localStorage.getItem(UI_COLLAPSE_KEY) === 'true'; const trackedTypes = [ "Strength", "Agility", "Dexterity", "Vitality", "Energy Tap", "System Breach", "Chemsynthesis", "Cyber Harvest", "Credits", "Power Cells", "Logic Cores", "Artifacts", "Neuro Stims", "Cyber Implants", "Cyber Components" ]; if (!localStorage.getItem(RESET_TIME_KEY)) { localStorage.setItem(RESET_TIME_KEY, Date.now()); } if (!localStorage.getItem(CYBER_GAIN_KEY)) { localStorage.setItem(CYBER_GAIN_KEY, '0'); } const trackerBox = document.createElement('div'); trackerBox.id = 'cybroria-tracker'; trackerBox.style.position = 'fixed'; trackerBox.style.top = localStorage.getItem(POS_KEY + '_top') || '10px'; trackerBox.style.left = localStorage.getItem(POS_KEY + '_left') || '10px'; applyPanelStyles(); function applyPanelStyles() { const base = trackerBox.style; const glow = '0 0 12px rgba(0,255,100,0.4)'; const light = '0 0 8px rgba(255,255,255,0.3)'; base.background = theme === 'dark' ? `rgba(0, 0, 0, ${panelOpacity})` : `rgba(255, 255, 255, ${panelOpacity})`; base.color = theme === 'dark' ? '#cfc' : '#333'; base.borderRadius = '10px'; base.boxShadow = theme === 'dark' ? glow : light; base.border = theme === 'dark' ? '1px solid rgba(0,255,100,0.3)' : '1px solid rgba(0,0,0,0.2)'; base.textShadow = theme === 'dark' ? '0 0 4px rgba(0,255,100,0.3)' : 'none'; base.fontFamily = 'monospace'; base.fontSize = '13px'; base.padding = '12px'; base.zIndex = 9999; base.cursor = 'move'; base.minWidth = '240px'; base.pointerEvents = clickThrough ? 'none' : 'auto'; } document.body.appendChild(trackerBox); const timer = document.createElement('div'); timer.id = 'cybroria-timer'; timer.style.marginTop = '8px'; timer.style.fontSize = '11px'; timer.style.color = theme === 'dark' ? '#8f8' : '#555'; trackerBox.appendChild(timer); function makeDraggable(el) { let offsetX, offsetY, dragging = false; el.addEventListener('mousedown', (e) => { if (e.target.tagName === 'BUTTON' || e.target.tagName === 'SELECT' || e.target.classList.contains('icon')) return; dragging = true; offsetX = e.clientX - el.offsetLeft; offsetY = e.clientY - el.offsetTop; }); document.addEventListener('mousemove', (e) => { if (!dragging) return; el.style.left = `${e.clientX - offsetX}px`; el.style.top = `${e.clientY - offsetY}px`; }); document.addEventListener('mouseup', () => { if (dragging) { localStorage.setItem(POS_KEY + '_top', el.style.top); localStorage.setItem(POS_KEY + '_left', el.style.left); } dragging = false; }); } makeDraggable(trackerBox); // Ctrl+L to toggle visibility window.addEventListener('keydown', (e) => { if (e.ctrlKey && e.key.toLowerCase() === 'l') { trackerBox.style.display = trackerBox.style.display === 'none' ? 'block' : 'none'; } }); function renderControls() { const controlsWrapper = document.createElement('div'); controlsWrapper.id = 'tracker-controls'; const collapseToggle = document.createElement('div'); collapseToggle.textContent = controlsHidden ? '[+]' : '[–]'; collapseToggle.style.cursor = 'pointer'; collapseToggle.style.marginBottom = '6px'; collapseToggle.style.fontWeight = 'bold'; collapseToggle.onclick = () => { controlsHidden = !controlsHidden; localStorage.setItem(UI_COLLAPSE_KEY, controlsHidden); updateBox(); }; trackerBox.appendChild(collapseToggle); if (controlsHidden) return; const resetBtn = document.createElement('button'); resetBtn.textContent = 'Reset'; resetBtn.onclick = () => { if (confirm('Reset all loot stats?')) { timestamps = []; localStorage.removeItem(STORAGE_KEY); localStorage.setItem(RESET_TIME_KEY, Date.now()); localStorage.setItem(CYBER_GAIN_KEY, '0'); localStorage.removeItem(CYBER_BASE_KEY); updateBox(); } }; const exportBtn = document.createElement('button'); exportBtn.textContent = 'Export CSV'; exportBtn.style.marginLeft = '6px'; exportBtn.onclick = exportCSV; const modeSelect = document.createElement('select'); modeSelect.style.marginLeft = '6px'; ['hour', 'day', 'session'].forEach(mode => { const opt = document.createElement('option'); opt.value = mode; opt.textContent = `Per ${mode}`; modeSelect.appendChild(opt); }); modeSelect.value = viewMode; modeSelect.onchange = () => { viewMode = modeSelect.value; updateBox(); }; const settingsBtn = document.createElement('span'); settingsBtn.textContent = '⚙️'; settingsBtn.title = 'Set values'; settingsBtn.className = 'icon'; settingsBtn.style.marginLeft = '8px'; settingsBtn.style.cursor = 'pointer'; settingsBtn.onclick = showSettingsPopup; controlsWrapper.appendChild(resetBtn); controlsWrapper.appendChild(exportBtn); controlsWrapper.appendChild(modeSelect); controlsWrapper.appendChild(settingsBtn); trackerBox.appendChild(controlsWrapper); } function updateTimer() { const resetTime = parseInt(localStorage.getItem(RESET_TIME_KEY), 10); const seconds = Math.floor((Date.now() - resetTime) / 1000); const hrs = String(Math.floor(seconds / 3600)).padStart(2, '0'); const mins = String(Math.floor((seconds % 3600) / 60)).padStart(2, '0'); const secs = String(seconds % 60).padStart(2, '0'); const timerEl = document.getElementById('cybroria-timer'); if (timerEl) { timerEl.textContent = `⏱ since last reset: ${hrs}:${mins}:${secs}`; } } setInterval(updateTimer, 1000); function exportCSV() { let csv = 'Item,Amount\n'; const totals = {}; timestamps.forEach(t => { if (!totals[t.item]) totals[t.item] = 0; totals[t.item] += t.amount; }); for (const item in totals) { csv += `${item},${totals[item]}\n`; } const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = 'cybroria_loot.csv'; link.click(); URL.revokeObjectURL(url); } function showSettingsPopup() { const popup = document.createElement('div'); popup.style.position = 'fixed'; popup.style.top = '50%'; popup.style.left = '50%'; popup.style.transform = 'translate(-50%, -50%)'; popup.style.background = '#222'; popup.style.color = '#fff'; popup.style.padding = '20px'; popup.style.border = '2px solid #0f0'; popup.style.zIndex = 10000; popup.style.maxHeight = '80vh'; popup.style.overflowY = 'auto'; const closeBtn = document.createElement('button'); closeBtn.textContent = 'Close'; closeBtn.style.marginTop = '10px'; closeBtn.onclick = () => popup.remove(); const inputItems = [ "Artifacts", "Power Cells", "Logic Cores", "Cyber Implants", "Neuro Stims", "Cyber Components" ]; lootValues.Credits = 1; inputItems.forEach(item => { const label = document.createElement('label'); label.textContent = `${item} price: `; label.style.display = 'block'; const input = document.createElement('input'); input.type = 'number'; input.value = lootValues[item] || ''; input.style.marginBottom = '6px'; input.onchange = () => { lootValues[item] = parseFloat(input.value) || 0; localStorage.setItem(VALUE_KEY, JSON.stringify(lootValues)); updateBox(); }; label.appendChild(input); popup.appendChild(label); }); // Opacity Slider const opacityLabel = document.createElement('label'); opacityLabel.textContent = `Panel Opacity: ${Math.round(panelOpacity * 100)}%`; opacityLabel.style.display = 'block'; const opacitySlider = document.createElement('input'); opacitySlider.type = 'range'; opacitySlider.min = '0.1'; opacitySlider.max = '1'; opacitySlider.step = '0.05'; opacitySlider.value = panelOpacity; opacitySlider.oninput = () => { panelOpacity = parseFloat(opacitySlider.value); localStorage.setItem(OPACITY_KEY, panelOpacity); opacityLabel.textContent = `Panel Opacity: ${Math.round(panelOpacity * 100)}%`; applyPanelStyles(); }; opacityLabel.appendChild(opacitySlider); popup.appendChild(opacityLabel); // Theme Switch const themeLabel = document.createElement('label'); themeLabel.textContent = `Theme: `; themeLabel.style.display = 'block'; const themeSelect = document.createElement('select'); ['dark', 'light'].forEach(t => { const opt = document.createElement('option'); opt.value = t; opt.textContent = t.charAt(0).toUpperCase() + t.slice(1); themeSelect.appendChild(opt); }); themeSelect.value = theme; themeSelect.onchange = () => { theme = themeSelect.value; localStorage.setItem(THEME_KEY, theme); applyPanelStyles(); updateBox(); }; themeLabel.appendChild(themeSelect); popup.appendChild(themeLabel); // Click-through toggle const clickBox = document.createElement('input'); clickBox.type = 'checkbox'; clickBox.checked = clickThrough; clickBox.onchange = () => { clickThrough = clickBox.checked; localStorage.setItem(CLICKTHRU_KEY, clickThrough); applyPanelStyles(); }; const clickLabel = document.createElement('label'); clickLabel.textContent = ' Enable Click-Through Mode'; clickLabel.prepend(clickBox); clickLabel.style.display = 'block'; popup.appendChild(clickLabel); popup.appendChild(closeBtn); document.body.appendChild(popup); } function updateBox() { localStorage.setItem(STORAGE_KEY, JSON.stringify(timestamps)); const now = Date.now(); const hourAgo = now - 3600000; const dayAgo = now - 86400000; const stats = {}; timestamps.forEach(entry => { const show = viewMode === 'session' || (viewMode === 'hour' && entry.time >= hourAgo) || (viewMode === 'day' && entry.time >= dayAgo); if (!show) return; if (!stats[entry.item]) stats[entry.item] = 0; stats[entry.item] += entry.amount; }); let totalIncome = 0; const html = []; html.push('<strong>Cybroria Loot Tracker</strong><br><br>'); html.push(`<u>Per ${viewMode.charAt(0).toUpperCase() + viewMode.slice(1)}:</u><br><br>`); const sections = { "Fighting Stats": ["Strength", "Agility", "Dexterity", "Vitality"], "Extraction Stats": ["Energy Tap", "System Breach", "Chemsynthesis", "Cyber Harvest"], "Currency": ["Credits", "Artifacts", "Cyber Components"], "Extraction Loot": ["Power Cells", "Logic Cores", "Cyber Implants", "Neuro Stims"] }; for (const [title, items] of Object.entries(sections)) { const group = items.map(item => { if (!stats[item]) return null; const amount = stats[item]; const value = lootValues[item] ? amount * lootValues[item] : 0; totalIncome += value; return `${item}: ${amount.toLocaleString()}`; }).filter(Boolean); if (group.length) { html.push(`<strong>${title}:</strong><br>${group.join('<br>')}<br><br>`); } } if (totalIncome > 0) { html.push(`<strong>Total Income:</strong> ${totalIncome.toLocaleString()} credits<br>`); } trackerBox.innerHTML = html.join(''); trackerBox.appendChild(timer); renderControls(); } function parseLootText(text) { text = text.replace(/\u00A0/g, ' ') .replace(/\s+/g, ' ') .replace(/\([\d,]+ for your syndicate\)/g, '') .trim(); const statMatch = text.match(/You have found ([\\d,]+) ([A-Za-z ]+?) Stat Value/i); if (statMatch) { const amount = parseInt(statMatch[1].replace(/,/g, ''), 10); const statName = statMatch[2].trim(); if (trackedTypes.includes(statName)) { timestamps.push({ time: Date.now(), item: statName, amount }); updateBox(); } return; } const lootMatch = text.match(/You have found ([\\d,]+) ([A-Za-z ]+)/); if (lootMatch) { const qty = parseInt(lootMatch[1].replace(/,/g, ''), 10); const item = lootMatch[2].trim(); if (trackedTypes.includes(item)) { timestamps.push({ time: Date.now(), item, amount: qty }); updateBox(); } } } function observeLootLog() { const seenLines = new Set(); setInterval(() => { const logSpans = document.querySelectorAll('app-loot-log span.ng-star-inserted'); logSpans.forEach(span => { const rawText = span.textContent.trim(); if (rawText.includes('You have found') && !seenLines.has(rawText)) { seenLines.add(rawText); parseLootText(rawText); } }); }, 1000); } function trackCyberComponentDelta() { setInterval(() => { const el = Array.from(document.querySelectorAll('*')).find(e => e.textContent && e.textContent.includes('Cyber Components') ); if (el) { const match = el.textContent.match(/Cyber Components\\s*:?\\s*([\\d,]+)/i); if (match) { const current = parseInt(match[1].replace(/,/g, ''), 10); const base = parseInt(localStorage.getItem(CYBER_BASE_KEY) || current); const gain = current - base; localStorage.setItem(CYBER_GAIN_KEY, gain.toString()); localStorage.setItem(CYBER_BASE_KEY, base.toString()); updateBox(); } } }, 2000); } window.addEventListener('load', () => { setTimeout(() => { observeLootLog(); trackCyberComponentDelta(); }, 3000); updateBox(); }); })();