您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Torn RR 1–10 Martingale buttons with configurable starting bet and multiplier (2x/2.5x/3x/etc.), plus persistent settings and inline Settings UI.
// ==UserScript== // @name Torn Russian Roulette Martingale Betting Buttons (Dynamic Base + Multiplier + Persistent Settings) // @namespace http://tampermonkey.net/ // @version 4.3 // @description Torn RR 1–10 Martingale buttons with configurable starting bet and multiplier (2x/2.5x/3x/etc.), plus persistent settings and inline Settings UI. // @author Original Author - Chris-Cross; Base/UX - GreasyDiddy; upgrades - ChatGPT // @license MIT // @match https://www.torn.com/page.php?sid=russianRoulette* // @grant none // @run-at document-idle // ==/UserScript== (function () { 'use strict'; // ------------------------- // Utilities & Persistence // ------------------------- const LS_KEY = 'trr_martingale_settings_v2'; const defaultSettings = { base: 25_000, // default base bet mult: 3 // default martingale multiplier }; function loadSettings() { try { const raw = localStorage.getItem(LS_KEY); if (!raw) return { ...defaultSettings }; const parsed = JSON.parse(raw); const base = Number(parsed.base); const mult = Number(parsed.mult); if (!Number.isFinite(base) || base <= 0) parsed.base = defaultSettings.base; if (!Number.isFinite(mult) || mult <= 1) parsed.mult = defaultSettings.mult; return { base: parsed.base, mult: parsed.mult }; } catch { return { ...defaultSettings }; } } function saveSettings(s) { localStorage.setItem(LS_KEY, JSON.stringify(s)); } function parseBetInput(input) { if (!input && input !== 0) return null; input = String(input).toLowerCase().replace(/,/g, '').trim(); let multiplier = 1; if (input.endsWith('k')) { multiplier = 1e3; input = input.slice(0, -1); } else if (input.endsWith('m')) { multiplier = 1e6; input = input.slice(0, -1); } else if (input.endsWith('b')) { multiplier = 1e9; input = input.slice(0, -1); } const value = parseFloat(input); if (isNaN(value) || value <= 0) return null; return Math.floor(value * multiplier); } function formatMoney(n) { try { return n.toLocaleString('en-US'); } catch { return String(n); } } function calcBets(base, mult, count = 10) { const arr = []; for (let i = 0; i < count; i++) { arr.push(Math.floor(base * Math.pow(mult, i))); } return arr; } // ------------------------- // Injection // ------------------------- const BUTTONS_CONTAINER_ID = 'martingale-buttons'; const MODAL_ID = 'martingale-settings-modal'; function makeBtn(text, title) { const btn = document.createElement('button'); btn.textContent = text; btn.title = title || ''; btn.className = 'torn-btn'; btn.style.padding = '4px 10px'; btn.style.fontSize = '11px'; btn.style.display = 'flex'; btn.style.alignItems = 'center'; btn.style.justifyContent = 'center'; btn.style.userSelect = 'none'; return btn; } function injectButtons() { const container = document.querySelector('[class*="createWrap___"]'); const inputBox = document.querySelector('input[aria-label="Money value"]'); if (!container || !inputBox) { setTimeout(injectButtons, 400); return; } if (document.getElementById(BUTTONS_CONTAINER_ID)) return; const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set; // Outer container const wrap = document.createElement('div'); wrap.id = BUTTONS_CONTAINER_ID; wrap.style.display = 'flex'; wrap.style.flexWrap = 'wrap'; wrap.style.gap = '6px'; wrap.style.marginTop = '10px'; wrap.style.alignItems = 'center'; // Build buttons from settings const settings = loadSettings(); const bets = calcBets(settings.base, settings.mult, 10); // 1–10 buttons bets.forEach((bet, idx) => { const btn = makeBtn(String(idx + 1), `$${formatMoney(bet)}`); btn.addEventListener('click', () => { nativeSetter.call(inputBox, formatMoney(bet)); inputBox.dispatchEvent(new Event('input', { bubbles: true })); inputBox.dispatchEvent(new Event('change', { bubbles: true })); }); wrap.appendChild(btn); // After "10", append Settings button if (idx === 9) { const settingsBtn = makeBtn('Settings', 'Configure base bet + multiplier (persists)'); settingsBtn.style.padding = '4px 10px'; settingsBtn.addEventListener('click', openSettingsModal); wrap.appendChild(settingsBtn); } }); container.appendChild(wrap); } // ------------------------- // Settings Modal // ------------------------- function openSettingsModal() { if (document.getElementById(MODAL_ID)) return; const settings = loadSettings(); // Backdrop const backdrop = document.createElement('div'); backdrop.id = MODAL_ID; backdrop.style.position = 'fixed'; backdrop.style.inset = '0'; backdrop.style.background = 'rgba(0,0,0,0.35)'; backdrop.style.zIndex = '99999'; backdrop.style.display = 'flex'; backdrop.style.alignItems = 'center'; backdrop.style.justifyContent = 'center'; // Modal const modal = document.createElement('div'); modal.style.minWidth = '320px'; modal.style.maxWidth = '420px'; modal.style.background = '#1f1f1f'; modal.style.color = '#eaeaea'; modal.style.borderRadius = '8px'; modal.style.boxShadow = '0 8px 20px rgba(0,0,0,0.4)'; modal.style.padding = '16px'; modal.style.fontSize = '13px'; modal.innerHTML = ` <div style="font-weight:700; font-size:14px; margin-bottom:8px;">Martingale Settings</div> <div style="display:flex; flex-direction:column; gap:10px;"> <label style="display:flex; flex-direction:column; gap:6px;"> <span>Starting/Base Bet</span> <input id="trr_base_input" type="text" placeholder="e.g. 25k, 2.5m, 1b" value="${settings.base}" style="padding:6px 8px; border-radius:6px; border:1px solid #3a3a3a; background:#111; color:#fff;"> </label> <label style="display:flex; flex-direction:column; gap:6px;"> <span>Multiplier (e.g., 2, 2.5, 3)</span> <input id="trr_mult_input" type="number" step="0.1" min="1.1" max="10" value="${settings.mult}" style="padding:6px 8px; border-radius:6px; border:1px solid #3a3a3a; background:#111; color:#fff;"> </label> <div id="trr_preview" style="max-height:160px; overflow:auto; border:1px dashed #3a3a3a; border-radius:6px; padding:8px;"></div> <div style="display:flex; gap:8px; justify-content:flex-end;"> <button id="trr_cancel_btn" class="torn-btn" style="padding:6px 12px;">Cancel</button> <button id="trr_save_btn" class="torn-btn" style="padding:6px 12px; font-weight:700;">Save</button> </div> </div> `; backdrop.appendChild(modal); document.body.appendChild(backdrop); const baseEl = modal.querySelector('#trr_base_input'); const multEl = modal.querySelector('#trr_mult_input'); const prevEl = modal.querySelector('#trr_preview'); // Center the modal buttons' text (fix) const cancelBtn = modal.querySelector('#trr_cancel_btn'); const saveBtn = modal.querySelector('#trr_save_btn'); [cancelBtn, saveBtn].forEach((b) => { b.style.display = 'flex'; b.style.alignItems = 'center'; b.style.justifyContent = 'center'; b.style.height = '28px'; // approximate Torn small button height b.style.lineHeight = '28px'; // match height for vertical centering b.style.padding = '0 12px'; // balanced horizontal padding }); function renderPreview() { const baseParsed = parseBetInput(baseEl.value); const multParsed = Number(multEl.value); if (!baseParsed || !Number.isFinite(multParsed) || multParsed <= 1) { prevEl.innerHTML = `<div style="opacity:.8;">Enter a valid base + multiplier > 1 to see preview.</div>`; return; } const previewBets = calcBets(baseParsed, multParsed, 10); prevEl.innerHTML = previewBets.map((b, i) => `<div>${i + 1}: $${formatMoney(b)}</div>`).join(''); } function closeModal() { document.removeEventListener('keydown', handleEsc); backdrop.remove(); } function handleEsc(ev) { if (ev.key === 'Escape') closeModal(); } document.addEventListener('keydown', handleEsc); renderPreview(); baseEl.addEventListener('input', renderPreview); multEl.addEventListener('input', renderPreview); cancelBtn.addEventListener('click', closeModal); saveBtn.addEventListener('click', () => { const newBase = parseBetInput(baseEl.value); const newMult = Number(multEl.value); if (!newBase) { alert('Invalid base bet.'); return; } if (!Number.isFinite(newMult) || newMult <= 1) { alert('Multiplier must be a number greater than 1 (e.g., 2, 2.5, 3).'); return; } saveSettings({ base: newBase, mult: newMult }); closeModal(); document.getElementById(BUTTONS_CONTAINER_ID)?.remove(); injectButtons(); }); // Close on backdrop click backdrop.addEventListener('click', (e) => { if (e.target === backdrop) closeModal(); }); } // ------------------------- // Bootstrapping // ------------------------- const observer = new MutationObserver(() => { if (location.href.includes('russianRoulette')) injectButtons(); }); observer.observe(document.body, { childList: true, subtree: true }); setTimeout(injectButtons, 800); setTimeout(injectButtons, 2000); })();