Cheat for any 8 ball pool game!
// ==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();
})();