您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Cheat for 8 Ball Pool on CrazyGames with manual calibration and correct alignment.
// ==UserScript== // @name 8 Ball Helper for CrazyGames // @namespace http://tampermonkey.net/ // @version 2.1.0 // @description Cheat for 8 Ball Pool on CrazyGames with manual calibration and correct alignment. // @author Adapted by Mazurka // @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 = 'Advanced8BallPoolGuide_v2_1_0'; // Default config with calibration points roughly around the iframe center let config = { calibPoints: [ { x: 50, y: 50 }, // Top-left corner of game area inside iframe { x: 750, y: 50 }, // Top-right { x: 750, y: 450 }, // Bottom-right { x: 50, y: 450 } // Bottom-left ], cueBallPos: { x: 400, y: 300 }, // Initial cue ball position inside iframe lineWidth: 2, lineColor: '#FFFFFF', guideLineColor: 'rgba(200, 200, 200, 0.6)', calibPointMarkerColor: 'rgba(0, 255, 0, 0.5)', calibPointSize: 30, cueBallSize: 15, calibrationLocked: false, settingsPanelVisible: true, settingsPanelPos: { top: '10px', left: '10px' } }; let gameIframe = null; let overlayCanvas = null; let ctx = null; let settingsPanel = null; let cueBallMarker = null; let calibMarkers = []; let isDraggingCueBall = false; let isDraggingCalibPoint = false; let activeCalibIndex = -1; let isDraggingPanel = false; let dragStartMouse = { x: 0, y: 0 }; let dragStartPos = { x: 0, y: 0 }; let drawRequested = false; // Calculate pockets based on calibration points function calculatePockets() { if (config.calibPoints.length !== 4) return []; const [tl, tr, br, bl] = config.calibPoints; let pockets = []; pockets.push({ x: tl.x, y: tl.y, name: 'Top-Left' }); pockets.push({ x: tr.x, y: tr.y, name: 'Top-Right' }); pockets.push({ x: br.x, y: br.y, name: 'Bottom-Right' }); pockets.push({ x: bl.x, y: bl.y, name: 'Bottom-Left' }); // Mid pockets on top and bottom cushions pockets.push({ x: (tl.x + tr.x) / 2, y: (tl.y + tr.y) / 2, name: 'Top-Mid' }); pockets.push({ x: (bl.x + br.x) / 2, y: (bl.y + br.y) / 2, name: 'Bottom-Mid' }); return pockets; } // Save config persistently function saveSettings() { GM_setValue(SCRIPT_ID + '_settings', JSON.stringify(config)); } // Load config from storage function loadSettings() { const saved = GM_getValue(SCRIPT_ID + '_settings'); if (saved) { try { const loaded = JSON.parse(saved); for (const key in config) { if (loaded[key] !== undefined) { config[key] = loaded[key]; } } } catch (e) { console.warn(SCRIPT_ID + ': Failed to parse saved settings', e); } } } // Find the game iframe by matching its src URL pattern function getGameIframe() { const iframes = document.querySelectorAll('iframe'); for (const iframe of iframes) { if (iframe.src && iframe.src.includes('8-ball-pool-billiards-multiplayer')) { return iframe; } } return null; } // Create the settings panel UI function createSettingsPanel() { settingsPanel = document.createElement('div'); settingsPanel.id = SCRIPT_ID + '_SettingsPanel'; settingsPanel.style.position = 'fixed'; settingsPanel.style.top = config.settingsPanelPos.top; settingsPanel.style.left = config.settingsPanelPos.left; settingsPanel.style.backgroundColor = 'rgba(50, 50, 50, 0.9)'; settingsPanel.style.color = 'white'; settingsPanel.style.padding = '10px'; settingsPanel.style.borderRadius = '8px'; settingsPanel.style.zIndex = 10002; settingsPanel.style.minWidth = '250px'; settingsPanel.style.fontFamily = 'Arial, sans-serif'; settingsPanel.style.userSelect = 'none'; settingsPanel.style.display = config.settingsPanelVisible ? 'block' : 'none'; settingsPanel.innerHTML = ` <div id="${SCRIPT_ID}_PanelHeader" style="cursor: grab; font-weight: bold; margin-bottom: 8px; text-align: center;">8 Ball Pool Guide (Toggle: Insert)</div> <label>Line Width: <span id="${SCRIPT_ID}_lineWidthValue">${config.lineWidth}</span> px</label> <input type="range" id="${SCRIPT_ID}_lineWidth" min="1" max="10" value="${config.lineWidth}" style="width: 100%;"> <label>Line Color:</label> <input type="color" id="${SCRIPT_ID}_lineColor" value="${config.lineColor}" style="width: 100%;"> <label><input type="checkbox" id="${SCRIPT_ID}_calibrationLocked" ${config.calibrationLocked ? 'checked' : ''}> Lock Calibration Points</label> <button id="${SCRIPT_ID}_saveSettings" style="width: 100%; margin-top: 10px;">Save Settings</button> <button id="${SCRIPT_ID}_resetCalibration" style="width: 100%; margin-top: 5px;">Reset Calibration</button> <div style="font-size: 12px; margin-top: 10px; color: #ccc;">Drag green points to calibrate corners.<br>Drag red circle to move cue ball.</div> `; document.body.appendChild(settingsPanel); // Event listeners document.getElementById(`${SCRIPT_ID}_lineWidth`).addEventListener('input', e => { config.lineWidth = parseInt(e.target.value); document.getElementById(`${SCRIPT_ID}_lineWidthValue`).textContent = config.lineWidth; requestDraw(); }); document.getElementById(`${SCRIPT_ID}_lineColor`).addEventListener('input', e => { config.lineColor = e.target.value; requestDraw(); }); document.getElementById(`${SCRIPT_ID}_calibrationLocked`).addEventListener('change', e => { config.calibrationLocked = e.target.checked; updateMarkersVisibility(); saveSettings(); }); document.getElementById(`${SCRIPT_ID}_saveSettings`).addEventListener('click', () => { saveSettings(); alert('Settings saved!'); }); document.getElementById(`${SCRIPT_ID}_resetCalibration`).addEventListener('click', () => { resetCalibrationPoints(); saveSettings(); requestDraw(); }); // Drag panel const header = document.getElementById(`${SCRIPT_ID}_PanelHeader`); header.addEventListener('mousedown', e => { isDraggingPanel = true; dragStartMouse = { x: e.clientX, y: e.clientY }; dragStartPos = { x: settingsPanel.offsetLeft, y: settingsPanel.offsetTop }; header.style.cursor = 'grabbing'; document.body.style.userSelect = 'none'; }); window.addEventListener('mouseup', () => { if (isDraggingPanel) { isDraggingPanel = false; header.style.cursor = 'grab'; document.body.style.userSelect = ''; config.settingsPanelPos = { top: settingsPanel.style.top, left: settingsPanel.style.left }; saveSettings(); } }); window.addEventListener('mousemove', e => { if (isDraggingPanel) { const dx = e.clientX - dragStartMouse.x; const dy = e.clientY - dragStartMouse.y; settingsPanel.style.left = dragStartPos.x + dx + 'px'; settingsPanel.style.top = dragStartPos.y + dy + 'px'; } }); // Toggle panel visibility with Insert key window.addEventListener('keydown', e => { if (e.key === 'Insert') { config.settingsPanelVisible = !config.settingsPanelVisible; settingsPanel.style.display = config.settingsPanelVisible ? 'block' : 'none'; saveSettings(); } }); } // Reset calibration points to default function resetCalibrationPoints() { config.calibPoints = [ { x: 50, y: 50 }, { x: 750, y: 50 }, { x: 750, y: 450 }, { x: 50, y: 450 } ]; config.cueBallPos = { x: 400, y: 300 }; updateMarkersPosition(); } // Create overlay canvas and draggable markers function createOverlayAndMarkers() { // Create overlay canvas overlayCanvas = document.createElement('canvas'); overlayCanvas.id = SCRIPT_ID + '_Overlay'; overlayCanvas.style.position = 'absolute'; overlayCanvas.style.top = '0'; overlayCanvas.style.left = '0'; overlayCanvas.style.zIndex = 10000; overlayCanvas.style.pointerEvents = 'none'; // Let mouse events pass through document.body.appendChild(overlayCanvas); ctx = overlayCanvas.getContext('2d'); // Create cue ball marker (red circle) cueBallMarker = document.createElement('div'); cueBallMarker.id = SCRIPT_ID + '_CueBallMarker'; cueBallMarker.style.position = 'absolute'; cueBallMarker.style.width = config.cueBallSize * 2 + 'px'; cueBallMarker.style.height = config.cueBallSize * 2 + 'px'; cueBallMarker.style.border = '3px solid red'; cueBallMarker.style.borderRadius = '50%'; cueBallMarker.style.backgroundColor = 'rgba(255,0,0,0.3)'; cueBallMarker.style.cursor = 'grab'; cueBallMarker.style.zIndex = 10001; document.body.appendChild(cueBallMarker); cueBallMarker.addEventListener('mousedown', e => { e.stopPropagation(); isDraggingCueBall = true; dragStartMouse = { x: e.clientX, y: e.clientY }; dragStartPos = { ...config.cueBallPos }; cueBallMarker.style.cursor = 'grabbing'; document.body.style.userSelect = 'none'; }); // Create calibration point markers (green squares) calibMarkers = []; for (let i = 0; i < 4; i++) { const marker = document.createElement('div'); marker.className = SCRIPT_ID + '_CalibMarker'; marker.style.position = 'absolute'; marker.style.width = config.calibPointSize + 'px'; marker.style.height = config.calibPointSize + 'px'; marker.style.backgroundColor = config.calibPointMarkerColor; marker.style.borderRadius = '6px'; marker.style.cursor = 'grab'; marker.style.zIndex = 10001; marker.dataset.index = i; document.body.appendChild(marker); marker.addEventListener('mousedown', e => { e.stopPropagation(); if (config.calibrationLocked) return; isDraggingCalibPoint = true; activeCalibIndex = parseInt(marker.dataset.index); dragStartMouse = { x: e.clientX, y: e.clientY }; dragStartPos = { ...config.calibPoints[activeCalibIndex] }; marker.style.cursor = 'grabbing'; document.body.style.userSelect = 'none'; }); calibMarkers.push(marker); } updateMarkersVisibility(); updateMarkersPosition(); } // Update visibility of calibration markers based on lock state function updateMarkersVisibility() { calibMarkers.forEach(marker => { marker.style.display = config.calibrationLocked ? 'none' : 'block'; marker.style.cursor = config.calibrationLocked ? 'default' : 'grab'; }); } // Update position of all markers relative to iframe function updateMarkersPosition() { if (!gameIframe) return; const rect = gameIframe.getBoundingClientRect(); // Position cue ball marker cueBallMarker.style.left = rect.left + config.cueBallPos.x - config.cueBallSize + 'px'; cueBallMarker.style.top = rect.top + config.cueBallPos.y - config.cueBallSize + 'px'; // Position calibration markers calibMarkers.forEach((marker, i) => { const p = config.calibPoints[i]; marker.style.left = rect.left + p.x - config.calibPointSize / 2 + 'px'; marker.style.top = rect.top + p.y - config.calibPointSize / 2 + 'px'; }); } // Update overlay canvas size and position to match iframe function updateOverlaySizeAndPosition() { if (!gameIframe || !overlayCanvas) return; const rect = gameIframe.getBoundingClientRect(); overlayCanvas.style.left = rect.left + 'px'; overlayCanvas.style.top = rect.top + 'px'; overlayCanvas.width = rect.width; overlayCanvas.height = rect.height; updateMarkersPosition(); requestDraw(); } // Request a redraw on next animation frame function requestDraw() { if (!drawRequested) { drawRequested = true; requestAnimationFrame(() => { draw(); drawRequested = false; }); } } // Draw guide lines and calibration grid function draw() { if (!ctx || !overlayCanvas) return; ctx.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height); // Draw calibration grid lines if unlocked if (!config.calibrationLocked && config.calibPoints.length === 4) { const [tl, tr, br, bl] = config.calibPoints; ctx.strokeStyle = config.guideLineColor; ctx.lineWidth = Math.max(1, config.lineWidth / 2); ctx.beginPath(); ctx.moveTo(tl.x, tl.y); ctx.lineTo(tr.x, tr.y); ctx.lineTo(br.x, br.y); ctx.lineTo(bl.x, bl.y); ctx.closePath(); ctx.moveTo(tl.x, tl.y); ctx.lineTo(br.x, br.y); ctx.moveTo(tr.x, tr.y); ctx.lineTo(bl.x, bl.y); ctx.stroke(); } // Draw lines from cue ball to pockets const pockets = calculatePockets(); ctx.strokeStyle = config.lineColor; ctx.lineWidth = config.lineWidth; pockets.forEach(pocket => { ctx.beginPath(); ctx.moveTo(config.cueBallPos.x, config.cueBallPos.y); ctx.lineTo(pocket.x, pocket.y); ctx.stroke(); }); } // Handle mouse move for dragging markers function onDocumentMouseMove(e) { if (!gameIframe) return; if (!isDraggingCueBall && !isDraggingCalibPoint && !isDraggingPanel) return; const rect = gameIframe.getBoundingClientRect(); const mouseXInIframe = e.clientX - rect.left; const mouseYInIframe = e.clientY - rect.top; if (isDraggingPanel) { // Handled in panel drag listeners return; } if (isDraggingCueBall) { const dx = mouseXInIframe - (dragStartMouse.x - rect.left); const dy = mouseYInIframe - (dragStartMouse.y - rect.top); config.cueBallPos.x = dragStartPos.x + dx; config.cueBallPos.y = dragStartPos.y + dy; clampCueBallPosition(); updateMarkersPosition(); requestDraw(); } else if (isDraggingCalibPoint && activeCalibIndex !== -1) { const dx = mouseXInIframe - (dragStartMouse.x - rect.left); const dy = mouseYInIframe - (dragStartMouse.y - rect.top); config.calibPoints[activeCalibIndex].x = dragStartPos.x + dx; config.calibPoints[activeCalibIndex].y = dragStartPos.y + dy; clampCalibrationPoint(activeCalibIndex); updateMarkersPosition(); requestDraw(); } } // Clamp cue ball position inside iframe bounds function clampCueBallPosition() { if (!gameIframe) return; const rect = gameIframe.getBoundingClientRect(); config.cueBallPos.x = Math.min(Math.max(config.cueBallPos.x, 0), rect.width); config.cueBallPos.y = Math.min(Math.max(config.cueBallPos.y, 0), rect.height); } // Clamp calibration points inside iframe bounds function clampCalibrationPoint(index) { if (!gameIframe) return; const rect = gameIframe.getBoundingClientRect(); let p = config.calibPoints[index]; p.x = Math.min(Math.max(p.x, 0), rect.width); p.y = Math.min(Math.max(p.y, 0), rect.height); } // Handle mouse up to stop dragging function onDocumentMouseUp(e) { if (isDraggingCueBall) { isDraggingCueBall = false; cueBallMarker.style.cursor = 'grab'; document.body.style.userSelect = ''; saveSettings(); } if (isDraggingCalibPoint) { isDraggingCalibPoint = false; if (activeCalibIndex !== -1) { calibMarkers[activeCalibIndex].style.cursor = 'grab'; } activeCalibIndex = -1; document.body.style.userSelect= ''; saveSettings(); } } // Initialize everything after iframe is ready function init() { console.log(SCRIPT_ID + ': Initializing...'); loadSettings(); gameIframe = getGameIframe(); if (!gameIframe) { console.warn(SCRIPT_ID + ': Game iframe not found.'); return; } createOverlayAndMarkers(); createSettingsPanel(); updateOverlaySizeAndPosition(); requestDraw(); window.addEventListener('resize', updateOverlaySizeAndPosition); window.addEventListener('scroll', updateOverlaySizeAndPosition); document.addEventListener('mousemove', onDocumentMouseMove); document.addEventListener('mouseup', onDocumentMouseUp); console.log(SCRIPT_ID + ': Initialization complete.'); } // Wait for iframe to be available and loaded function waitForIframeAndInit() { gameIframe = getGameIframe(); if (gameIframe && gameIframe.contentWindow) { // Wait for iframe to load fully if (gameIframe.contentDocument && gameIframe.contentDocument.readyState === 'complete') { init(); } else { gameIframe.addEventListener('load', () => { init(); }); } } else { console.log(SCRIPT_ID + ': Waiting for game iframe...'); setTimeout(waitForIframeAndInit, 500); } } waitForIframeAndInit(); })();