您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
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); })();