您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Numpad shortcuts for Torn attack page with configurable Continue behavior + hospital reload check
// ==UserScript== // @name Torn.com Attack Numpad Helper // @namespace https://torn.com/ // @version 1.7.0 // @description Numpad shortcuts for Torn attack page with configurable Continue behavior + hospital reload check // @author You // @match https://www.torn.com/loader.php* // @run-at document-idle // @grant GM.getValue // @grant GM.setValue // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // ==/UserScript== (async function () { 'use strict'; // Only run on the attack loader with a user2ID param const params = new URLSearchParams(location.search); if (!(params.get('sid') === 'attack' && params.has('user2ID'))) return; // GM compatibility const GMAPI = { getValue: async (k, d) => { try { if (typeof GM !== 'undefined' && GM.getValue) return await GM.getValue(k, d); if (typeof GM_getValue !== 'undefined') return GM_getValue(k, d); } catch {} const v = localStorage.getItem('tm_' + k); return v == null ? d : v; }, setValue: async (k, v) => { try { if (typeof GM !== 'undefined' && GM.setValue) return await GM.setValue(k, v); if (typeof GM_setValue !== 'undefined') return GM_setValue(k, v); } catch {} localStorage.setItem('tm_' + k, v); }, }; // '.' target preference (Punch=5th, Kick=6th) let decimalTarget = await GMAPI.getValue('decimalTarget', 'punch'); // 'punch' | 'kick' function getDecimalIndex() { return decimalTarget === 'kick' ? 6 : 5; } // Continue action preference (default "default") let continueAction = await GMAPI.getValue('continueAction', 'default'); // 'default' | 'close' | 'openFixed' function tryCloseTab() { try { window.close(); } catch {} try { const w = window.open('', '_self'); w && w.close && w.close(); } catch {} } function handleContinue() { if (continueAction === 'close') { tryCloseTab(); return true; } if (continueAction === 'openFixed') { window.location.href = "https://www.torn.com/loader.php?sid=attack&user2ID=1598729"; return true; } return false; // default } // Userscript menu toggles let menuIds = []; function registerMenu() { if (typeof GM_registerMenuCommand !== 'function') return; if (menuIds.length && typeof GM_unregisterMenuCommand === 'function') { try { menuIds.forEach((id) => GM_unregisterMenuCommand(id)); } catch {} } menuIds = []; // Decimal mapping toggle const labelDecimal = `Decimal key: ${decimalTarget === 'kick' ? 'Kick' : 'Punch'} (click to toggle)`; const id1 = GM_registerMenuCommand(labelDecimal, async () => { decimalTarget = decimalTarget === 'kick' ? 'punch' : 'kick'; await GMAPI.setValue('decimalTarget', decimalTarget); scheduleUpdate(); registerMenu(); console.info('[Torn Numpad Helper] Decimal key set to:', decimalTarget); }); menuIds.push(id1); // Continue action toggle const labelContinue = `Continue action: ${ continueAction === 'close' ? 'Close tab' : continueAction === 'openFixed' ? 'attack bodybagger' : 'Default click' } (click to cycle)`; const id2 = GM_registerMenuCommand(labelContinue, async () => { continueAction = continueAction === 'default' ? 'close' : continueAction === 'close' ? 'openFixed' : 'default'; await GMAPI.setValue('continueAction', continueAction); scheduleUpdate(); registerMenu(); console.info('[Torn Numpad Helper] Continue action set to:', continueAction); }); menuIds.push(id2); } registerMenu(); // Keys const overrideKeys = new Set(['Numpad4', 'Numpad5', 'Numpad6']); const defaultKeys = new Set(['Numpad1', 'Numpad2', 'Numpad3', 'Numpad0', 'NumpadDecimal', 'NumpadComma']); const isNumpadKey = (code) => typeof code === 'string' && code.startsWith('Numpad'); // Default mapping (hoverEnabled) const baseDefaultMap = { Numpad1: 'div.hoverEnabled___skjqK:nth-child(1)', Numpad2: 'div.hoverEnabled___skjqK:nth-child(2)', Numpad3: 'div.hoverEnabled___skjqK:nth-child(3)', Numpad0: 'div.hoverEnabled___skjqK:nth-child(4)', }; function selectorForKey(code) { if (code === 'NumpadDecimal' || code === 'NumpadComma') { return `div.hoverEnabled___skjqK:nth-child(${getDecimalIndex()})`; } return baseDefaultMap[code]; } // Utilities function isTypingInField(target) { return !!( target && (target.isContentEditable || target.closest('input, textarea, [contenteditable=""], [contenteditable="true"]')) ); } function clickEl(el) { if (el) { el.click(); return true; } return false; } function hasContinueText(btn) { const txt = (btn?.textContent || '').toLowerCase(); return txt.includes('continue'); } function isHospitalBlocked() { return !!document.querySelector('.colored___sN72G.red___SANWO .title___fOh2J'); } // Find the primary button in default mode function findPrimaryButton() { return ( document.querySelector('button.torn-btn:nth-child(1)') || document.querySelector('button[class^="btn___"]:nth-child(1)') ); } // Override mode: find buttons by walking siblings of the 3rd button function getOverrideButtons() { const b3 = document.querySelector('button.torn-btn:nth-child(3)') || document.querySelector('button[class^="btn___"]:nth-child(3)'); if (!b3) return null; let b2 = b3.previousElementSibling; while (b2 && b2.tagName !== 'BUTTON') b2 = b2.previousElementSibling; let b1 = b2 ? b2.previousElementSibling : null; while (b1 && b1.tagName !== 'BUTTON') b1 = b1.previousElementSibling; return { b1, b2, b3 }; } // Minimal key badges const style = document.createElement('style'); style.textContent = ` .torn-keyhint { position: absolute; top: 4px; right: 4px; background: rgba(0,0,0,.55); color: #fff; border-radius: 4px; padding: 1px 4px; font-size: 10px; line-height: 1.2; font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial; letter-spacing: .2px; pointer-events: none; z-index: 2147483647; opacity: .9; } .torn-keyhint--multi { opacity: .85; } `; document.head.appendChild(style); function clearAllHints() { document.querySelectorAll('.torn-keyhint').forEach((el) => el.remove()); } function ensureHintOnElement(el, text, isMulti = false) { if (!el) return; const cs = getComputedStyle(el); if (cs.position === 'static') el.style.position = 'relative'; let hint = el.querySelector(':scope > .torn-keyhint'); if (!hint) { hint = document.createElement('span'); hint.className = 'torn-keyhint'; el.appendChild(hint); } hint.classList.toggle('torn-keyhint--multi', !!isMulti); hint.textContent = text; } function ensureHintOnSelector(selector, text, isMulti = false) { const el = document.querySelector(selector); if (el) ensureHintOnElement(el, text, isMulti); } function updateHints() { clearAllHints(); const ob = getOverrideButtons(); if (ob && (ob.b1 || ob.b2 || ob.b3)) { if (ob.b1) ensureHintOnElement(ob.b1, '4'); if (ob.b2) ensureHintOnElement(ob.b2, '5'); if (ob.b3) ensureHintOnElement(ob.b3, '6'); return; } const primary = findPrimaryButton(); if (primary) { const label = hasContinueText(primary) ? (continueAction === 'close' ? 'any → close' : continueAction === 'openFixed' ? 'any → fixed' : 'any') : 'any'; ensureHintOnElement(primary, label); } else { ensureHintOnSelector('div.hoverEnabled___skjqK:nth-child(1)', '1'); ensureHintOnSelector('div.hoverEnabled___skjqK:nth-child(2)', '2'); ensureHintOnSelector('div.hoverEnabled___skjqK:nth-child(3)', '3'); ensureHintOnSelector('div.hoverEnabled___skjqK:nth-child(4)', '0'); ensureHintOnSelector(`div.hoverEnabled___skjqK:nth-child(${getDecimalIndex()})`, '.'); } } // Key handling document.addEventListener( 'keydown', (e) => { if (isTypingInField(e.target)) return; // Hospital check: any numpad key reloads if (isHospitalBlocked() && isNumpadKey(e.code)) { e.preventDefault(); e.stopPropagation(); location.reload(); return; } const ob = getOverrideButtons(); if (ob && (ob.b1 || ob.b2 || ob.b3)) { if (!overrideKeys.has(e.code)) return; const target = e.code === 'Numpad4' ? ob.b1 : e.code === 'Numpad5' ? ob.b2 : e.code === 'Numpad6' ? ob.b3 : null; if (clickEl(target)) { e.preventDefault(); e.stopPropagation(); } return; } const primary = findPrimaryButton(); if (primary) { if (!isNumpadKey(e.code)) return; if (hasContinueText(primary) && continueAction !== 'default') { e.preventDefault(); e.stopPropagation(); if (handleContinue()) return; } if (clickEl(primary)) { e.preventDefault(); e.stopPropagation(); } return; } if (!defaultKeys.has(e.code)) return; const selector = selectorForKey(e.code); const el = selector ? document.querySelector(selector) : null; if (clickEl(el)) { e.preventDefault(); e.stopPropagation(); } }, true ); const scheduleUpdate = (() => { let t = null; return () => { if (t) return; t = setTimeout(() => { t = null; updateHints(); }, 50); }; })(); updateHints(); const observer = new MutationObserver(() => scheduleUpdate()); observer.observe(document.body, { subtree: true, childList: true, attributes: true, attributeFilter: ['class', 'style'], }); window.addEventListener('focus', scheduleUpdate); })();