您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Plays an alert and highlights when the Cyclist pickpocket target is available. Alerts are on by default and can be toggled.
// ==UserScript== // @name Cyclist Ring (Enhanced) // @namespace microbes.torn.ring.enhanced // @version 1.1 // @description Plays an alert and highlights when the Cyclist pickpocket target is available. Alerts are on by default and can be toggled. // @author Microbes (Enhanced by eaksquad) // @match https://www.torn.com/loader.php?sid=crimes // @icon https://www.google.com/s2/favicons?sz=64&domain=torn.com // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; // --- Configuration --- // Set to true to have alerts enabled by default when the page loads, false to have them off. const ALERTS_ENABLED_BY_DEFAULT = true; const ALERT_SOUND_URL = 'https://audio.jukehost.co.uk/gxd2HB9RibSHhr13OiW6ROCaaRbD8103'; const HIGHLIGHT_COLOR = "#00ff00"; // Green highlight for cyclist // --- State Management --- let isAlertsEnabled = ALERTS_ENABLED_BY_DEFAULT; // --- Core Functions --- /** * Checks the API response to see if the "Cyclist" target is available. * @param {Array} crimes - The array of crime objects from the API. * @returns {boolean} - True if the cyclist is available, false otherwise. */ function isCyclistAvailable(crimes) { if (!crimes || !Array.isArray(crimes)) return false; for (const crime of crimes) { if (crime.title === "Cyclist" && crime.available === true) { return true; } } return false; } /** * Plays the alert sound. */ function playAlertSound() { const audio = new Audio(ALERT_SOUND_URL); audio.play().catch(error => console.error("[Cyclist Ring] Audio playback failed:", error)); } /** * Highlights all available cyclist targets on the page. */ function highlightCyclists() { // The original logic to find the cyclist icon is clever. // It checks the background-position-y of the icon sprite. '0px' is the cyclist. $('.CircularProgressbar').nextAll().each(function() { if ($(this).css('background-position-y') === '0px') { // Traverse up to the main container for the target and apply the highlight $(this).parent().parent().parent().parent().css("background-color", HIGHLIGHT_COLOR); } }); } /** * Sets up the main logic to intercept network requests and check for the cyclist. */ function initializeAlerts() { interceptFetch("torn.com", "/page.php?sid=crimesData", (response) => { // Only run the check if alerts are currently enabled by the user if (!isAlertsEnabled) { return; } const crimes = response?.DB?.crimesByType; if (isCyclistAvailable(crimes)) { playAlertSound(); highlightCyclists(); } }); } /** * Creates the toggle button and adds it to the page. */ function setupInterface() { // *** FIX 1: Check if the button already exists to prevent duplicates *** if (document.getElementById('cyclist-alert-controls')) { return; } const controlsContainer = ` <div id="cyclist-alert-controls" style="margin-bottom: 10px;"> <a id="cyclist-toggle-btn" class="torn-btn"></a> </div> `; $('.pickpocketing-root').prepend(controlsContainer); const toggleButton = $('#cyclist-toggle-btn'); // Function to update the button's appearance based on the current state function updateButtonState() { if (isAlertsEnabled) { toggleButton.text('Cyclist Alerts: ON').css({ 'background': '#4CAF50', 'color': 'white' }); } else { toggleButton.text('Cyclist Alerts: OFF').css({ 'background': '', 'color': '' }); } } // *** FIX 2: Make the button a toggle instead of disappearing *** toggleButton.on('click', () => { isAlertsEnabled = !isAlertsEnabled; // Flip the state updateButtonState(); // Play a sound when enabling as confirmation if (isAlertsEnabled) { playAlertSound(); } }); // Set the initial state of the button when it's first created updateButtonState(); } // --- Utility Functions (kept inside the script to avoid global conflicts) --- function waitForElementToExist(selector) { return new Promise(resolve => { if (document.querySelector(selector)) { return resolve(document.querySelector(selector)); } const observer = new MutationObserver(() => { if (document.querySelector(selector)) { resolve(document.querySelector(selector)); observer.disconnect(); } }); observer.observe(document.body, { subtree: true, childList: true }); }); } function interceptFetch(url, q, callback) { const originalFetch = window.fetch; window.fetch = function(...args) { const [resource, config] = args; const requestUrl = typeof resource === 'string' ? resource : resource.url; return originalFetch.apply(this, args).then(response => { if (response.url.includes(url) && response.url.includes(q)) { const clone = response.clone(); clone.json() .then(json => callback(json, response.url)) .catch(error => console.error("[Cyclist Ring][InterceptFetch] Error parsing JSON:", error)); } return response; }).catch(error => { console.error("[Cyclist Ring][InterceptFetch] Error with fetch:", error); // Still reject the promise to not break the chain return Promise.reject(error); }); }; } // --- Script Entry Point --- // Wait for the crimes container to exist before doing anything waitForElementToExist('.pickpocketing-root').then(() => { console.log("[Cyclist Ring] Pickpocketing container found. Initializing script."); setupInterface(); }); // *** FIX 3: Enable by default by initializing the alerts immediately *** // This runs once when the script is loaded, independent of the UI. initializeAlerts(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址