8 Ball Helper for Crazygames

This Helper is for 8 ball pool billiards.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

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

})();