Greasy Fork is available in English.

Torn Quick Bust Helper

quick bust helper for Torn.com jail. Just spam J on your keyboard.

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         Torn Quick Bust Helper
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  quick bust helper for Torn.com jail. Just spam J on your keyboard.
// @author       GFOUR
// @match        https://www.torn.com/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// ==/UserScript==

(function() {
    'use strict';

    // =========================
    // Configuration & Constants
    // =========================
    const SELECTORS = {
        nerveBar: '.nerve___AyYv_ .bar-value___NTdce',
        bustAvailable: '.bustr-stats__availableBusts',
        jailEntry: 'li',
        level: '.level',
        time: '.time',
        confirmBust: '.confirm-bust',
        bustButton: 'a.bust',
        pageNumberActive: '.page-number.active .page-nb',
        pageNumberAll: '.page-number .page-nb'
    };

    let maxHardness = GM_getValue('maxHardness', 50);
    let debugMode = GM_getValue('debugMode', false);


    // =========================
    // Debug Overlay
    // =========================
    const debugOverlay = document.createElement('div');
    debugOverlay.style.cssText = `
        position: fixed;
        top: 10px;
        right: 10px;
        max-width: 300px;
        background: rgba(0,0,0,0.8);
        color: #0f0;
        font-size: 12px;
        font-family: monospace;
        padding: 5px 10px;
        border-radius: 4px;
        z-index: 10000;
        overflow-y: auto;
        max-height: 400px;
        display: none;
    `;
    document.body.appendChild(debugOverlay);

    function debugLog(message) {
        if (debugMode) {
            console.log('[Bust Helper]', message);
            const line = document.createElement('div');
            line.textContent = message;
            debugOverlay.appendChild(line);
            debugOverlay.scrollTop = debugOverlay.scrollHeight;
        }
    }

    // =========================
    // Menu Commands
    // =========================
    GM_registerMenuCommand('Set Max Hardness', () => {
        const newValue = prompt('Enter maximum hardness threshold:', maxHardness);
        if (newValue !== null && !isNaN(newValue)) {
            maxHardness = parseInt(newValue);
            GM_setValue('maxHardness', maxHardness);
            showPopup(`Max hardness set to: ${maxHardness}`, 'info');
        }
    });


    GM_registerMenuCommand('Toggle Debug Mode', () => {
        debugMode = !debugMode;
        GM_setValue('debugMode', debugMode);
        debugOverlay.style.display = debugMode ? 'block' : 'none';
        showPopup(`Debug mode: ${debugMode ? 'ON' : 'OFF'}`, 'info');
    });

    // =========================
    // Popups
    // =========================
    function showPopup(message, type = 'error', duration = 2000) {
        const popup = document.createElement('div');
        const bgColors = { info: '#333', error: '#ff4c4c', success: '#4caf50', warn: '#ff9900' };
        popup.style.cssText = `
            position: fixed;
            bottom: 20px;
            left: 20px;
            background: ${bgColors[type] || '#333'};
            color: #fff;
            padding: 12px 20px;
            border-radius: 6px;
            z-index: 10000;
            font-size: 16px;
            font-weight: bold;
            box-shadow: 0 0 8px rgba(0,0,0,0.6);
            opacity: 0;
            transform: translateY(15px);
            transition: opacity 0.25s ease, transform 0.25s ease;
            pointer-events: none;
        `;
        popup.textContent = message;
        document.body.appendChild(popup);

        requestAnimationFrame(() => {
            popup.style.opacity = "1";
            popup.style.transform = "translateY(0)";
        });

        setTimeout(() => {
            popup.style.opacity = "0";
            popup.style.transform = "translateY(15px)";
            setTimeout(() => popup.remove(), 250);
        }, duration);
    }

    // =========================
    // Utility Functions
    // =========================
    function parseTimeToHours(timeString) {
        return (timeString.match(/(\d+)([dhm])/g) || []).reduce((total, part) => {
            const value = parseInt(part);
            const unit = part.slice(-1);
            if (unit === 'd') return total + value * 24;
            if (unit === 'h') return total + value;
            if (unit === 'm') return total + value / 60;
            return total;
        }, 0);
    }


    // =========================
    // Jail Helper Module
    // =========================
    const JailHelper = {
        getEntries: () => Array.from(document.querySelectorAll(SELECTORS.jailEntry))
            .filter(li => li.querySelector(SELECTORS.level)),

        getCurrentNerve: () => {
            const nerveBar = document.querySelector(SELECTORS.nerveBar);
            if (nerveBar) {
                const current = parseInt(nerveBar.textContent.split('/')[0]);
                debugLog(`Current nerve: ${current}`);
                return current;
            }
            debugLog('Nerve bar not found');
            showPopup('Nerve info missing!', 'warn');
            return 0;
        },

        getAvailableBusts: () => {
            const bustBar = document.querySelector(SELECTORS.bustAvailable);
            if (bustBar) {
                const current = parseInt(bustBar.textContent.split('/')[0]);
                debugLog(`Busts available: ${current}`);
                return current;
            }
            debugLog('Bust bar not found');
            showPopup('Bust info missing!', 'warn');
            return 0;
        },

        getIsInJail: () => {
            const body = document.querySelector("body");
            if (body) {
                const layout = body.getAttribute("data-layout");
                if (layout === "jail") {
                    debugLog("Player is in jail.");
                    return true;
                } else {
                    debugLog(`Player not in jail. Current layout: ${layout}`);
                    return false;
                }
            }
            debugLog("Body element not found.");
            showPopup("Page structure missing!", "warn");
            return false;
        },



        calculateHardness: (entry) => {
            const existing = entry.querySelector('.bustr-hardness-score');
            if (existing) return parseInt(existing.textContent);

            const levelElement = entry.querySelector(SELECTORS.level);
            if (!levelElement) return null;
            const levelMatch = levelElement.textContent.match(/LEVEL\s*:\s*(\d+)/);
            if (!levelMatch) return null;
            const level = parseInt(levelMatch[1]);

            const timeElement = entry.querySelector(SELECTORS.time);
            if (!timeElement) return null;
            const timeMatch = timeElement.textContent.match(/TIME\s*:\s*(.+)/);
            if (!timeMatch) return null;

            const durationInHours = parseTimeToHours(timeMatch[1]);
            const hardness = Math.floor(level * (durationInHours + 3));

            debugLog(`Calculated hardness: Level ${level}, Time ${timeMatch[1]} = ${hardness}`);
            return hardness;
        },

        hasAnyPlayers: () => {
            const entries = JailHelper.getEntries();
            debugLog(`Found ${entries.length} jail entries`);
            return entries.length > 0;
        },

        findBustableTarget: () => {
            const candidates = [];
            const entries = JailHelper.getEntries();

            for (const entry of entries) {
                const confirmBust = entry.querySelector(SELECTORS.confirmBust);
                if (confirmBust && confirmBust.style.display !== 'none') {
                    const ajaxAction = confirmBust.querySelector('.ajax-action');
                    if (ajaxAction && ajaxAction.textContent.trim()) continue;
                }

                const hardness = JailHelper.calculateHardness(entry);
                if (hardness === null) continue;

                if (hardness <= maxHardness) {
                    const bustButton = entry.querySelector(SELECTORS.bustButton);
                    if (bustButton) {
                        // Visual indication of target
                        entry.style.outline = '2px solid #ff4c4c';
                        candidates.push({ entry, hardness, bustButton });
                    }
                }
            }

            if (candidates.length === 0) {
                debugLog('No bustable targets found');
                return null;
            }

            candidates.sort((a, b) => a.hardness - b.hardness);
            debugLog(`Found ${candidates.length} candidates, choosing hardness ${candidates[0].hardness}`);
            return candidates[0].bustButton;
        },

        getCurrentPage: () => {
            const activePage = document.querySelector(SELECTORS.pageNumberActive);
            return activePage ? parseInt(activePage.textContent) : 1;
        },

        getTotalPages: () => {
            const pages = document.querySelectorAll(SELECTORS.pageNumberAll);
            let max = 1;
            pages.forEach(p => { const n = parseInt(p.textContent); if (n > max) max = n; });
            return max;
        },

        navigateToPage: (pageNum) => {
            const targetPage = document.querySelector(`a[page="${pageNum}"]`);
            if (targetPage) {
                targetPage.click();
            } else if (pageNum === 1) {
                location.reload();
            }
        },

        goToPageOne: () => {
            window.location.href = 'https://www.torn.com/jailview.php';
        }
    };

    // =========================
    // Main Bust Logic
    // =========================
    function performBust() {
        debugLog('=== BUST ATTEMPT ===');

        const isInJail = JailHelper.getIsInJail();
        if (isInJail) {showPopup('You\'re in Jail', 'warn'); return; }

        const currentNerve = JailHelper.getCurrentNerve();
        if (currentNerve < 5) { showPopup('Not enough nerve!', 'warn'); return; }

        const availableBusts = JailHelper.getAvailableBusts();
        if (availableBusts === 0) { showPopup('No Busts Available!', 'warn'); return; }


        const targetUrl = "https://www.torn.com/jailview.php";
        if (!window.location.href.startsWith(targetUrl)) { window.location.href = targetUrl; return; }

        if (!JailHelper.hasAnyPlayers()) { JailHelper.goToPageOne(); return; }

        const bustTarget = JailHelper.findBustableTarget();
        if (bustTarget && bustTarget.click) {
            debugLog('Clicking bust button');
            bustTarget.click();
        } else {
            const currentPage = JailHelper.getCurrentPage();
            const totalPages = JailHelper.getTotalPages();
            if (currentPage < totalPages) JailHelper.navigateToPage(currentPage + 1);
            else JailHelper.navigateToPage(1);
        }
    }

    // =========================
    // Shortcut Listener
    // =========================
    document.addEventListener('keydown', function(event) {
        if (event.code === 'KeyJ' && !event.target.matches('input, textarea, [contenteditable]')) {
            event.preventDefault();
            performBust();
        }
    });

    console.log('Torn Quick Bust Helper Pro loaded. Press Alt+J to bust. Use Tampermonkey menu to configure settings and debug overlay.');
})();