您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Manual Island Vault tracker styled for Torn's dark UI with draggable, resizable UI, comma input support, and individual resets.
// ==UserScript== // @name Island Vault Tracker - Torn Dark Mode - Public (Local Storage Only) // @namespace http://torn.com/ // @version 1.1 // @description Manual Island Vault tracker styled for Torn's dark UI with draggable, resizable UI, comma input support, and individual resets. // @author lR3TR0l [3646989] // @match https://www.torn.com/properties.php* // @grant none // @license MIT // ==/UserScript== (function () { 'use strict'; const personA = 'INSERT_NAME_A'; const personB = 'INSERT_NAME_B'; const defaultBalances = { [personA]: 0, [personB]: 0 }; let storedBalances = JSON.parse(localStorage.getItem('islandVaultBalances')) || { ...defaultBalances }; const saveBalances = () => { localStorage.setItem('islandVaultBalances', JSON.stringify(storedBalances)); updateUI(); }; const formatMoney = (amount) => (amount < 0 ? '-$' : '$') + Math.abs(amount).toLocaleString(); const adjustBalance = (person, amount) => { storedBalances[person] = (storedBalances[person] || 0) + amount; saveBalances(); }; const resetPerson = (person) => { storedBalances[person] = 0; saveBalances(); }; const createAdjustButton = (person, amount) => { const btn = document.createElement('button'); const sign = amount > 0 ? '+' : ''; const millionAmount = amount / 1_000_000; btn.innerText = `${sign}${millionAmount}M`; Object.assign(btn.style, { margin: '2px', padding: '3px 7px', cursor: 'pointer', color: amount > 0 ? '#a4ffa4' : '#ff9999', backgroundColor: '#2b2d2f', border: '1px solid #444', borderRadius: '4px', fontSize: '12px' }); btn.onclick = () => adjustBalance(person, amount); return btn; }; // Main tracker box const box = document.createElement('div'); box.id = 'island-vault-tracker'; Object.assign(box.style, { position: 'fixed', top: '100px', right: '20px', backgroundColor: '#1e1f21', color: '#f0f0f0', padding: '15px', border: '1px solid #444', borderRadius: '8px', zIndex: '9999', fontFamily: 'Verdana, sans-serif', fontSize: '13px', minWidth: '220px', minHeight: '150px', maxWidth: '400px', maxHeight: '90vh', overflow: 'auto', userSelect: 'none', boxSizing: 'border-box', right: '20px' }); // Restore position & size from localStorage const savedTop = localStorage.getItem('vaultBoxTop'); const savedLeft = localStorage.getItem('vaultBoxLeft'); const savedWidth = localStorage.getItem('vaultBoxWidth'); const savedHeight = localStorage.getItem('vaultBoxHeight'); if (savedTop && savedLeft) { box.style.top = savedTop; box.style.left = savedLeft; box.style.right = 'auto'; } if (savedWidth) box.style.width = savedWidth; if (savedHeight) box.style.height = savedHeight; document.body.appendChild(box); // Create the resizer handle const resizer = document.createElement('div'); Object.assign(resizer.style, { width: '15px', height: '15px', background: 'transparent', position: 'absolute', right: '5px', bottom: '5px', cursor: 'se-resize', zIndex: '10000', }); box.appendChild(resizer); // Update UI content function function updateUI() { // Clear all except the resizer box.querySelectorAll(':not(div:last-child)').forEach(el => el !== resizer && el.remove()); const title = document.createElement('div'); title.style.cssText = 'font-weight:bold;font-size:15px;margin-bottom:10px;cursor:move;color:#f0f0f0;'; title.textContent = '🏝️ Island Vault Tracker'; box.insertBefore(title, resizer); [personA, personB].forEach(person => { const personDiv = document.createElement('div'); personDiv.style.marginTop = '10px'; personDiv.innerHTML = `<strong>${person}:</strong> <span style="color:${storedBalances[person] >= 0 ? '#a4ffa4' : '#ff9999'};">${formatMoney(storedBalances[person])}</span>`; box.insertBefore(personDiv, resizer); const buttonsDiv = document.createElement('div'); [1, 5, 10].forEach(m => buttonsDiv.appendChild(createAdjustButton(person, m * 1_000_000))); [1, 5, 10].forEach(m => buttonsDiv.appendChild(createAdjustButton(person, -m * 1_000_000))); box.insertBefore(buttonsDiv, resizer); }); const customDiv = document.createElement('div'); customDiv.style.marginTop = '15px'; customDiv.style.textAlign = 'center'; const amountInput = document.createElement('input'); Object.assign(amountInput, { type: 'text', placeholder: 'Amount (e.g. 1,000,000)', }); Object.assign(amountInput.style, { width: '160px', marginBottom: '6px', backgroundColor: '#2b2d2f', color: '#f0f0f0', border: '1px solid #444', padding: '4px', borderRadius: '4px' }); const personSelect = document.createElement('select'); [personA, personB].forEach(name => { const option = document.createElement('option'); option.value = name; option.textContent = name; personSelect.appendChild(option); }); Object.assign(personSelect.style, { marginBottom: '6px', backgroundColor: '#2b2d2f', color: '#f0f0f0', border: '1px solid #444', padding: '3px', borderRadius: '4px' }); const makeButton = (label, color, callback) => { const btn = document.createElement('button'); btn.innerHTML = label; Object.assign(btn.style, { margin: '4px', padding: '4px 10px', cursor: 'pointer', border: `1px solid ${color}`, color: '#f0f0f0', backgroundColor: '#2b2d2f', borderRadius: '4px', fontWeight: 'bold' }); btn.onclick = callback; return btn; }; const addBtn = makeButton('➕ Add', 'limegreen', () => { const val = parseFloat(amountInput.value.replace(/,/g, '')); if (isNaN(val) || val <= 0) return alert('Enter a valid positive number!'); adjustBalance(personSelect.value, val); amountInput.value = ''; }); const subtractBtn = makeButton('➖ Subtract', 'tomato', () => { const val = parseFloat(amountInput.value.replace(/,/g, '')); if (isNaN(val) || val <= 0) return alert('Enter a valid positive number!'); adjustBalance(personSelect.value, -val); amountInput.value = ''; }); customDiv.appendChild(amountInput); customDiv.appendChild(document.createElement('br')); customDiv.appendChild(personSelect); customDiv.appendChild(document.createElement('br')); customDiv.appendChild(addBtn); customDiv.appendChild(subtractBtn); box.insertBefore(customDiv, resizer); const resetDiv = document.createElement('div'); resetDiv.style.marginTop = '12px'; resetDiv.style.textAlign = 'center'; [personA, personB].forEach(person => { const resetBtn = makeButton(`Reset ${person}`, '#999', () => { if (confirm(`Reset ${person}'s balance to $0?`)) resetPerson(person); }); resetDiv.appendChild(resetBtn); }); box.insertBefore(resetDiv, resizer); const fallbackBtn = makeButton('🔄 Reset UI', '#888', () => { localStorage.removeItem('vaultBoxTop'); localStorage.removeItem('vaultBoxLeft'); localStorage.removeItem('vaultBoxWidth'); localStorage.removeItem('vaultBoxHeight'); location.reload(); }); box.insertBefore(fallbackBtn, resizer); const creditLine = document.createElement('div'); creditLine.textContent = 'Created by lR3TR0l'; creditLine.style.cssText = 'margin-top:10px;font-size:11px;text-align:center;color:#777;'; box.insertBefore(creditLine, resizer); } // Dragging & saving (() => { let isDragging = false, offsetX = 0, offsetY = 0; box.addEventListener('mousedown', function (e) { if (e.target === resizer) return; // don't drag when resizing if (['button', 'input', 'select'].includes(e.target.tagName.toLowerCase())) return; isDragging = true; offsetX = e.clientX - box.offsetLeft; offsetY = e.clientY - box.offsetTop; document.body.style.userSelect = 'none'; }); document.addEventListener('mousemove', function (e) { if (!isDragging) return; let newLeft = e.clientX - offsetX; let newTop = e.clientY - offsetY; // Clamp within window bounds newLeft = Math.max(0, Math.min(window.innerWidth - box.offsetWidth, newLeft)); newTop = Math.max(0, Math.min(window.innerHeight - box.offsetHeight, newTop)); box.style.left = `${newLeft}px`; box.style.top = `${newTop}px`; box.style.right = 'auto'; }); document.addEventListener('mouseup', () => { if (isDragging) { localStorage.setItem('vaultBoxTop', box.style.top); localStorage.setItem('vaultBoxLeft', box.style.left); } isDragging = false; document.body.style.userSelect = ''; }); // Touch support for dragging box.addEventListener('touchstart', function (e) { if (e.target === resizer || ['button', 'input', 'select'].includes(e.target.tagName.toLowerCase())) return; isDragging = true; offsetX = e.touches[0].clientX - box.offsetLeft; offsetY = e.touches[0].clientY - box.offsetTop; document.body.style.userSelect = 'none'; }, { passive: false }); document.addEventListener('touchmove', function (e) { if (!isDragging) return; let newLeft = e.touches[0].clientX - offsetX; let newTop = e.touches[0].clientY - offsetY; newLeft = Math.max(0, Math.min(window.innerWidth - box.offsetWidth, newLeft)); newTop = Math.max(0, Math.min(window.innerHeight - box.offsetHeight, newTop)); box.style.left = `${newLeft}px`; box.style.top = `${newTop}px`; box.style.right = 'auto'; }, { passive: false }); document.addEventListener('touchend', () => { if (isDragging) { localStorage.setItem('vaultBoxTop', box.style.top); localStorage.setItem('vaultBoxLeft', box.style.left); } isDragging = false; document.body.style.userSelect = ''; }); })(); // Resizing logic with mouse and touch (() => { let isResizing = false; let startX, startY, startWidth, startHeight; const onPointerDown = (e) => { e.preventDefault(); isResizing = true; startX = e.clientX || e.touches[0].clientX; startY = e.clientY || e.touches[0].clientY; startWidth = box.offsetWidth; startHeight = box.offsetHeight; document.body.style.userSelect = 'none'; }; const onPointerMove = (e) => { if (!isResizing) return; const currentX = e.clientX || (e.touches && e.touches[0].clientX); const currentY = e.clientY || (e.touches && e.touches[0].clientY); if (currentX === undefined || currentY === undefined) return; let newWidth = startWidth + (currentX - startX); let newHeight = startHeight + (currentY - startY); // Clamp size within min/max limits newWidth = Math.max(220, Math.min(400, newWidth)); newHeight = Math.max(150, Math.min(window.innerHeight * 0.9, newHeight)); box.style.width = `${newWidth}px`; box.style.height = `${newHeight}px`; }; const onPointerUp = () => { if (isResizing) { localStorage.setItem('vaultBoxWidth', box.style.width); localStorage.setItem('vaultBoxHeight', box.style.height); } isResizing = false; document.body.style.userSelect = ''; }; resizer.addEventListener('mousedown', onPointerDown); document.addEventListener('mousemove', onPointerMove); document.addEventListener('mouseup', onPointerUp); // Touch support resizer.addEventListener('touchstart', onPointerDown); document.addEventListener('touchmove', onPointerMove, { passive: false }); document.addEventListener('touchend', onPointerUp); document.addEventListener('touchcancel', onPointerUp); })(); updateUI(); console.log('Island Vault Tracker loaded – script by lR3TR0l 💼'); })();