Safe Fold

Requires a double click to fold when checking is available. Prevents accidental folds.

スクリプトをインストールするには、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         Safe Fold
// @namespace    torn.poker.safefold
// @version      1.1
// @description  Requires a double click to fold when checking is available. Prevents accidental folds.
// @author       HopesG
// @match        https://www.torn.com/*
// @grant        none
// @run-at       document-end
// @license      All rights reserved
// ==/UserScript==

(function () {
'use strict';

/* ---------- page check ---------- */

function isPokerPage() {
    return document.body && document.body.dataset.page && document.body.dataset.page.includes('holdem');
}

/* ---------- helpers ---------- */

function getText(el) {
    return (el.innerText || el.textContent || '').replace(/\s+/g, ' ').trim().toLowerCase();
}

function isVisible(el) {
    return !!(el && el.offsetParent !== null);
}

function canCheck() {
    // Look for any visible button that is check or check/fold
    return Array.from(document.querySelectorAll('button')).some(btn => {
        if (!isVisible(btn)) return false;
        const text = getText(btn);
        return text === 'check' || text === 'check / fold' || text === 'call any / check';
    });
}

function findFoldButtons() {
    return Array.from(document.querySelectorAll('button')).filter(btn => {
        return isVisible(btn) && getText(btn) === 'fold';
    });
}

/* ---------- style ---------- */

const STYLE_ID = 'tp-safefold-style';

function injectStyle() {
    if (document.getElementById(STYLE_ID)) return;
    const style = document.createElement('style');
    style.id = STYLE_ID;
    style.textContent = `
    .tp-fold-armed {
        background: linear-gradient(180deg, #ffb347 0%, #cc6600 100%) !important;
        box-shadow:
            inset 0 1px 0 rgba(255,255,255,.20),
            0 0 0 2px rgba(255,160,50,.50),
            0 0 14px rgba(255,140,0,.45) !important;
        animation: tpFoldPulse .4s ease-in-out infinite alternate !important;
        transition: none !important;
    }

    .tp-fold-armed::after {
        content: "Click again to fold";
        position: absolute;
        bottom: calc(100% + 8px);
        left: 50%;
        transform: translateX(-50%);
        background: rgba(20, 10, 0, 0.92);
        color: #ffcc88;
        font-size: 12px;
        font-weight: 600;
        padding: 5px 10px;
        border-radius: 8px;
        white-space: nowrap;
        pointer-events: none;
        box-shadow: 0 2px 8px rgba(0,0,0,.5);
        border: 1px solid rgba(255,160,50,.35);
        z-index: 99999;
    }

    .tp-fold-armed::before {
        /* override the button shine from Colorful Poker if present */
        opacity: 0 !important;
    }

    @keyframes tpFoldPulse {
        from { filter: brightness(1.00); }
        to   { filter: brightness(1.15); }
    }
    `;
    document.head.appendChild(style);
}

/* ---------- core logic ---------- */

const armed = new WeakMap(); // btn → timeout id
const intercepted = new WeakSet();

function armButton(btn) {
    // Already armed — this is the confirmation click, let it through
    if (armed.has(btn)) {
        clearTimeout(armed.get(btn));
        armed.delete(btn);
        btn.classList.remove('tp-fold-armed');
        // Don't block — the real click fires naturally
        return true;
    }

    // First click — arm it
    btn.classList.add('tp-fold-armed');
    const timer = setTimeout(() => {
        btn.classList.remove('tp-fold-armed');
        armed.delete(btn);
    }, 2000);
    armed.set(btn, timer);
    return false; // block this click
}

function interceptFoldButton(btn) {
    if (intercepted.has(btn)) return;
    intercepted.add(btn);

    btn.addEventListener('click', (e) => {
        // Only intercept when check is available
        if (!canCheck()) return;

        // If already armed, let the click through (confirmation)
        if (armed.has(btn)) {
            clearTimeout(armed.get(btn));
            armed.delete(btn);
            btn.classList.remove('tp-fold-armed');
            return; // allow
        }

        // First click — block and arm
        e.preventDefault();
        e.stopImmediatePropagation();
        armButton(btn);
    }, true); // capture phase so we beat Torn's handler
}

/* ---------- main loop ---------- */

function run() {
    if (!isPokerPage()) return;
    injectStyle();
    findFoldButtons().forEach(interceptFoldButton);
}

setInterval(run, 700);

})();