8 Ball Cheat

Cheat for any 8 ball pool game!

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

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

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==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();
})();