Torn Racing Telemetry

Track racing data and show acceleration status

当前为 2024-12-29 提交的版本,查看 最新版本

// ==UserScript==
// @name         Torn Racing Telemetry
// @namespace    https://www.torn.com/profiles.php?XID=2782979
// @version      1.3
// @description  Track racing data and show acceleration status
// @match        https://www.torn.com/page.php?sid=racing*
// @match        https://www.torn.com/loader.php?sid=racing*
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // Store previous percentages and timestamps for calculations
    let previousData = {};
    
    // Function to get track data
    function getTrackData() {
        const trackInfoElement = document.querySelector('.track-info');
        const trackLength = trackInfoElement ? parseFloat(trackInfoElement.dataset.length) : 3.4;
        
        const titleElement = document.querySelector('.title-black');
        const lapMatch = titleElement ? titleElement.textContent.match(/(\d+)\s+laps?/) : null;
        const numberOfLaps = lapMatch ? parseInt(lapMatch[1]) : 5;
        
        const totalDistance = trackLength * numberOfLaps;
        
        return {
            trackLength,
            numberOfLaps,
            totalDistance
        };
    }

    // Function to parse race time to seconds
    function parseRaceTime(timeStr) {
        const parts = timeStr.split(':');
        if (parts.length === 2) {
            const minutes = parseFloat(parts[0]);
            const seconds = parseFloat(parts[1]);
            return (minutes * 60) + seconds;
        }
        return 0;
    }
    
    // Function to calculate average speed for finished racers
    function calculateAverageSpeed(timeStr) {
        const { totalDistance } = getTrackData();
        const totalSeconds = parseRaceTime(timeStr);
        if (totalSeconds === 0) return 0;
        
        // Convert to mph (distance in miles / time in hours)
        return (totalDistance / (totalSeconds / 3600));
    }
    
    // Function to calculate speed and acceleration from percentage
    function calculateMetrics(currentPercentage, driverId, currentTime) {
        const { totalDistance } = getTrackData();
        
        if (!previousData[driverId]) {
            previousData[driverId] = {
                percentage: currentPercentage,
                timestamp: currentTime,
                speed: 0,
                acceleration: 'calculating...'
            };
            return {
                speed: 0,
                acceleration: 'calculating...'
            };
        }

        const timeDiff = (currentTime - previousData[driverId].timestamp) / 1000;
        if (timeDiff === 0) return previousData[driverId];

        const previousDistanceCovered = (previousData[driverId].percentage / 100) * totalDistance;
        const currentDistanceCovered = (currentPercentage / 100) * totalDistance;
        const distanceDifference = currentDistanceCovered - previousDistanceCovered;
        
        const speed = (distanceDifference / timeDiff) * 3600;

        let acceleration;
        const speedDiff = speed - (previousData[driverId].speed || 0);
        const threshold = 1;

        if (Math.abs(speedDiff) < threshold) {
            acceleration = 'steady';
        } else if (speedDiff > threshold) {
            acceleration = 'accelerating';
        } else {
            acceleration = 'braking';
        }

        previousData[driverId] = {
            percentage: currentPercentage,
            timestamp: currentTime,
            speed: speed,
            acceleration: acceleration
        };

        return {
            speed: Math.abs(speed),
            acceleration: acceleration
        };
    }
    
    // Function to update status display
    function updateStatusDisplay(nameElement, status, speed, isFinished = false) {
        const existingStatus = nameElement.querySelector('.status-text');
        if (existingStatus) {
            existingStatus.remove();
        }
        
        const statusElement = document.createElement('span');
        statusElement.className = 'status-text';
        statusElement.style.marginLeft = '10px';
        statusElement.style.fontSize = '12px';
        
        if (isFinished) {
            statusElement.style.color = '#666';
            statusElement.textContent = `[Finished - Avg: ${speed.toFixed(1)} mph]`;
        } else if (status === 'not started') {
            statusElement.style.color = '#666';
            statusElement.textContent = '[Not Started]';
        } else {
            statusElement.style.color = status === 'accelerating' ? 'green' : 
                                      status === 'braking' ? 'red' : 'gray';
            const speedText = status === 'calculating...' ? '' : ` - ${Math.abs(speed).toFixed(1)} mph`;
            statusElement.textContent = `[${status}${speedText}]`;
        }
        
        nameElement.appendChild(statusElement);
    }
    
    // Main update function
    function updateRacingData() {
        const drivers = document.querySelectorAll('#leaderBoard li[id^="lbr-"]');
        const currentTime = Date.now();
        
        drivers.forEach(driver => {
            const driverId = driver.id;
            const nameElement = driver.querySelector('.name');
            const timeElement = driver.querySelector('.time');
            const statusElement = driver.querySelector('.status-wrap div');
            
            if (!nameElement || !timeElement) return;
            
            // Check if race is finished for this driver
            const isFinished = statusElement.classList.contains('finished') || 
                             statusElement.classList.contains('gold') || 
                             statusElement.classList.contains('silver') || 
                             statusElement.classList.contains('bronze');
            
            // Check if race hasn't started
            const notStarted = !timeElement.textContent.trim();
            
            if (notStarted) {
                updateStatusDisplay(nameElement, 'not started', 0);
            } else if (isFinished) {
                const avgSpeed = calculateAverageSpeed(timeElement.textContent);
                updateStatusDisplay(nameElement, null, avgSpeed, true);
            } else {
                // Race is in progress
                const currentPercentage = parseFloat(timeElement.textContent.replace('%', ''));
                if (!isNaN(currentPercentage)) {
                    const { speed, acceleration } = calculateMetrics(currentPercentage, driverId, currentTime);
                    updateStatusDisplay(nameElement, acceleration, speed);
                }
            }
        });
    }
    
    // Add custom styles
    const styles = document.createElement('style');
    styles.textContent = `
        .status-text {
            display: inline-block;
            font-family: Arial, sans-serif;
            padding: 2px 5px;
            border-radius: 3px;
            background-color: rgba(0,0,0,0.05);
        }
    `;
    document.head.appendChild(styles);
    
    // Run update every 2 seconds
    setInterval(updateRacingData, 2000);
    
    // Initial update
    updateRacingData();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址