您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Highlights and explain on hover effects.
// ==UserScript== // @name Torn Weapon Effects Highlighter // @namespace http://tampermonkey.net/ // @version 1.1 // @description Highlights and explain on hover effects. // @author aquagloop // @match https://www.torn.com/* // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; const weaponEffects = { 'blindfire': 'Expends remaining ammunition in current clip with reduced accuracy', 'burn': 'Burning DOT effect over 3 turns (45% initial damage)', 'demoralized': '10% debuff to all opponent stats (stacks up to 5x)', 'emasculate': 'Grants percentage of max happy on finishing hit', 'freeze': '50% debuff to opponent speed and dexterity', 'hazardous': 'Take percentage of damage you deal', 'laceration': 'Devastating DOT effect over 9 turns (90% initial damage)', 'poisoned': 'Long DOT effect over 19 turns (95% initial damage)', 'severe burning': 'Short DOT effect over 3 turns (45% initial damage)', 'shock': 'Causes opponent to miss next turn', 'sleep': 'Enemy misses turns until damaged', 'smash': 'Double damage but requires recharge turn', 'spray': 'Empty full clip for double damage', 'storage': 'Allows two temporary items in fight', 'toxin': '25% debuff to random opponent stat (stacks up to 3x)', 'achilles': 'Increased foot damage', 'assassinate': 'Increased damage on first turn', 'backstab': 'Double damage when opponent distracted', 'berserk': 'Increased damage but reduced hit chance', 'bleed': 'Bleeding DOT effect over 9 turns (45% initial damage)', 'blindside': 'Increased damage if target has full life', 'bloodlust': 'Life regenerated by percentage of damage dealt', 'comeback': 'Increased damage while under 25% life', 'conserve': 'Increased ammo conservation', 'cripple': 'Reduces dexterity by 25% (stacks up to 3x for -75% total)', 'crusher': 'Increased head damage', 'cupid': 'Increased heart damage', 'deadeye': 'Increased critical hit damage', 'deadly': 'Chance of deadly hit (+500% damage)', 'disarm': 'Disables opponent weapon for multiple turns', 'double-edged': 'Double damage at cost of self injury', 'double tap': 'Hit twice in single turn', 'empower': 'Increased strength while using weapon', 'eviscerate': 'Opponent receives extra damage under effect', 'execute': 'Instant defeat when opponent below threshold life', 'expose': 'Increased critical hit rate', 'finale': 'Increased damage for every turn weapon not used', 'focus': 'Hit chance increase for successive misses', 'frenzy': 'Damage and accuracy increase on successive hits', 'fury': 'Hit twice in single turn', 'grace': 'Increased hit chance but reduced damage', 'home run': 'Deflect incoming temporary items', 'irradiate': 'Apply radiation poisoning on finishing hit', 'motivation': 'Increase all stats by 10% (stacks 5x)', 'paralyzed': '50% chance of missing turns for 300 seconds', 'parry': 'Block incoming melee attacks', 'penetrate': 'Ignore percentage of enemy armor', 'plunder': 'Increase money mugged on finishing hit', 'powerful': 'Increased damage', 'proficience': 'Increase XP gained on finishing hit', 'puncture': 'Ignore armor completely', 'quicken': 'Increased speed while using weapon', 'rage': 'Hit 2-8 times in single turn', 'revitalize': 'Restore energy spent attacking on finishing hit', 'roshambo': 'Increased groin damage', 'slow': 'Reduce opponent speed by 25% (stacks 3x)', 'smurf': 'Damage increase for each level under opponent', 'specialist': 'Increased damage but limited to single clip', 'stricken': 'Increased hospital time on final hit', 'stun': 'Cause opponent to miss next turn', 'suppress': '25% chance for opponent to miss future turns', 'sure shot': 'Guaranteed hit', 'throttle': 'Increased throat damage', 'warlord': 'Increases respect gained', 'weaken': 'Reduce opponent defense by 25% (stacks 3x)', 'wind-up': 'Increased damage after spending turn to wind up', 'wither': 'Reduce opponent strength by 25% (stacks 3x)' }; const effectPatterns = [ { regex: /\bcrippled\b/gi, key: 'cripple' }, { regex: /\bweakened\b/gi, key: 'weaken' }, { regex: /\bwithered\b/gi, key: 'wither' }, { regex: /\bslowed\b/gi, key: 'slow' }, { regex: /\bdemoralized\b/gi, key: 'demoralized' }, { regex: /\bfrozen\b/gi, key: 'freeze' }, { regex: /\beviscerated\b/gi, key: 'eviscerate' }, { regex: /\bstunned\b/gi, key: 'stun' }, { regex: /\bpoisoned\b/gi, key: 'poisoned' }, { regex: /\bbleeding\b/gi, key: 'bleed' }, { regex: /\bburning\b/gi, key: 'burn' }, { regex: /powerful hit/gi, key: 'powerful' }, { regex: /double damage/gi, key: 'backstab' }, { regex: /punctured through armor/gi, key: 'puncture' }, { regex: /ignores armor/gi, key: 'puncture' }, { regex: /deadly hit/gi, key: 'deadly' }, { regex: /fired \d+ rounds.*hitting.*\d+ times/gi, key: 'rage' }, { regex: /executed/gi, key: 'execute' }, { regex: /finishing blow/gi, key: 'execute' } ]; const css = ` .wep-highlight { display: inline; background-color: #add8e6; /* Light Blue */ color: #ffffff; /* White Text */ border: 1px solid #88b0c2; /* Darker Blue Border */ border-radius: 2px; padding: 0 2px; cursor: help; } .wep-highlight:hover { background-color: #87ceeb; /* Sky Blue on hover */ } `; const styleTag = document.createElement('style'); styleTag.textContent = css; document.head.appendChild(styleTag); function makeHighlightSpan(matchedText, effectKey) { const span = document.createElement('span'); span.className = 'wep-highlight'; span.setAttribute('title', `${effectKey.toUpperCase()}: ${weaponEffects[effectKey]}`); span.textContent = matchedText; return span; } function replaceInTextNodes(node, regex, effectKey) { if (node.nodeType === Node.ELEMENT_NODE && node.classList.contains('wep-highlight')) { return; } if (node.nodeType === Node.TEXT_NODE) { const text = node.nodeValue; if (!regex.test(text)) return; const frag = document.createDocumentFragment(); let lastIndex = 0; text.replace(regex, (match, ...args) => { const matchIndex = args[args.length - 2]; if (matchIndex > lastIndex) { frag.appendChild(document.createTextNode(text.slice(lastIndex, matchIndex))); } const hlSpan = makeHighlightSpan(match, effectKey); frag.appendChild(hlSpan); lastIndex = matchIndex + match.length; }); if (lastIndex < text.length) { frag.appendChild(document.createTextNode(text.slice(lastIndex))); } node.parentNode.replaceChild(frag, node); return; } if (node.nodeType === Node.ELEMENT_NODE) { Array.from(node.childNodes).forEach(child => replaceInTextNodes(child, regex, effectKey)); } } function highlightInsideMessage(msgNode) { const rawText = msgNode.innerText; if (!rawText || !rawText.trim()) return; Object.keys(weaponEffects).forEach(key => { const regex = new RegExp(`\\b${key}\\b`, 'gi'); if (regex.test(rawText)) { replaceInTextNodes(msgNode, regex, key); } }); effectPatterns.forEach(({ regex, key }) => { if (regex.test(rawText)) { replaceInTextNodes(msgNode, regex, key); } }); } function processNode(node) { if (node.nodeType !== Node.ELEMENT_NODE) return; const messageNodes = node.matches('.message') ? [node] : Array.from(node.querySelectorAll('.message')); messageNodes.forEach(highlightInsideMessage); } function processExistingLog() { document.querySelectorAll('.log-list.overview li').forEach(li => { const msg = li.querySelector('.message'); if (msg) highlightInsideMessage(msg); }); } function watchForNewLines() { const container = document.querySelector('.log-list.overview') || document.body; const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.nodeType !== Node.ELEMENT_NODE) return; if (node.tagName === 'LI') { const msg = node.querySelector('.message'); if (msg) highlightInsideMessage(msg); } else { const newLis = node.querySelectorAll?.('li') || []; newLis.forEach(li => { const msg = li.querySelector('.message'); if (msg) highlightInsideMessage(msg); }); } }); }); }); observer.observe(container, { childList: true, subtree: true }); } function init() { processExistingLog(); watchForNewLines(); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();