您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
RR panel with wins/losses, winrate, streak, profit — visible on menu (“Password”), mini-icons + colored compact profit when collapsed; drag & collapse always work; remembers collapsed state, position, and auto-hide preference; tracks during play.
当前为
// ==UserScript== // @name TornPDA RR Tracker v4.8 (Auto-Hide Toggle) // @namespace https://greasyfork.org/users/1493252 // @version 4.8 // @description RR panel with wins/losses, winrate, streak, profit — visible on menu (“Password”), mini-icons + colored compact profit when collapsed; drag & collapse always work; remembers collapsed state, position, and auto-hide preference; tracks during play. // @match https://www.torn.com/page.php?sid=russianRoulette* // @grant none // @run-at document-idle // @license MIT // ==/UserScript== (function waitUntilReady() { const PANEL_ID = 'rr-tracker-panel'; const STORAGE = 'torn_rr_tracker_results'; const POS_KEY = 'rr_panelPos'; const COLLAPSE_KEY = 'rr_panelCollapsed'; const AUTOHIDE_KEY = 'rr_autoHide'; const MAX = 100; if (document.getElementById(PANEL_ID)) return; if (!document.body.innerText.includes('Password') && !document.body.innerText.includes('POT MONEY')) { return setTimeout(waitUntilReady, 200); } let lastPot = 0; let roundActive = true; let hasTracked = false; let results = JSON.parse(localStorage.getItem(STORAGE) || '[]'); let collapsed = JSON.parse(localStorage.getItem(COLLAPSE_KEY) || 'false'); let autoHide = JSON.parse(localStorage.getItem(AUTOHIDE_KEY) || 'true'); const panel = document.createElement('div'); panel.id = PANEL_ID; Object.assign(panel.style, { position: 'fixed', top: '12px', left: '12px', background: 'rgba(0,0,0,0.6)', color: '#fff', fontFamily: 'monospace', fontSize: '14px', padding: '36px 12px 12px', borderRadius: '10px', boxShadow: '0 0 12px rgba(255,0,0,0.3)', zIndex: '9999999', userSelect: 'none', display: 'flex', flexDirection: 'column', gap: '8px', minWidth: '140px' }); document.body.appendChild(panel); try { const pos = JSON.parse(localStorage.getItem(POS_KEY) || '{}'); if (pos.top && pos.left) { panel.style.top = pos.top; panel.style.left = pos.left; } } catch {} const miniBar = document.createElement('div'); Object.assign(miniBar.style, { display: 'none', flexWrap: 'wrap', gap: '2px', padding: '4px 0' }); panel.appendChild(miniBar); const profitMini = document.createElement('div'); Object.assign(profitMini.style, { display: 'none', fontSize: '14px', fontFamily: 'monospace', margin: '2px 0' }); panel.appendChild(profitMini); const statusDiv = document.createElement('div'); Object.assign(statusDiv.style, { position: 'absolute', top: '8px', left: '8px', width: '20px', height: '20px', fontSize: '18px', cursor: 'pointer', color: 'rgba(255,255,255,0.7)' }); panel.appendChild(statusDiv); const dragHandle = document.createElement('div'); dragHandle.textContent = '☰'; Object.assign(dragHandle.style, { position: 'absolute', top: '8px', right: '8px', width: '20px', height: '20px', fontSize: '18px', cursor: 'move', color: 'rgba(255,255,255,0.7)', touchAction: 'none' }); panel.appendChild(dragHandle); const statsGroup = document.createElement('div'); Object.assign(statsGroup.style, { display: 'flex', flexDirection: 'column', gap: '4px' }); panel.appendChild(statsGroup); const profitDiv = document.createElement('div'); const winrateDiv = document.createElement('div'); const streakDiv = document.createElement('div'); statsGroup.append(profitDiv, winrateDiv, streakDiv); const resultsContainer = document.createElement('div'); Object.assign(resultsContainer.style, { maxHeight: '140px', overflowY: 'auto', marginTop: '4px' }); panel.appendChild(resultsContainer); const resetBtn = document.createElement('button'); resetBtn.textContent = '🔄 Reset'; Object.assign(resetBtn.style, { alignSelf: 'flex-start', background: 'rgba(255,255,255,0.1)', color: '#fff', border: 'none', borderRadius:'6px', padding: '4px 8px', cursor: 'pointer' }); resetBtn.onmouseenter = () => resetBtn.style.background = 'rgba(255,255,255,0.2)'; resetBtn.onmouseleave = () => resetBtn.style.background = 'rgba(255,255,255,0.1)'; resetBtn.onclick = () => { if (confirm('Clear all results and reset profit?')) { results = []; saveResults(); lastPot = 0; roundActive = true; hasTracked = false; refreshAll(); } }; panel.appendChild(resetBtn); const autoHideBtn = document.createElement('button'); autoHideBtn.textContent = autoHide ? 'Auto-Hide: On' : 'Auto-Hide: Off'; Object.assign(autoHideBtn.style, { alignSelf: 'flex-start', background: 'rgba(255,255,255,0.1)', color: '#fff', border: 'none', borderRadius:'6px', padding: '4px 8px', cursor: 'pointer', marginTop: '4px' }); autoHideBtn.onmouseenter = () => autoHideBtn.style.background = 'rgba(255,255,255,0.2)'; autoHideBtn.onmouseleave = () => autoHideBtn.style.background = 'rgba(255,255,255,0.1)'; autoHideBtn.onclick = () => { autoHide = !autoHide; localStorage.setItem(AUTOHIDE_KEY, JSON.stringify(autoHide)); autoHideBtn.textContent = autoHide ? 'Auto-Hide: On' : 'Auto-Hide: Off'; refreshAll(); }; panel.appendChild(autoHideBtn); const saveResults = () => localStorage.setItem(STORAGE, JSON.stringify(results)); const saveCollapsed = () => localStorage.setItem(COLLAPSE_KEY, JSON.stringify(collapsed)); const makeCircle = color => { const el = document.createElement('span'); Object.assign(el.style, { display: 'inline-block', width: '12px', height: '12px', borderRadius: '50%', backgroundColor: color, marginRight: '2px' }); return el; }; function updateStatus() { statusDiv.textContent = collapsed ? '▪' : (roundActive ? '►' : '▸'); } function updatePanelVisibility() { if (!autoHide) { panel.style.display = 'flex'; return; } const onMenu = document.body.innerText.includes('Password'); panel.style.display = onMenu ? 'flex' : 'none'; } function refreshAll() { const profit = results.reduce((sum, r) => r.result==='win' ? sum + r.bet : sum - r.bet, 0); const sign = profit >= 0 ? '+' : '–'; profitMini.textContent = `${sign}${Math.abs(profit).toLocaleString()}`; profitMini.style.color = profit >= 0 ? '#4CAF50' : '#E53935'; profitDiv.textContent = `💰 Profit: ${sign}$${Math.abs(profit).toLocaleString()}`; profitDiv.style.color = profit >= 0 ? '#4CAF50' : '#E53935'; const wins = results.filter(r => r.result==='win').length; const tot = results.length; winrateDiv.textContent = `🎯 Win Rate: ${tot?((wins/tot)*100).toFixed(1):'0.0'}% (${wins}/${tot})`; let w=0,l=0; for (const r of results) { if (r.result==='win') { if(l) break; w++; } else { if(w) break; l++; } } streakDiv.textContent = w?`🔥 Streak: ${w}`: l?`💀 Streak: ${l}`:'⏸️ No streak'; resultsContainer.innerHTML = ''; results.forEach((r,i) => { const row = document.createElement('div'); row.append( makeCircle(r.result==='win'? '#4CAF50':'#E53935'), document.createTextNode(`${i+1}. ${r.result.toUpperCase()} — $${r.bet.toLocaleString()}`) ); resultsContainer.appendChild(row); }); miniBar.innerHTML = ''; results.slice(0,10).forEach(r => miniBar.append(makeCircle(r.result==='win'? '#4CAF50':'#E53935'))); miniBar.style.display = collapsed ? 'flex' : 'none'; profitMini.style.display = collapsed ? 'block' : 'none'; statsGroup.style.display = collapsed ? 'none' : 'flex'; resultsContainer.style.display = collapsed ? 'none' : 'block'; resetBtn.style.display = collapsed ? 'none' : 'inline-block'; autoHideBtn.style.display = collapsed ? 'none' : 'inline-block'; updateStatus(); updatePanelVisibility(); } function addResult(type) { if (!roundActive) return; results.unshift({ result: type, bet: lastPot }); if (results.length > MAX) results.pop(); saveResults(); roundActive = false; hasTracked = true; refreshAll(); } function scanPot() { document.querySelectorAll('body *').forEach(el => { const txt = el.innerText?.trim(); if (txt?.includes('POT MONEY:$')) { const m = txt.match(/POT MONEY:\$\s*([\d,]+)/); if (m) lastPot = Math.floor(parseInt(m[1].replace(/,/g,''),10)/2); } }); } function scanResult() { if (!roundActive) return; document.querySelectorAll('body *').forEach(el => { const txt = el.innerText?.trim(); if (txt?.includes('You take your winnings')) addResult('win'); if (txt?.includes('BANG! You fall down')) addResult('lose'); }); } function scanStart() { if (hasTracked && document.body.innerText.includes('Waiting:')) { roundActive = true; hasTracked = false; updateStatus(); } } (function() { let mx, my; const savePos = () => localStorage.setItem(POS_KEY, JSON.stringify({ top: panel.style.top, left: panel.style.left })); dragHandle.addEventListener('mousedown', e => { e.preventDefault(); mx = e.clientX; my = e.clientY; document.addEventListener('mousemove', onMove); document.addEventListener('mouseup', onUp); function onMove(ev) { const dx = ev.clientX - mx, dy = ev.clientY - my; mx = ev.clientX; my = ev.clientY; panel.style.top = panel.offsetTop + dy + 'px'; panel.style.left = panel.offsetLeft + dx + 'px'; } function onUp() { document.removeEventListener('mousemove', onMove); document.removeEventListener('mouseup', onUp); savePos(); } }); dragHandle.addEventListener('touchstart', e => { mx = e.touches[0].clientX; my = e.touches[0].clientY; document.addEventListener('touchmove', onTmove); document.addEventListener('touchend', onTend); function onTmove(ev) { const dx = ev.touches[0].clientX - mx, dy = ev.touches[0].clientY - my; mx = ev.touches[0].clientX; my = ev.touches[0].clientY; panel.style.top = panel.offsetTop + dy + 'px'; panel.style.left = panel.offsetLeft + dx + 'px'; } function onTend() { document.removeEventListener('touchmove', onTmove); document.removeEventListener('touchend', onTend); savePos(); } }); })(); statusDiv.addEventListener('click', () => { collapsed = !collapsed; localStorage.setItem(COLLAPSE_KEY, JSON.stringify(collapsed)); refreshAll(); }); refreshAll(); setInterval(() => { updatePanelVisibility(); scanStart(); scanPot(); scanResult(); }, 500); })();