8 Ball Helper for Crazygames

This Helper is for 8 ball pool billiards.

// ==UserScript==
// @name         8 Ball Helper for Crazygames
// @namespace    http://tampermonkey.net/
// @version      1.9.3
// @description  This Helper is for 8 ball pool billiards.
// @author       Made by Kakoncheater
// @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_v1_9_1';

    let config = {
        calibPoints: [ { x: 100, y: 100 }, { x: 700, y: 100 }, { x: 700, y: 400 }, { x: 100, y: 400 } ],
        cueBallPos: { x: 250, y: 250 },
        lineWidth: 2,
        lineColor: '#FFFFFF',
        guideLineColor: 'rgba(200, 200, 200, 0.6)',
        cueBallMarkerBorderColor: 'rgba(255, 0, 0, 0.8)', // Farbe für den Rand des Cue-Markers
        calibPointMarkerColor: 'rgba(0, 255, 0, 0.4)',
        calibPointSize: 40,
        cueBallSize: 20, // Radius
        cueBallMarkerBorderSize: 2, // Dicke des Rands für den Cue-Marker
        showGridLines: true,
        calibrationLocked: false,
        settingsPanelVisible: true,
        settingsPanelPos: { top: '10px', left: '10px' }
    };

    let gameIframe;
    let overlayCanvas, ctx;
    let settingsPanel;
    let cueBallMarkerElement;
    let calibPointMarkerElements = [];

    let isDraggingCueBall = false;
    let activeCalibPointIndex = -1;
    let isDraggingCalibPoint = false;
    let isDraggingPanel = false;
    let dragStartMousePos = { x: 0, y: 0 };
    let dragStartElementPos = { x: 0, y: 0 };
    let calculatedPockets = [];
    let drawRequested = false;

    function calculatePocketPositions() { /* ... (same as v1.9) ... */ calculatedPockets = []; if (config.calibPoints.length !== 4) return; const [tl, tr, br, bl] = config.calibPoints; calculatedPockets.push({ x: tl.x, y: tl.y, name: 'TL' }); calculatedPockets.push({ x: tr.x, y: tr.y, name: 'TR' }); calculatedPockets.push({ x: br.x, y: br.y, name: 'BR' }); calculatedPockets.push({ x: bl.x, y: bl.y, name: 'BL' }); const topCushionY = (tl.y + tr.y) / 2; const bottomCushionY = (bl.y + br.y) / 2; calculatedPockets.push({ x: (tl.x + tr.x) / 2, y: topCushionY, name: 'TM' }); calculatedPockets.push({ x: (bl.x + br.x) / 2, y: bottomCushionY, name: 'BM' }); }
    function saveSettings() { GM_setValue(SCRIPT_ID + '_settings', JSON.stringify(config)); }
    function loadSettings() { const saved = GM_getValue(SCRIPT_ID + '_settings'); if (saved) { const loadedConfig = JSON.parse(saved); for (const key in config) { if (loadedConfig[key] !== undefined) { config[key] = loadedConfig[key]; } } } console.log("Settings loaded, panel visible:", config.settingsPanelVisible); }
    function getGameIframe() { return document.getElementById('game-iframe'); }

    function createSettingsPanel() { /* ... (same as v1.9) ... */
        console.log("Attempting to create settings panel. Initial visibility:", config.settingsPanelVisible);
        settingsPanel = document.createElement('div');
        settingsPanel.id = SCRIPT_ID + '_SettingsPanel';
        settingsPanel.style.display = config.settingsPanelVisible ? 'block' : 'none';
        settingsPanel.innerHTML = `
            <div id="${SCRIPT_ID}_PanelHeader" style="background-color:#333; color:white; padding:5px; cursor:grab; text-align:center;">8 Ball Guide (Toggle: Insert)</div>
            <div style="padding:10px;">
                <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}_showGridLines" ${config.showGridLines ? 'checked' : ''}> Show Grid Lines</label>
                <label><input type="checkbox" id="${SCRIPT_ID}_calibrationLocked" ${config.calibrationLocked ? 'checked' : ''}> Lock Calibration</label>
                <button id="${SCRIPT_ID}_saveSettings" style="width:100%; margin-top:10px;">Save All Settings</button>
            </div>`;
        document.body.appendChild(settingsPanel);
        GM_addStyle( `
            #${SCRIPT_ID}_SettingsPanel { position: fixed; top: ${config.settingsPanelPos.top}; left: ${config.settingsPanelPos.left}; background-color: rgba(70, 70, 70, 0.95); border: 1px solid #888; border-radius: 5px; z-index: 10002; color: white; font-family: sans-serif; font-size: 14px; min-width: 230px; }
            #${SCRIPT_ID}_SettingsPanel label { display: block; margin: 5px 0; }
            #${SCRIPT_ID}_SettingsPanel input[type="checkbox"] { margin-right: 5px; vertical-align: middle; }
            #${SCRIPT_ID}_SettingsPanel button { padding: 8px; background-color: #555; color: white; border: none; border-radius: 3px; cursor: pointer; }
            #${SCRIPT_ID}_SettingsPanel button:hover { background-color: #666; }
            body.dragging-active, body.dragging-active * { user-select: none !important; -webkit-user-select: none !important; -moz-user-select: none !important; -ms-user-select: none !important; }
        `);
        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}_showGridLines`).addEventListener('change', (e) => { config.showGridLines = e.target.checked; requestDraw(); });
        document.getElementById(`${SCRIPT_ID}_calibrationLocked`).addEventListener('change', (e) => { config.calibrationLocked = e.target.checked; updateDraggableMarkersVisibilityAndStyle(); requestDraw(); });
        document.getElementById(`${SCRIPT_ID}_saveSettings`).addEventListener('click', () => { saveSettings(); alert('Settings Saved!'); });
        const panelHeader = document.getElementById(`${SCRIPT_ID}_PanelHeader`);
        panelHeader.addEventListener('mousedown', (e) => { isDraggingPanel = true; dragStartMousePos = { x: e.clientX, y: e.clientY }; dragStartElementPos = { x: settingsPanel.offsetLeft, y: settingsPanel.offsetTop }; panelHeader.style.cursor = 'grabbing'; document.body.classList.add('dragging-active'); if (overlayCanvas) overlayCanvas.style.pointerEvents = 'none !important'; });
        window.addEventListener('keydown', (e) => { if (e.key === 'Insert') { config.settingsPanelVisible = !config.settingsPanelVisible; if (settingsPanel) { settingsPanel.style.display = config.settingsPanelVisible ? 'block' : 'none'; } saveSettings(); } });
        console.log("Settings panel created and listeners attached.");
    }

    function createOverlayCanvasAndMarkers() {
        overlayCanvas = document.createElement('canvas');
        overlayCanvas.id = SCRIPT_ID + '_Overlay';
        ctx = overlayCanvas.getContext('2d');
        overlayCanvas.style.pointerEvents = 'none !important';
        document.body.appendChild(overlayCanvas);
        GM_addStyle(`
            #${SCRIPT_ID}_Overlay { position: absolute; top: 0; left: 0; z-index: 10000; pointer-events: none !important; }
            .${SCRIPT_ID}_Marker { position: absolute; z-index: 10001; cursor: grab; box-sizing: border-box; /* border: 1px dashed rgba(255,255,255,0.5); */ }
        `);
        cueBallMarkerElement = document.createElement('div');
        cueBallMarkerElement.id = SCRIPT_ID + '_CueBallMarker';
        cueBallMarkerElement.className = `${SCRIPT_ID}_Marker`;
        cueBallMarkerElement.style.width = (config.cueBallSize * 2) + 'px';
        cueBallMarkerElement.style.height = (config.cueBallSize * 2) + 'px';
        cueBallMarkerElement.style.backgroundColor = 'transparent'; // Transparent machen
        cueBallMarkerElement.style.border = `${config.cueBallMarkerBorderSize}px solid ${config.cueBallMarkerBorderColor}`; // Rand hinzufügen
        cueBallMarkerElement.style.borderRadius = '50%';
        cueBallMarkerElement.addEventListener('mousedown', (e) => { e.stopPropagation(); isDraggingCueBall = true; dragStartMousePos = { x: e.clientX, y: e.clientY }; dragStartElementPos = { ...config.cueBallPos }; cueBallMarkerElement.style.cursor = 'grabbing'; document.body.classList.add('dragging-active'); if (overlayCanvas) overlayCanvas.style.pointerEvents = 'none !important'; });
        document.body.appendChild(cueBallMarkerElement);

        calibPointMarkerElements = [];
        for (let i = 0; i < 4; i++) {
            const marker = document.createElement('div');
            marker.id = `${SCRIPT_ID}_CalibMarker_${i}`;
            marker.className = `${SCRIPT_ID}_Marker`;
            marker.style.width = config.calibPointSize + 'px';
            marker.style.height = config.calibPointSize + 'px';
            marker.style.backgroundColor = config.calibPointMarkerColor;
            marker.dataset.index = i;
            marker.addEventListener('mousedown', (e) => { e.stopPropagation(); if (config.calibrationLocked) return; isDraggingCalibPoint = true; activeCalibPointIndex = parseInt(e.target.dataset.index); dragStartMousePos = { x: e.clientX, y: e.clientY }; dragStartElementPos = { ...config.calibPoints[activeCalibPointIndex] }; marker.style.cursor = 'grabbing'; document.body.classList.add('dragging-active'); if (overlayCanvas) overlayCanvas.style.pointerEvents = 'none !important'; });
            calibPointMarkerElements.push(marker);
            document.body.appendChild(marker);
        }
        updateDraggableMarkersVisibilityAndStyle();
        updateDraggableMarkersPosition();
    }
    function updateDraggableMarkersVisibilityAndStyle() { calibPointMarkerElements.forEach(m => { m.style.display = config.calibrationLocked ? 'none' : 'block'; m.style.cursor = config.calibrationLocked ? 'default' : 'grab'; }); }
    function updateDraggableMarkersPosition() { if (!gameIframe) return; const iframeRect = gameIframe.getBoundingClientRect(); if (cueBallMarkerElement) { cueBallMarkerElement.style.left = (iframeRect.left + config.cueBallPos.x - config.cueBallSize) + 'px'; cueBallMarkerElement.style.top = (iframeRect.top + config.cueBallPos.y - config.cueBallSize) + 'px'; } calibPointMarkerElements.forEach((marker, i) => { if (config.calibPoints[i]) { marker.style.left = (iframeRect.left + config.calibPoints[i].x - config.calibPointSize / 2) + 'px'; marker.style.top = (iframeRect.top + config.calibPoints[i].y - config.calibPointSize / 2) + 'px'; } }); }

    function handleDocumentMouseMove(e) { /* ... (same as v1.8 / v1.7) ... */
        if (!gameIframe) return;
        if (!isDraggingPanel && !isDraggingCueBall && !isDraggingCalibPoint) return;
        const iframeRect = gameIframe.getBoundingClientRect();
        if (isDraggingPanel) { const dx = e.clientX - dragStartMousePos.x; const dy = e.clientY - dragStartMousePos.y; settingsPanel.style.left = (dragStartElementPos.x + dx) + 'px'; settingsPanel.style.top = (dragStartElementPos.y + dy) + 'px'; return; }
        const mouseXOnIframe = e.clientX - iframeRect.left;
        const mouseYOnIframe = e.clientY - iframeRect.top;
        if (isDraggingCueBall) { const dx = mouseXOnIframe - (dragStartMousePos.x - iframeRect.left); const dy = mouseYOnIframe - (dragStartMousePos.y - iframeRect.top); config.cueBallPos.x = dragStartElementPos.x + dx; config.cueBallPos.y = dragStartElementPos.y + dy; updateDraggableMarkersPosition(); requestDraw();
        } else if (isDraggingCalibPoint && activeCalibPointIndex !== -1) { const dx = mouseXOnIframe - (dragStartMousePos.x - iframeRect.left); const dy = mouseYOnIframe - (dragStartMousePos.y - iframeRect.top); config.calibPoints[activeCalibPointIndex].x = dragStartElementPos.x + dx; config.calibPoints[activeCalibPointIndex].y = dragStartElementPos.y + dy; calculatePocketPositions(); updateDraggableMarkersPosition(); requestDraw(); }
    }
    function handleDocumentMouseUp(e) { /* ... (same as v1.8, mit Logging) ... */
        console.log("MouseUp event triggered. Target:", e.target ? e.target.id || e.target.tagName : 'N/A');
        console.log("Mouse buttons state:", e.buttons);
        console.log("Current dragging states before reset: CueBall:", isDraggingCueBall, "CalibPoint:", isDraggingCalibPoint, "Panel:", isDraggingPanel);
        document.body.classList.remove('dragging-active');
        if (overlayCanvas) { overlayCanvas.style.pointerEvents = 'none'; } // Bleibt 'none'
        let wasDraggingSomething = isDraggingCueBall || isDraggingCalibPoint || isDraggingPanel;
        if (isDraggingCueBall) { console.log("Releasing CueBall drag."); isDraggingCueBall = false; }
        if (isDraggingCalibPoint) { console.log("Releasing CalibPoint drag for index:", activeCalibPointIndex); isDraggingCalibPoint = false; }
        if (isDraggingPanel) { console.log("Releasing Panel drag."); isDraggingPanel = false; if (settingsPanel) { config.settingsPanelPos = { top: settingsPanel.style.top, left: settingsPanel.style.left }; saveSettings(); } }
        if (wasDraggingSomething) { requestDraw(); }
        if(cueBallMarkerElement) cueBallMarkerElement.style.cursor = 'grab';
        calibPointMarkerElements.forEach(m => m.style.cursor = 'grab');
        const panelHeader = document.getElementById(`${SCRIPT_ID}_PanelHeader`);
        if(panelHeader) panelHeader.style.cursor = 'grab';
    }

    function requestDraw() { if (!drawRequested) { drawRequested = true; requestAnimationFrame(() => { draw(); drawRequested = false; }); } }
    function draw() { /* ... (same as v1.8 / v1.7, ohne Angle Line) ... */ if (!ctx || !overlayCanvas || !gameIframe) return; ctx.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height); if (config.showGridLines && 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(); } ctx.strokeStyle = config.lineColor; ctx.lineWidth = config.lineWidth; if (calculatedPockets.length > 0) { calculatedPockets.forEach(pocket => { ctx.beginPath(); ctx.moveTo(config.cueBallPos.x, config.cueBallPos.y); ctx.lineTo(pocket.x, pocket.y); ctx.stroke(); }); } }
    function updateOverlaySizeAndPosition() { /* ... (same as v1.8 / v1.7) ... */ if (!gameIframe || !overlayCanvas) { if (overlayCanvas) overlayCanvas.style.display = 'none'; if(cueBallMarkerElement) cueBallMarkerElement.style.display = 'none'; calibPointMarkerElements.forEach(m => m.style.display = 'none'); return; } overlayCanvas.style.display = 'block'; if(cueBallMarkerElement) cueBallMarkerElement.style.display = 'block'; updateDraggableMarkersVisibilityAndStyle(); const rect = gameIframe.getBoundingClientRect(); overlayCanvas.style.left = rect.left + 'px'; overlayCanvas.style.top = rect.top + 'px'; overlayCanvas.width = rect.width; overlayCanvas.height = rect.height; updateDraggableMarkersPosition(); calculatePocketPositions(); requestDraw(); }
    function init() { /* ... (same as v1.8 / v1.7) ... */ console.log("Initializing script", SCRIPT_ID); loadSettings(); gameIframe = getGameIframe(); if (!gameIframe) { console.log(SCRIPT_ID + ": Game Iframe not found. Retrying..."); setTimeout(init, 2000); return; } console.log(SCRIPT_ID + ": Game Iframe found."); createOverlayCanvasAndMarkers(); createSettingsPanel(); calculatePocketPositions(); updateOverlaySizeAndPosition(); const resizeObserver = new ResizeObserver(updateOverlaySizeAndPosition); resizeObserver.observe(gameIframe); window.addEventListener('resize', updateOverlaySizeAndPosition); document.addEventListener('fullscreenchange', () => { setTimeout(updateOverlaySizeAndPosition, 150); }); document.addEventListener('mousemove', handleDocumentMouseMove); document.addEventListener('mouseup', handleDocumentMouseUp); requestDraw(); console.log("Initialization complete."); }
    const checkInterval = setInterval(() => { /* ... (same as v1.8 / v1.7) ... */ gameIframe = getGameIframe(); if (gameIframe && typeof gameIframe.getBoundingClientRect === 'function') { clearInterval(checkInterval); init(); } else if (document.readyState === "complete") { gameIframe = getGameIframe(); if (gameIframe && typeof gameIframe.getBoundingClientRect === 'function') { clearInterval(checkInterval); init(); } else { console.log(SCRIPT_ID + ": Game Iframe not definitively found after page load."); } } }, 500);

})();