// ==UserScript==
// @name TriX Executor for Territorial.io
// @namespace Violentmonkey Scripts
// @version 34.0.1
// @description A streamlined, powerful, multi-tab JavaScript executor for territorial.io. Features a simplified UI, dynamic tab detection, and targeted execution options (current vs. all tabs).
// @author Painsel
// @match https://*/*
// @grant none
// @run-at document-end
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// --- Configuration & State ---
const tabId = sessionStorage.getItem('trixTabId') || Date.now() + Math.random();
sessionStorage.setItem('trixTabId', tabId);
let isMaster = false;
if (!isTerritorialPage()) return;
// --- UI CREATION AND MANAGEMENT ---
function createExecutorUI() {
if (document.getElementById('trix-frame')) return;
const frame = document.createElement('div'); frame.id = 'trix-frame';
Object.assign(frame.style, { position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', background: '#111', border: '1px solid #444', borderRadius: '6px', zIndex: '100000', boxShadow: '0 0 15px rgba(0,0,0,0.5)', padding: '8px', cursor: 'move', animation: 'trix-fade-in 0.3s ease-out' });
const executor = document.createElement('div'); executor.id = 'trix-executor';
Object.assign(executor.style, { width: '600px', backgroundColor: '#1e1e1e', display: 'flex', flexDirection: 'column', fontFamily: 'Consolas, "Courier New", monospace', cursor: 'default' });
const header = document.createElement('div'); header.id = 'trix-header';
Object.assign(header.style, { backgroundColor: '#111', color: 'white', padding: '8px', borderTopLeftRadius: '5px', borderTopRightRadius: '5px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' });
header.innerHTML = `<div><img src="https://i.postimg.cc/pVhYV8kL/image.png" style="height: 20px; margin-right: 10px; vertical-align: middle;"> TriX <span style="font-size: 0.7em; color: #888;">by Painsel</span></div>`;
const headerControls = document.createElement('div'); Object.assign(headerControls.style, { display: 'flex', alignItems: 'center', gap: '8px' });
const tabCounter = document.createElement('span'); tabCounter.id = "trix-tab-counter"; tabCounter.title = "Active territorial.io tabs"; Object.assign(tabCounter.style, { color: '#ccc', fontSize: '12px', marginRight: '8px', background: '#252526', padding: '3px 6px', borderRadius: '4px' }); tabCounter.textContent = "Tabs: 1"; // Master starts at 1
const toggleButton = document.createElement('button'); toggleButton.innerHTML = "−"; Object.assign(toggleButton.style, { background: '#555', border: 'none', color: 'white', borderRadius: '4px', cursor: 'pointer', width: '22px', height: '22px', fontSize: '16px', lineHeight: '1', padding: '0' });
const fileMenuContainer = document.createElement('div'); fileMenuContainer.className = 'trix-menu-container';
const fileButton = document.createElement('button'); fileButton.className = 'trix-menu-button'; fileButton.textContent = 'File';
const fileDropdown = document.createElement('div'); fileDropdown.className = 'trix-dropdown-content';
fileDropdown.innerHTML = `<a href="#" id="trix-inject-btn">Inject</a><a href="#" id="trix-open-btn">Open...</a><a href="#" id="trix-save-btn">Save As...</a>`;
fileMenuContainer.appendChild(fileButton); fileMenuContainer.appendChild(fileDropdown);
fileButton.addEventListener('click', (e) => { e.stopPropagation(); fileDropdown.style.display = fileDropdown.style.display === 'block' ? 'none' : 'block'; });
document.addEventListener('click', () => { fileDropdown.style.display = 'none'; });
headerControls.appendChild(tabCounter); headerControls.appendChild(toggleButton); headerControls.appendChild(fileMenuContainer); header.appendChild(headerControls);
const body = document.createElement('div'); body.id = 'trix-body'; Object.assign(body.style, { display: 'flex', overflow: 'hidden', height: '420px', transition: 'height 0.3s ease-out' });
const executorPage = document.createElement('div'); executorPage.id = 'trix-page-executor';
Object.assign(executorPage.style, { display: 'flex', flexDirection: 'column', height: '100%', padding: '10px', width: '100%' });
executorPage.innerHTML = `
<div style="margin-bottom: 5px; color: #ffeb3b;">⚠️ JS Executor. Use 'return { ... }' to manage scripts in the F12 console.</div>
<textarea id="trix-script-input" placeholder="let myVar = 10;\\nfunction myFunc(){...}\\n\\nreturn { myVar, myFunc };" style="flex-grow: 1; background: #111; color: #e0e0e0; border: 1px solid #444; resize: none; width: 100%; box-sizing: border-box; font-family: inherit;"></textarea>
<div style="display: flex; gap: 10px; margin-top: 10px;">
<button id="trix-execute-script-btn" style="background: #c62828; color: white; border: none; padding: 10px; cursor: pointer; flex-grow: 1;" disabled>Execute</button>
<button id="trix-clear-script-btn" style="background: #555; color: white; border: none; padding: 10px; cursor: pointer;">Clear</button>
</div>`;
body.appendChild(executorPage);
executor.appendChild(header); executor.appendChild(body);
frame.appendChild(executor); document.body.appendChild(frame);
addGlobalStyles(); makeDraggable(frame); setupExecutorInjection();
toggleButton.onclick = () => { const isMinimized = body.style.display === 'none'; body.style.display = isMinimized ? 'flex' : 'none'; frame.style.padding = isMinimized ? '8px' : '0'; toggleButton.innerHTML = isMinimized ? '−' : '+'; };
}
function showExecutionModal(scriptText) {
if (document.getElementById('trix-modal-overlay')) return;
const overlay = document.createElement('div'); overlay.id = 'trix-modal-overlay';
Object.assign(overlay.style, { position: 'fixed', top: 0, left: 0, width: '100%', height: '100%', background: 'rgba(0,0,0,0.5)', zIndex: '100001', display: 'flex', alignItems: 'center', justifyContent: 'center' });
const modal = document.createElement('div');
Object.assign(modal.style, { background: '#1e1e1e', padding: '20px', borderRadius: '5px', border: '1px solid #444', textAlign: 'center', fontFamily: 'sans-serif' });
modal.innerHTML = `<h3 style="margin: 0 0 15px; color: white;">Choose Execution Scope</h3><div style="display: flex; gap: 15px;"><button id="trix-exec-current" class="modal-btn">This Tab Only</button><button id="trix-exec-all" class="modal-btn">All Tabs</button></div>`;
overlay.appendChild(modal);
document.body.appendChild(overlay);
const closeModal = () => overlay.remove();
overlay.onclick = e => { if (e.target === overlay) closeModal(); };
document.getElementById('trix-exec-current').onclick = () => { executeLocally(scriptText); closeModal(); };
document.getElementById('trix-exec-all').onclick = () => { broadcastCommand('execute-script', [scriptText]); closeModal(); };
}
// --- SCRIPT EXECUTOR & FILE MENU ---
function setupExecutorInjection() {
document.getElementById('trix-inject-btn').addEventListener("click", e => { e.preventDefault(); const injectButton = e.target; injectButton.innerHTML = 'Injecting...'; injectButton.style.pointerEvents = 'none'; setTimeout(() => { document.getElementById("trix-execute-script-btn").disabled = false; injectButton.innerHTML = 'Injected ✔'; injectButton.style.color = '#4caf50'; }, 1000); });
document.getElementById('trix-save-btn').addEventListener("click", e => { e.preventDefault(); saveScriptToFile(); });
document.getElementById('trix-open-btn').addEventListener("click", e => { e.preventDefault(); openScriptFromFile(); });
document.getElementById('trix-execute-script-btn').addEventListener('click', () => { const scriptText = document.getElementById('trix-script-input').value; if (scriptText.trim()) showExecutionModal(scriptText); });
document.getElementById('trix-clear-script-btn').addEventListener('click', () => { document.getElementById('trix-script-input').value = ""; });
}
// --- COMMAND & EXECUTION LOGIC ---
function broadcastCommand(command, args = []) { const payload = { cmd: command, args: args, ts: Date.now() }; localStorage.setItem('trix-command', JSON.stringify(payload)); }
function executeLocally(scriptText) {
console.log(`[TriX] Executing locally on tab ${tabId}`);
try {
window.trixHandle = new Function(scriptText)();
if (isMaster) logToServerPanel(`[200 OK] Script attached to window.trixHandle.`);
} catch (e) {
if (isMaster) logToServerPanel(`[500 ERROR] ${e.message}`);
console.error("Custom Script Error:", e);
}
}
// --- CROSS-TAB COMMUNICATION & ROLE MANAGEMENT ---
function setupStorageListener() {
window.addEventListener('storage', e => {
if (e.key === 'trix-master-heartbeat' && e.newValue) {
if (isMaster) {
console.log("[TriX] Another Master detected. Becoming Agent.");
isMaster = false;
const frame = document.getElementById('trix-frame');
if (frame) frame.style.display = 'none';
}
}
// **THE FIX IS HERE:** All tabs, including the Master, listen for commands now.
if (e.key === 'trix-command' && e.newValue) {
const payload = JSON.parse(e.newValue);
if (payload.cmd === 'execute-script') {
executeLocally(payload.args[0]);
}
}
});
}
function setupTabCounter() {
setInterval(() => { const heartbeats = JSON.parse(localStorage.getItem('trix-agent-heartbeats') || '{}'); heartbeats[tabId] = Date.now(); localStorage.setItem('trix-agent-heartbeats', JSON.stringify(heartbeats)); }, 2000);
window.addEventListener('beforeunload', () => { const heartbeats = JSON.parse(localStorage.getItem('trix-agent-heartbeats') || '{}'); delete heartbeats[tabId]; localStorage.setItem('trix-agent-heartbeats', JSON.stringify(heartbeats)); });
if (isMaster) {
setInterval(() => { localStorage.setItem('trix-master-heartbeat', Date.now()); }, 2000);
setInterval(() => {
const heartbeats = JSON.parse(localStorage.getItem('trix-agent-heartbeats') || '{}');
let activeCount = 0;
for (const id in heartbeats) { if (Date.now() - heartbeats[id] < 5000) activeCount++; else delete heartbeats[id]; }
localStorage.setItem('trix-agent-heartbeats', JSON.stringify(heartbeats));
const counterEl = document.getElementById('trix-tab-counter');
if(counterEl) counterEl.textContent = `Tabs: ${activeCount}`;
}, 1000);
window.addEventListener('beforeunload', () => { localStorage.removeItem('trix-master-heartbeat'); });
}
}
// --- GENERIC HELPERS & UTILITIES ---
function addGlobalStyles(){const style=document.createElement("style");style.textContent=`@keyframes trix-fade-in{from{opacity:0;transform:translate(-50%,-50%) scale(.95)}to{opacity:1;transform:translate(-50%,-50%) scale(1)}}.trix-menu-container{position:relative;display:inline-block;}.trix-menu-button{background:#555;color:white;border:none;padding:4px 10px;border-radius:4px;cursor:pointer;font-family:inherit;}.trix-dropdown-content{display:none;position:absolute;right:0;background-color:#2a2d2e;min-width:120px;box-shadow:0 8px 16px 0 rgba(0,0,0,0.2);z-index:1;border:1px solid #444;border-radius:4px;}.trix-dropdown-content a{color:#ccc;padding:8px 12px;text-decoration:none;display:block;font-size:13px;}.trix-dropdown-content a:hover{background-color:#333;}.trix-menu-button:focus + .trix-dropdown-content, .trix-menu-container:hover .trix-dropdown-content{display:block;}.trix-menu-container:hover .trix-menu-button{background-color:#666;}.modal-btn{background:#0e639c;color:white;border:none;padding:10px 20px;border-radius:4px;cursor:pointer;font-size:14px;}.modal-btn:hover{background:#1a8dd4;}`,document.head.appendChild(style)}
function saveScriptToFile(){const scriptText=document.getElementById("trix-script-input").value;if(scriptText.trim()==="")return;const blob=new Blob([scriptText],{type:"text/plain;charset=utf-8"}),now=new Date,pad=num=>num.toString().padStart(2,"0"),timestamp=`${now.getFullYear()}-${pad(now.getMonth()+1)}-${pad(now.getDate())}_${pad(now.getHours())}-${pad(now.getMinutes())}-${pad(now.getSeconds())}`,filename=`trix_script_${timestamp}.txt`,link=document.createElement("a");link.href=URL.createObjectURL(blob),link.download=filename,document.body.appendChild(link),link.click(),document.body.removeChild(link),URL.revokeObjectURL(link.href)}
function openScriptFromFile(){const input=document.createElement("input");input.type="file",input.accept=".txt,.js,.script",input.onchange=e=>{const file=e.target.files[0];if(!file)return;const reader=new FileReader;reader.onload=event=>{document.getElementById("trix-script-input").value=event.target.result},reader.readAsText(file)},input.click()}
function makeDraggable(element) { let isDragging = false, offsetX, offsetY; const handle = element; handle.onmousedown = e => { if (e.target !== handle && !e.target.closest("#trix-header")) return; e.preventDefault(); isDragging = true; offsetX = e.clientX - element.offsetLeft; offsetY = e.clientY - element.offsetTop; document.onmousemove = e => { if (isDragging) { element.style.left = `${e.clientX - offsetX}px`; element.style.top = `${e.clientY - offsetY}px`; } }; document.onmouseup = () => { isDragging = false; document.onmousemove = document.onmouseup = null; }; }; }
function isTerritorialPage() { const href = window.location.href; if (href.includes("territorial.io")) return true; if (href.includes("?__cpo=")) { try { return atob(new URLSearchParams(window.location.search).get("__cpo")).includes("territorial.io"); } catch (e) { return false; } } return document.title.toLowerCase().includes("territorial.io"); }
function waitForElement(findFunction, description, timeout = 15000) { return new Promise((resolve, reject) => { let attempts = 0, maxAttempts = timeout / 200; const interval = setInterval(() => { if (attempts++ >= maxAttempts) { clearInterval(interval); reject(new Error(`Timed out: ${description}`)); } const element = findFunction(); if (element) { clearInterval(interval); resolve(element); } }, 200); }); }
const findUsernameInput = () => document.getElementById("input0");
function initialize() { waitForElement(findUsernameInput, "Game UI to load", 5000).then(() => { isMasterTab() ? becomeMaster() : becomeAgent(); }).catch(() => {}); }
function isMasterTab() { const heartbeat = localStorage.getItem("trix-master-heartbeat"); return !heartbeat || (Date.now() - heartbeat > 4000); }
function becomeAgent() { console.log("[TriX] Master detected. Initializing as Agent."); setupStorageListener(); setupTabCounter(); }
function becomeMaster() { console.log("[TriX] No master detected. Initializing as Master."); isMaster = true; createExecutorUI(); setupStorageListener(); setupTabCounter(); }
initialize();
})();