// ==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 💼');
})();