CoolFace
Apppublic

Trojanizer/pocket-rocket-pool-party

sourceHugging Faceupdated 8mo agoView on Hugging Face
0likes
script.js497 linesDownload Raw Back to root
1// Game variables2let engine, world, render;3let cueBall, cue, balls = [], pockets = [];4let isAiming = false;5let power = 50;6let maxPower = 100;7let powerIncreasing = true;8let gameStarted = false;9let soundEnabled = true;10 11// DOM elements12const gameCanvas = document.getElementById('gameCanvas');13const powerMeter = document.getElementById('powerMeter');14const powerFill = document.getElementById('powerFill');15const newGameBtn = document.getElementById('newGameBtn');16const toggleGuideBtn = document.getElementById('toggleGuideBtn');17const toggleSoundBtn = document.getElementById('toggleSoundBtn');18const fullscreenBtn = document.getElementById('fullscreenBtn');19const guideModal = document.getElementById('guideModal');20const closeGuideBtn = document.getElementById('closeGuideBtn');21 22// Initialize the game23function initGame() {24    // Setup Matter.js25    engine = Matter.Engine.create();26    world = engine.world;27    28    render = Matter.Render.create({29        canvas: gameCanvas,30        engine: engine,31        options: {32            width: gameCanvas.clientWidth,33            height: gameCanvas.clientHeight,34            wireframes: false,35            background: '#1a202c',36            showAngleIndicator: false37        }38    });39    40    Matter.Render.run(render);41    42    // Create table boundaries43    createTable();44    45    // Create balls46    createBalls();47    48    // Create pockets49    createPockets();50    51    // Start the game loop52    Matter.Engine.run(engine);53    54    // Event listeners55    setupEventListeners();56    57    gameStarted = true;58}59 60function createTable() {61    const tableWidth = gameCanvas.clientWidth - 100;62    const tableHeight = gameCanvas.clientHeight - 100;63    const tableX = (gameCanvas.clientWidth - tableWidth) / 2;64    const tableY = (gameCanvas.clientHeight - tableHeight) / 2;65    66    // Table body (green surface)67    const table = Matter.Bodies.rectangle(68        gameCanvas.clientWidth / 2,69        gameCanvas.clientHeight / 2,70        tableWidth,71        tableHeight,72        {73            isStatic: true,74            render: {75                fillStyle: '#14532d',76                strokeStyle: '#14532d',77                lineWidth: 178            },79            chamfer: { radius: 20 }80        }81    );82    83    // Table borders (wooden rails)84    const borderOptions = {85        isStatic: true,86        render: { fillStyle: '#713f12' }87    };88    89    const topBorder = Matter.Bodies.rectangle(90        gameCanvas.clientWidth / 2,91        tableY - 15,92        tableWidth + 60,93        30,94        borderOptions95    );96    97    const bottomBorder = Matter.Bodies.rectangle(98        gameCanvas.clientWidth / 2,99        tableY + tableHeight + 15,100        tableWidth + 60,101        30,102        borderOptions103    );104    105    const leftBorder = Matter.Bodies.rectangle(106        tableX - 15,107        gameCanvas.clientHeight / 2,108        30,109        tableHeight + 60,110        borderOptions111    );112    113    const rightBorder = Matter.Bodies.rectangle(114        tableX + tableWidth + 15,115        gameCanvas.clientHeight / 2,116        30,117        tableHeight + 60,118        borderOptions119    );120    121    Matter.Composite.add(world, [table, topBorder, bottomBorder, leftBorder, rightBorder]);122}123 124function createBalls() {125    const ballRadius = 15;126    const startX = gameCanvas.clientWidth / 2 + 150;127    const startY = gameCanvas.clientHeight / 2;128    129    // Cue ball (white)130    cueBall = Matter.Bodies.circle(131        gameCanvas.clientWidth / 2 - 200,132        gameCanvas.clientHeight / 2,133        ballRadius,134        {135            restitution: 0.95,136            friction: 0.01,137            frictionAir: 0.02,138            render: {139                fillStyle: '#ffffff',140                strokeStyle: '#cccccc',141                lineWidth: 1142            },143            label: 'cue'144        }145    );146    147    // Create racked balls (triangle formation)148    const colors = [149        '#f59e0b', '#ef4444', '#3b82f6', '#8b5cf6', '#10b981', 150        '#ec4899', '#f97316', '#000000', '#64748b', '#84cc16',151        '#06b6d4', '#a855f7', '#d946ef', '#f43f5e', '#f97316'152    ];153    154    let row = 0;155    let ballIndex = 0;156    157    for (let i = 0; i < 5; i++) {158        for (let j = 0; j <= i; j++) {159            const x = startX + (j * ballRadius * 2) - (i * ballRadius);160            const y = startY - (i * ballRadius * 1.732) + (ballRadius * 1.732 * 2);161            162            const ball = Matter.Bodies.circle(163                x,164                y,165                ballRadius,166                {167                    restitution: 0.95,168                    friction: 0.01,169                    frictionAir: 0.02,170                    render: {171                        fillStyle: colors[ballIndex],172                        strokeStyle: '#333',173                        lineWidth: 1174                    },175                    label: 'ball_' + ballIndex176                }177            );178            179            balls.push(ball);180            ballIndex++;181        }182    }183    184    // Add all balls to the world185    Matter.Composite.add(world, [cueBall, ...balls]);186}187 188function createPockets() {189    const pocketRadius = 25;190    const tableWidth = gameCanvas.clientWidth - 100;191    const tableHeight = gameCanvas.clientHeight - 100;192    const tableX = (gameCanvas.clientWidth - tableWidth) / 2;193    const tableY = (gameCanvas.clientHeight - tableHeight) / 2;194    195    // Corner pockets196    const topLeftPocket = Matter.Bodies.circle(197        tableX - 5,198        tableY - 5,199        pocketRadius,200        {201            isStatic: true,202            isSensor: true,203            render: { fillStyle: '#000000' },204            label: 'pocket'205        }206    );207    208    const topRightPocket = Matter.Bodies.circle(209        tableX + tableWidth + 5,210        tableY - 5,211        pocketRadius,212        {213            isStatic: true,214            isSensor: true,215            render: { fillStyle: '#000000' },216            label: 'pocket'217        }218    );219    220    const bottomLeftPocket = Matter.Bodies.circle(221        tableX - 5,222        tableY + tableHeight + 5,223        pocketRadius,224        {225            isStatic: true,226            isSensor: true,227            render: { fillStyle: '#000000' },228            label: 'pocket'229        }230    );231    232    const bottomRightPocket = Matter.Bodies.circle(233        tableX + tableWidth + 5,234        tableY + tableHeight + 5,235        pocketRadius,236        {237            isStatic: true,238            isSensor: true,239            render: { fillStyle: '#000000' },240            label: 'pocket'241        }242    );243    244    // Side pockets245    const leftMiddlePocket = Matter.Bodies.circle(246        tableX - 5,247        gameCanvas.clientHeight / 2,248        pocketRadius,249        {250            isStatic: true,251            isSensor: true,252            render: { fillStyle: '#000000' },253            label: 'pocket'254        }255    );256    257    const rightMiddlePocket = Matter.Bodies.circle(258        tableX + tableWidth + 5,259        gameCanvas.clientHeight / 2,260        pocketRadius,261        {262            isStatic: true,263            isSensor: true,264            render: { fillStyle: '#000000' },265            label: 'pocket'266        }267    );268    269    pockets = [topLeftPocket, topRightPocket, bottomLeftPocket, bottomRightPocket, leftMiddlePocket, rightMiddlePocket];270    Matter.Composite.add(world, pockets);271}272 273function setupEventListeners() {274    // Mouse movement for aiming275    gameCanvas.addEventListener('mousemove', handleMouseMove);276    277    // Keyboard controls278    document.addEventListener('keydown', handleKeyDown);279    document.addEventListener('keyup', handleKeyUp);280    281    // UI buttons282    newGameBtn.addEventListener('click', resetGame);283    toggleGuideBtn.addEventListener('click', () => guideModal.classList.toggle('hidden'));284    closeGuideBtn.addEventListener('click', () => guideModal.classList.add('hidden'));285    toggleSoundBtn.addEventListener('click', toggleSound);286    fullscreenBtn.addEventListener('click', toggleFullscreen);287    288    // Collision detection289    Matter.Events.on(engine, 'collisionStart', handleCollision);290}291 292function handleMouseMove(e) {293    if (!gameStarted) return;294    295    const rect = gameCanvas.getBoundingClientRect();296    const mouseX = e.clientX - rect.left;297    const mouseY = e.clientY - rect.top;298    299    // Update cue position based on mouse300    const cueBallPos = cueBall.position;301    const angle = Math.atan2(mouseY - cueBallPos.y, mouseX - cueBallPos.x);302    303    // Draw cue304    drawCue(angle);305}306 307function drawCue(angle) {308    // This would be implemented with canvas drawing309    // For simplicity, we'll just update a visual cue element310    const cueElement = document.querySelector('.cue');311    if (!cueElement) {312        const cue = document.createElement('div');313        cue.className = 'cue';314        document.body.appendChild(cue);315    }316    317    const cueBallPos = cueBall.position;318    const cueLength = 200;319    const endX = cueBallPos.x - Math.cos(angle) * cueLength;320    const endY = cueBallPos.y - Math.sin(angle) * cueLength;321    322    cueElement.style.left = `${endX}px`;323    cueElement.style.top = `${endY}px`;324    cueElement.style.transform = `rotate(${angle}rad)`;325}326 327function handleKeyDown(e) {328    if (!gameStarted) return;329    330    switch (e.key) {331        case ' ':332            // Shoot the ball333            shootCueBall();334            break;335        case 'ArrowUp':336            // Increase power337            powerMeter.classList.remove('hidden');338            powerIncreasing = true;339            updatePowerMeter();340            break;341        case 'ArrowDown':342            // Decrease power343            powerMeter.classList.remove('hidden');344            powerIncreasing = false;345            updatePowerMeter();346            break;347        case 'r':348        case 'R':349            // Reset cue ball position350            resetCueBall();351            break;352    }353}354 355function handleKeyUp(e) {356    if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {357        powerMeter.classList.add('hidden');358    }359}360 361function updatePowerMeter() {362    if (powerIncreasing) {363        power = Math.min(power + 2, maxPower);364    } else {365        power = Math.max(power - 2, 0);366    }367    368    powerFill.style.width = `${power}%`;369    370    // Continue updating if keys are held371    if (power > 0 && power < maxPower) {372        requestAnimationFrame(updatePowerMeter);373    }374}375 376function shootCueBall() {377    if (!gameStarted || cueBall.speed > 0.1) return;378    379    const cueElement = document.querySelector('.cue');380    const angle = parseFloat(cueElement.style.transform.replace('rotate(', '').replace('rad)', ''));381    382    const forceMagnitude = power * 0.01;383    const force = {384        x: Math.cos(angle) * forceMagnitude * -1,385        y: Math.sin(angle) * forceMagnitude * -1386    };387    388    Matter.Body.applyForce(cueBall, cueBall.position, force);389    390    // Play sound391    if (soundEnabled) {392        playSound('hit');393    }394    395    // Reset power396    power = 50;397    powerFill.style.width = '50%';398}399 400function resetCueBall() {401    if (!gameStarted || cueBall.speed > 0.1) return;402    403    Matter.Body.setPosition(cueBall, {404        x: gameCanvas.clientWidth / 2 - 200,405        y: gameCanvas.clientHeight / 2406    });407    Matter.Body.setVelocity(cueBall, { x: 0, y: 0 });408    Matter.Body.setAngularVelocity(cueBall, 0);409}410 411function handleCollision(event) {412    const pairs = event.pairs;413    414    for (let i = 0; i < pairs.length; i++) {415        const pair = pairs[i];416        417        // Check if a ball fell into a pocket418        if (pair.bodyA.label.includes('ball') && pair.bodyB.label === 'pocket') {419            handleBallPocketed(pair.bodyA);420        } else if (pair.bodyB.label.includes('ball') && pair.bodyA.label === 'pocket') {421            handleBallPocketed(pair.bodyB);422        }423        424        // Play collision sound425        if ((pair.bodyA.label.includes('ball') || pair.bodyA.label === 'cue') && 426            (pair.bodyB.label.includes('ball') || pair.bodyB.label === 'cue') && 427            soundEnabled) {428            playSound('collision');429        }430    }431}432 433function handleBallPocketed(ball) {434    // Remove the ball from the world435    Matter.Composite.remove(world, ball);436    437    // Play sound438    if (soundEnabled) {439        playSound('pocket');440    }441    442    // Check if cue ball was pocketed443    if (ball.label === 'cue') {444        setTimeout(resetCueBall, 1000);445    }446}447 448function playSound(type) {449    // In a real implementation, we would play actual sounds450    console.log(`Playing ${type} sound`);451}452 453function toggleSound() {454    soundEnabled = !soundEnabled;455    toggleSoundBtn.innerHTML = soundEnabled ? 456        '<i data-feather="volume-2" class="mr-2"></i> Sound' : 457        '<i data-feather="volume-x" class="mr-2"></i> Sound';458    feather.replace();459}460 461function toggleFullscreen() {462    if (!document.fullscreenElement) {463        gameCanvas.requestFullscreen().catch(err => {464            console.error(`Error attempting to enable fullscreen: ${err.message}`);465        });466    } else {467        document.exitFullscreen();468    }469}470 471function resetGame() {472    // Clear the world473    Matter.Composite.clear(world, false);474    475    // Reset game state476    gameStarted = false;477    power = 50;478    479    // Recreate game elements480    createTable();481    createBalls();482    createPockets();483    484    gameStarted = true;485}486 487// Initialize the game when the page loads488window.addEventListener('load', () => {489    initGame();490    491    // Handle window resize492    window.addEventListener('resize', () => {493        render.options.width = gameCanvas.clientWidth;494        render.options.height = gameCanvas.clientHeight;495        Matter.Render.setPixelRatio(render, window.devicePixelRatio);496    });497});