8 Ball Cheat

Cheat for any 8 ball pool game!

スクリプトをインストールするには、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         8 Ball Cheat
// @namespace    http://tampermonkey.net/
// @version      1.0
// @license MIT
// @description  Cheat for any 8 ball pool game!
// @author       Alex
// @match        https://www.crazygames.com/game/8-ball-pool-billiards-multiplayer*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    const SCRIPT_ID = 'Advanced8Ball_Pro_v4';

    // --- Styling ---
    if (typeof GM_addStyle !== 'undefined') {
        GM_addStyle(`
            #${SCRIPT_ID}_SettingsPanel {
                position: fixed; background: rgba(20, 20, 20, 0.9); color: #efefef;
                padding: 15px; border-radius: 10px; z-index: 10005; min-width: 260px;
                font-family: 'Segoe UI', Tahoma, sans-serif; box-shadow: 0 4px 15px rgba(0,0,0,0.5);
                border: 1px solid #444; backdrop-filter: blur(4px);
            }
            #${SCRIPT_ID}_PanelHeader { cursor: move; font-weight: bold; border-bottom: 1px solid #444; padding-bottom: 8px; margin-bottom: 10px; text-align: center; color: #00ffcc; }
            .helper-btn { width: 100%; margin-top: 8px; padding: 6px; cursor: pointer; background: #333; color: white; border: 1px solid #555; border-radius: 4px; transition: 0.2s; font-size: 12px; }
            .helper-btn:hover { background: #444; border-color: #00ffcc; }
            .helper-btn.active { background: #a82323; border-color: #ff4444; color: white; font-weight: bold; }
            .helper-label { display: block; margin-top: 8px; font-size: 13px; color: #bbb; }
            .marker-active { outline: 2px solid gold !important; box-shadow: 0 0 10px gold; }
            .profile-input { width: 100%; padding: 5px; box-sizing: border-box; background: #222; border: 1px solid #555; color: #fff; border-radius: 4px; margin-top: 4px; font-size: 12px; }
            .profile-select { width: 100%; padding: 5px; box-sizing: border-box; background: #222; border: 1px solid #555; color: #00ffcc; border-radius: 4px; margin-top: 4px; font-size: 12px; }
            .profile-row { display: flex; gap: 5px; margin-top: 5px; }
            .profile-row button { margin-top: 0; }
            #${SCRIPT_ID}_PanBlocker {
                position: fixed; z-index: 10001; background: rgba(0, 255, 204, 0.05);
                border: 2px dashed #00ffcc; cursor: move; display: none; box-sizing: border-box;
            }
        `);
    }

    let config = {
        calibPoints: [{x:50,y:50}, {x:750,y:50}, {x:750,y:450}, {x:50,y:450}],
        cueBallPos: { x: 400, y: 300 },
        lineWidth: 2,
        guideLineColor: 'rgba(255, 255, 255, 0.2)',
        ballRadius: 11,
        calibrationLocked: false,
        showGhostBall: true,
        settingsPanelVisible: true,
        settingsPanelPos: { top: '20px', left: '20px' }
    };

    let gameIframe = null, overlayCanvas = null, ctx = null, settingsPanel = null;
    let cueBallMarker = null, calibMarkers = [], cachedPockets = [];
    let activeElement = null;
    let isPanningMode = false, panBlocker = null;
    let manualPocketOverride = -1;

    function iframeRect() { return gameIframe?.getBoundingClientRect(); }

    function updateCachedPockets() {
        if (config.calibPoints.length !== 4) return;
        const [tl, tr, br, bl] = config.calibPoints;
        cachedPockets = [
            { x: tl.x, y: tl.y, name: "Sup. Esquerda (1)" },
            { x: tr.x, y: tr.y, name: "Sup. Direita (2)" },
            { x: br.x, y: br.y, name: "Inf. Direita (3)" },
            { x: bl.x, y: bl.y, name: "Inf. Esquerda (4)" },
            { x: (tl.x + tr.x) / 2, y: (tl.y + tr.y) / 2, name: "Meio Sup. (5)" },
            { x: (bl.x + br.x) / 2, y: (bl.y + br.y) / 2, name: "Meio Inf. (6)" }
        ];
    }

    function save() { if (typeof GM_setValue !== 'undefined') GM_setValue(SCRIPT_ID + '_cfg', JSON.stringify(config)); }
    function load() {
        if (typeof GM_getValue !== 'undefined') {
            const saved = GM_getValue(SCRIPT_ID + '_cfg');
            if (saved) { try { Object.assign(config, JSON.parse(saved)); } catch(e) {} }
        }
    }

    function getProfiles() {
        const profiles = localStorage.getItem(SCRIPT_ID + '_profiles');
        return profiles ? JSON.parse(profiles) : {};
    }

    document.body.addEventListener('click', function(e) {
        if (e.target && e.target.id === 'save_profile') {
            const name = document.getElementById('profile_name').value;
            if (!name.trim()) return alert("Por favor digite um nome válido.");
            const profiles = getProfiles();
            profiles[name] = { calibPoints: config.calibPoints, cueBallPos: config.cueBallPos, lineWidth: config.lineWidth, ballRadius: config.ballRadius, showGhostBall: config.showGhostBall };
            localStorage.setItem(SCRIPT_ID + '_profiles', JSON.stringify(profiles));
            updateProfileDropdown();
            document.getElementById('profile_name').value = '';
        }
        if (e.target && e.target.id === 'load_profile') {
            const name = document.getElementById('profile_select').value;
            if (!name) return;
            const profiles = getProfiles();
            if (profiles[name]) {
                Object.assign(config, profiles[name]);
                document.getElementById('lw_in').value = config.lineWidth;
                document.getElementById('lw_val').innerText = config.lineWidth;
                document.getElementById('bs_in').value = config.ballRadius;
                document.getElementById('gb_toggle').checked = config.showGhostBall;
                updateCachedPockets(); updateMarkersPosition(); draw(); save();
            }
        }
        if (e.target && e.target.id === 'delete_profile') {
            const name = document.getElementById('profile_select').value;
            if (!name) return;
            const profiles = getProfiles();
            if (profiles[name]) {
                delete profiles[name];
                localStorage.setItem(SCRIPT_ID + '_profiles', JSON.stringify(profiles));
                updateProfileDropdown();
            }
        }
    });

    function updateProfileDropdown() {
        const select = document.getElementById('profile_select'); if (!select) return;
        select.innerHTML = '<option value="">-- Carregar Formato Salvo --</option>';
        const profiles = getProfiles();
        for (let name in profiles) {
            const opt = document.createElement('option'); opt.value = name; opt.innerText = name; select.appendChild(opt);
        }
    }

    function createPanel() {
        settingsPanel = document.createElement('div');
        settingsPanel.id = SCRIPT_ID + '_SettingsPanel';
        settingsPanel.style.top = config.settingsPanelPos.top;
        settingsPanel.style.left = config.settingsPanelPos.left;
        settingsPanel.style.display = config.settingsPanelVisible ? 'block' : 'none';
        settingsPanel.innerHTML = `
            <div id="${SCRIPT_ID}_PanelHeader">8 BALL PRO HELPER</div>
            <label class="helper-label">Line Width: <span id="lw_val">${config.lineWidth}</span></label>
            <input type="range" id="lw_in" min="1" max="5" value="${config.lineWidth}" style="width:100%">
            <label class="helper-label">Ball Scale (Ghost Ball Size):</label>
            <input type="range" id="bs_in" min="5" max="25" value="${config.ballRadius}" style="width:100%">
            <label class="helper-label"><input type="checkbox" id="gb_toggle" ${config.showGhostBall ? 'checked' : ''}> Show Ghost Balls</label>
            <label class="helper-label"><input type="checkbox" id="lock_toggle" ${config.calibrationLocked ? 'checked' : ''}> Lock Calibration</label>
            <button id="toggle_pan" class="helper-btn">Move Whole Table Grid</button>
            <button id="reset_cal" class="helper-btn">Reset Grid</button>
            <hr style="border: 0; border-top: 1px solid #444; margin: 12px 0 8px 0;">
            <label class="helper-label" style="color: #00ffcc;">Format Configuration Manager</label>
            <input type="text" id="profile_name" class="profile-input" placeholder="Nome do layout...">
            <button id="save_profile" class="helper-btn" style="background: #2a5236; border-color: #3b7a4e;">Salvar Formato Atual</button>
            <select id="profile_select" class="profile-select"></select>
            <div class="profile-row">
                <button id="load_profile" class="helper-btn" style="background: #28446b; border-color: #3b6299;">Carregar</button>
                <button id="delete_profile" class="helper-btn" style="background: #5c2626; border-color: #823636;">Excluir</button>
            </div>
            <div style="font-size:10px; color:#aaa; margin-top:10px; border-top: 1px solid #333; padding-top: 5px;">
                <b style="color:#00ffcc;">Controle de Exceção Dinâmico:</b><br>
                Teclas <span style="color:#fff; background:#333; padding:1px 4px; border-radius:3px;">1 a 6</span>: Foca mira em uma única caçapa.<br>
                Tecla <span style="color:#fff; background:#333; padding:1px 4px; border-radius:3px;">0</span>: Reseta e mostra todas as caçapas.<br>
                <span id="active_pocket_status" style="color:#ffcc00; display:block; margin-top:4px;">Modo: Todas as Caçapas</span>
            </div>
        `;
        document.body.appendChild(settingsPanel);
        document.getElementById('lw_in').oninput = (e) => { config.lineWidth = parseInt(e.target.value); document.getElementById('lw_val').innerText = config.lineWidth; draw(); };
        document.getElementById('bs_in').oninput = (e) => { config.ballRadius = parseInt(e.target.value); draw(); };
        document.getElementById('gb_toggle').onchange = (e) => { config.showGhostBall = e.target.checked; draw(); };
        document.getElementById('lock_toggle').onchange = (e) => { config.calibrationLocked = e.target.checked; updateMarkersVisibility(); save(); };
        document.getElementById('toggle_pan').onclick = toggleTablePanning;
        document.getElementById('reset_cal').onclick = () => { resetCalib(); save(); draw(); };
        updateProfileDropdown();
        setupDraggable(document.getElementById(`${SCRIPT_ID}_PanelHeader`), settingsPanel, 'panel');
    }

    function toggleTablePanning() {
        const btn = document.getElementById('toggle_pan'); isPanningMode = !isPanningMode;
        if (isPanningMode) { btn.classList.add('active'); btn.innerText = 'LOCK GRID POSITION'; panBlocker.style.display = 'block'; syncPanBlockerSize(); }
        else { btn.classList.remove('active'); btn.innerText = 'Move Whole Table Grid'; panBlocker.style.display = 'none'; }
    }

    function syncPanBlockerSize() {
        const r = iframeRect(); if (!r || !panBlocker) return;
        panBlocker.style.left = r.left + 'px'; panBlocker.style.top = r.top + 'px'; panBlocker.style.width = r.width + 'px'; panBlocker.style.height = r.height + 'px';
    }

    function resetCalib() {
        config.calibPoints = [{x:50,y:50}, {x:750,y:50}, {x:750,y:450}, {x:50,y:450}]; config.cueBallPos = { x: 400, y: 300 };
        updateCachedPockets(); updateMarkersPosition(); if (isPanningMode) syncPanBlockerSize();
    }

    function setupDraggable(handle, target, type, index = -1) {
        handle.onpointerdown = (e) => {
            handle.setPointerCapture(e.pointerId); const startX = e.clientX, startY = e.clientY;
            const initialX = type === 'panel' ? target.offsetLeft : (type === 'cue' ? config.cueBallPos.x : (index !== -1 ? config.calibPoints[index].x : 0));
            const initialY = type === 'panel' ? target.offsetTop : (type === 'cue' ? config.cueBallPos.y : (index !== -1 ? config.calibPoints[index].y : 0));
            const initialCalibPoints = config.calibPoints.map(p => ({ ...p })); const initialCueBall = { ...config.cueBallPos };
            document.querySelectorAll('.marker-active').forEach(m => m.classList.remove('marker-active'));
            if (type !== 'panel' && type !== 'table_pan') { handle.classList.add('marker-active'); activeElement = { type, index }; }
            handle.onpointermove = (moveEvt) => {
                const dx = moveEvt.clientX - startX; const dy = moveEvt.clientY - startY;
                if (type === 'panel') { target.style.left = (initialX + dx) + 'px'; target.style.top = (initialY + dy) + 'px'; config.settingsPanelPos = { top: target.style.top, left: target.style.left }; }
                else if (type === 'table_pan') { config.calibPoints = initialCalibPoints.map(p => ({ x: p.x + dx, y: p.y + dy })); config.cueBallPos = { x: initialCueBall.x + dx, y: initialCueBall.y + dy }; updateCachedPockets(); updateMarkersPosition(); draw(); }
                else {
                    const newX = initialX + dx; const newY = initialY + dy;
                    if (type === 'cue') { config.cueBallPos = { x: newX, y: newY }; } else { config.calibPoints[index] = { x: newX, y: newY }; updateCachedPockets(); }
                    updateMarkersPosition(); draw();
                }
            };
            handle.onpointerup = () => { handle.onpointermove = null; save(); };
        };
    }

    function createMarkers() {
        overlayCanvas = document.createElement('canvas');
        Object.assign(overlayCanvas.style, { position:'fixed', top:'0', left:'0', zIndex:'10000', pointerEvents:'none' });
        document.body.appendChild(overlayCanvas);
        ctx = overlayCanvas.getContext('2d');
        panBlocker = document.createElement('div');
        panBlocker.id = SCRIPT_ID + '_PanBlocker';
        document.body.appendChild(panBlocker);
        setupDraggable(panBlocker, null, 'table_pan');
        cueBallMarker = document.createElement('div');
        Object.assign(cueBallMarker.style, { position:'fixed', width:'20px', height:'20px', border:'2px solid red', borderRadius:'50%', zIndex:'10003', cursor:'move' });
        document.body.appendChild(cueBallMarker);
        setupDraggable(cueBallMarker, null, 'cue');
        for (let i = 0; i < 4; i++) {
            const m = document.createElement('div');
            Object.assign(m.style, { position:'fixed', width:'12px', height:'12px', background:'lime', zIndex:'10003', cursor:'move' });
            document.body.appendChild(m);
            setupDraggable(m, null, 'calib', i);
            calibMarkers.push(m);
        }
        updateMarkersVisibility();
        updateMarkersPosition();
    }

    function updateMarkersPosition() {
        const r = iframeRect(); if (!r) return;
        cueBallMarker.style.left = (r.left + config.cueBallPos.x - 10) + 'px'; cueBallMarker.style.top = (r.top + config.cueBallPos.y - 10) + 'px';
        calibMarkers.forEach((m, i) => { m.style.left = (r.left + config.calibPoints[i].x - 6) + 'px'; m.style.top = (r.top + config.calibPoints[i].y - 6) + 'px'; });
    }

    function updateMarkersVisibility() { calibMarkers.forEach(m => m.style.display = config.calibrationLocked ? 'none' : 'block'); }

    function draw() {
        if (!ctx || !gameIframe) return;
        const r = iframeRect(); overlayCanvas.width = window.innerWidth; overlayCanvas.height = window.innerHeight;
        ctx.save(); ctx.translate(r.left, r.top);
        if (!config.calibrationLocked) {
            ctx.strokeStyle = config.guideLineColor; ctx.lineWidth = 1; ctx.beginPath();
            ctx.moveTo(config.calibPoints[0].x, config.calibPoints[0].y);
            config.calibPoints.forEach(p => ctx.lineTo(p.x, p.y)); ctx.closePath(); ctx.stroke();
        }
        let pocketDistances = cachedPockets.map((p, idx) => {
            let dx = p.x - config.cueBallPos.x; let dy = p.y - config.cueBallPos.y;
            return { index: idx, distance: Math.sqrt(dx * dx + dy * dy), pocket: p };
        });
        let sortedPockets = [...pocketDistances].sort((a, b) => a.distance - b.distance);
        let closestIndex = sortedPockets[0]?.index;
        let maxDist = sortedPockets[sortedPockets.length - 1]?.distance || 1;
        pocketDistances.forEach(item => {
            let p = item.pocket; let idx = item.index;
            if (manualPocketOverride !== -1 && manualPocketOverride !== idx) return;
            let currentLineColor = '#ff3333';
            if (idx === closestIndex) currentLineColor = '#00ff55';
            else if (item.distance < maxDist * 0.65) currentLineColor = '#ffcc00';
            ctx.beginPath();
            if (manualPocketOverride === idx) { ctx.setLineDash([]); ctx.lineWidth = config.lineWidth + 1; }
            else { ctx.setLineDash([5, 5]); ctx.lineWidth = config.lineWidth; }
            ctx.strokeStyle = currentLineColor;
            ctx.moveTo(config.cueBallPos.x, config.cueBallPos.y);
            ctx.lineTo(p.x, p.y); ctx.stroke(); ctx.setLineDash([]);
            if (config.showGhostBall) {
                ctx.beginPath();
                ctx.arc(p.x, p.y, config.ballRadius + (manualPocketOverride === idx ? 2 : 0), 0, Math.PI * 2);
                ctx.fillStyle = currentLineColor === '#00ff55' ? 'rgba(0, 255, 85, 0.15)' : (currentLineColor === '#ffcc00' ? 'rgba(255, 204, 0, 0.15)' : 'rgba(255, 51, 51, 0.15)');
                ctx.fill(); ctx.strokeStyle = currentLineColor; ctx.stroke();
            }
        });
        ctx.restore();
    }

    window.addEventListener('keydown', (e) => {
        const key = e.key.toLowerCase();
        if (e.key >= '1' && e.key <= '6') {
            const index = parseInt(e.key) - 1;
            if (cachedPockets[index]) {
                manualPocketOverride = index;
                const status = document.getElementById('active_pocket_status');
                if (status) status.innerHTML = `Foco: <span style="color:#00ffcc;">${cachedPockets[index].name}</span>`;
                draw();
            }
        }
        if (e.key === '0') {
            manualPocketOverride = -1;
            const status = document.getElementById('active_pocket_status');
            if (status) status.innerText = 'Modo: Todas as Caçapas';
            draw();
        }
        if (e.key === 'Insert' || key === 'm') {
            config.settingsPanelVisible = !config.settingsPanelVisible;
            settingsPanel.style.display = config.settingsPanelVisible ? 'block' : 'none';
            save();
        }
        if (key === 'h') { overlayCanvas.style.visibility = overlayCanvas.style.visibility === 'hidden' ? 'visible' : 'hidden'; }
        if (activeElement) {
            const step = e.shiftKey ? 5 : 1;
            let target = activeElement.type === 'cue' ? config.cueBallPos : config.calibPoints[activeElement.index];
            if (e.key === 'ArrowUp') target.y -= step;
            if (e.key === 'ArrowDown') target.y += step;
            if (e.key === 'ArrowLeft') target.x -= step;
            if (e.key === 'ArrowRight') target.x += step;
            if (e.key.startsWith('Arrow')) { e.preventDefault(); updateCachedPockets(); updateMarkersPosition(); draw(); save(); }
        }
    });

    // FIX 1: Use #game-iframe id as primary selector (CrazyGames sets src dynamically)
    // FIX 2: setInterval(draw, 100) not setInterval(draw(), 100)
    function init() {
        load();
        gameIframe = document.querySelector('#game-iframe') || document.querySelector('iframe[src*="8-ball-pool"]');
        if (!gameIframe) return setTimeout(init, 1000);
        updateCachedPockets(); createMarkers(); createPanel();
        window.addEventListener('resize', () => { updateMarkersPosition(); if (isPanningMode) syncPanBlockerSize(); draw(); });
        new ResizeObserver(() => { updateMarkersPosition(); if (isPanningMode) syncPanBlockerSize(); draw(); }).observe(gameIframe);
        setInterval(draw, 100); // FIX 2: was setInterval(draw(), 100)
        draw();
    }

    init();
})();