Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Pinball Game</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| @keyframes bump { | |
| 0% { transform: scale(1); } | |
| 50% { transform: scale(1.2); } | |
| 100% { transform: scale(1); } | |
| } | |
| @keyframes flash { | |
| 0% { opacity: 1; } | |
| 50% { opacity: 0.5; } | |
| 100% { opacity: 1; } | |
| } | |
| .bump-animation { | |
| animation: bump 0.2s ease; | |
| } | |
| .flash-animation { | |
| animation: flash 0.3s ease; | |
| } | |
| #game-container { | |
| perspective: 1000px; | |
| } | |
| #pinball-table { | |
| transform-style: preserve-3d; | |
| transform: rotateX(10deg); | |
| box-shadow: 0 20px 30px rgba(0, 0, 0, 0.3); | |
| } | |
| #ball { | |
| transition: transform 0.05s linear; | |
| } | |
| .flipper { | |
| transform-origin: left center; | |
| transition: transform 0.1s ease; | |
| } | |
| .flipper.active { | |
| transform: rotate(-30deg); | |
| } | |
| .bumper { | |
| transition: transform 0.2s ease; | |
| } | |
| .bumper.hit { | |
| transform: scale(1.1); | |
| } | |
| @media (max-width: 768px) { | |
| #pinball-table { | |
| transform: rotateX(15deg); | |
| } | |
| .controls { | |
| flex-direction: column; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-900 text-white min-h-screen flex flex-col items-center justify-center p-4"> | |
| <div class="text-center mb-4"> | |
| <h1 class="text-4xl font-bold text-yellow-400 mb-2">Cosmic Pinball</h1> | |
| <div class="flex justify-center gap-8 mb-4"> | |
| <div class="bg-gray-800 px-4 py-2 rounded-lg"> | |
| <span class="text-yellow-400">Score:</span> <span id="score" class="text-xl font-mono">0</span> | |
| </div> | |
| <div class="bg-gray-800 px-4 py-2 rounded-lg"> | |
| <span class="text-yellow-400">Balls:</span> <span id="balls" class="text-xl font-mono">3</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="game-container" class="relative mb-8"> | |
| <div id="pinball-table" class="relative bg-blue-900 border-4 border-yellow-600 rounded-lg overflow-hidden" style="width: 600px; height: 800px;"> | |
| <!-- Ball --> | |
| <div id="ball" class="absolute w-6 h-6 bg-white rounded-full shadow-lg z-10"></div> | |
| <!-- Flippers --> | |
| <div class="absolute bottom-16 left-32 w-32 h-6 bg-red-600 rounded-lg flipper" id="left-flipper"></div> | |
| <div class="absolute bottom-16 right-32 w-32 h-6 bg-red-600 rounded-lg flipper" id="right-flipper" style="transform-origin: right center;"></div> | |
| <!-- Bumpers --> | |
| <div class="absolute top-40 left-40 w-16 h-16 bg-purple-600 rounded-full bumper" id="bumper1"></div> | |
| <div class="absolute top-40 right-40 w-16 h-16 bg-purple-600 rounded-full bumper" id="bumper2"></div> | |
| <div class="absolute top-60 left-1/2 transform -translate-x-1/2 w-16 h-16 bg-purple-600 rounded-full bumper" id="bumper3"></div> | |
| <!-- Targets --> | |
| <div class="absolute top-20 left-20 w-8 h-20 bg-green-500 rounded target" id="target1"></div> | |
| <div class="absolute top-20 right-20 w-8 h-20 bg-green-500 rounded target" id="target2"></div> | |
| <!-- Slingshots --> | |
| <div class="absolute bottom-40 left-10 w-4 h-20 bg-orange-500 rounded slingshot" id="left-slingshot"></div> | |
| <div class="absolute bottom-40 right-10 w-4 h-20 bg-orange-500 rounded slingshot" id="right-slingshot"></div> | |
| <!-- Plunger area --> | |
| <div class="absolute bottom-4 left-1/2 transform -translate-x-1/2 w-24 h-8 bg-yellow-600 rounded"></div> | |
| <!-- Side walls --> | |
| <div class="absolute top-0 left-0 w-full h-8 bg-yellow-600"></div> | |
| <div class="absolute top-0 left-0 w-8 h-full bg-yellow-600"></div> | |
| <div class="absolute top-0 right-0 w-8 h-full bg-yellow-600"></div> | |
| <!-- Decorations --> | |
| <div class="absolute top-10 left-1/2 transform -translate-x-1/2 text-yellow-400 font-bold text-xl">COSMIC PINBALL</div> | |
| <div class="absolute top-80 left-1/2 transform -translate-x-1/2 text-yellow-400 font-bold text-lg">EXTRA BALL</div> | |
| <div class="absolute bottom-24 left-1/2 transform -translate-x-1/2 w-32 h-2 bg-yellow-400 rounded-full"></div> | |
| </div> | |
| </div> | |
| <div class="controls flex gap-8 mb-8"> | |
| <button id="start-btn" class="bg-green-600 hover:bg-green-700 px-6 py-3 rounded-lg font-bold transition">START GAME</button> | |
| <button id="left-btn" class="bg-red-600 hover:bg-red-700 px-6 py-3 rounded-lg font-bold transition">LEFT FLIPPER (A)</button> | |
| <button id="right-btn" class="bg-red-600 hover:bg-red-700 px-6 py-3 rounded-lg font-bold transition">RIGHT FLIPPER (L)</button> | |
| <button id="plunger-btn" class="bg-blue-600 hover:bg-blue-700 px-6 py-3 rounded-lg font-bold transition">PLUNGER (SPACE)</button> | |
| </div> | |
| <div class="text-gray-400 text-sm text-center max-w-md"> | |
| <p>Use the buttons or keyboard (A for left flipper, L for right flipper, SPACE for plunger)</p> | |
| <p class="mt-2">Hit bumpers and targets to score points. Don't let the ball fall!</p> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // Game elements | |
| const ball = document.getElementById('ball'); | |
| const leftFlipper = document.getElementById('left-flipper'); | |
| const rightFlipper = document.getElementById('right-flipper'); | |
| const bumpers = document.querySelectorAll('.bumper'); | |
| const targets = document.querySelectorAll('.target'); | |
| const slingshots = document.querySelectorAll('.slingshot'); | |
| const scoreDisplay = document.getElementById('score'); | |
| const ballsDisplay = document.getElementById('balls'); | |
| const startBtn = document.getElementById('start-btn'); | |
| const leftBtn = document.getElementById('left-btn'); | |
| const rightBtn = document.getElementById('right-btn'); | |
| const plungerBtn = document.getElementById('plunger-btn'); | |
| // Game state | |
| let score = 0; | |
| let balls = 3; | |
| let ballX = 300; | |
| let ballY = 750; | |
| let ballSpeedX = 0; | |
| let ballSpeedY = 0; | |
| let gravity = 0.2; | |
| let friction = 0.99; | |
| let gameActive = false; | |
| let gameInterval; | |
| let leftFlipperActive = false; | |
| let rightFlipperActive = false; | |
| // Initialize game | |
| function initGame() { | |
| resetBall(); | |
| score = 0; | |
| balls = 3; | |
| updateDisplay(); | |
| gameActive = true; | |
| if (gameInterval) clearInterval(gameInterval); | |
| gameInterval = setInterval(updateGame, 16); | |
| } | |
| // Reset ball to plunger position | |
| function resetBall() { | |
| ballX = 300; | |
| ballY = 750; | |
| ballSpeedX = 0; | |
| ballSpeedY = 0; | |
| updateBallPosition(); | |
| } | |
| // Launch ball | |
| function launchBall() { | |
| if (ballY >= 750 && !gameActive) { | |
| ballSpeedY = -15 + Math.random() * 5; | |
| ballSpeedX = (Math.random() - 0.5) * 5; | |
| gameActive = true; | |
| } | |
| } | |
| // Update game state | |
| function updateGame() { | |
| if (!gameActive) return; | |
| // Apply gravity | |
| ballSpeedY += gravity; | |
| // Apply friction | |
| ballSpeedX *= friction; | |
| ballSpeedY *= friction; | |
| // Update position | |
| ballX += ballSpeedX; | |
| ballY += ballSpeedY; | |
| // Wall collisions | |
| if (ballX <= 8) { | |
| ballX = 8; | |
| ballSpeedX = -ballSpeedX * 0.8; | |
| playSound('wall'); | |
| } | |
| if (ballX >= 592) { | |
| ballX = 592; | |
| ballSpeedX = -ballSpeedX * 0.8; | |
| playSound('wall'); | |
| } | |
| if (ballY <= 8) { | |
| ballY = 8; | |
| ballSpeedY = -ballSpeedY * 0.8; | |
| playSound('wall'); | |
| } | |
| // Bottom out | |
| if (ballY >= 792) { | |
| balls--; | |
| updateDisplay(); | |
| if (balls <= 0) { | |
| gameActive = false; | |
| setTimeout(() => alert(`Game Over! Final Score: ${score}`), 100); | |
| } else { | |
| setTimeout(() => { | |
| resetBall(); | |
| gameActive = false; | |
| }, 500); | |
| } | |
| playSound('lose'); | |
| } | |
| // Flipper collisions | |
| checkFlipperCollision(leftFlipper, true); | |
| checkFlipperCollision(rightFlipper, false); | |
| // Bumper collisions | |
| bumpers.forEach(bumper => { | |
| const rect = bumper.getBoundingClientRect(); | |
| const tableRect = document.getElementById('pinball-table').getBoundingClientRect(); | |
| const bumperX = rect.left - tableRect.left + rect.width / 2; | |
| const bumperY = rect.top - tableRect.top + rect.height / 2; | |
| const bumperRadius = rect.width / 2; | |
| const dx = ballX - bumperX; | |
| const dy = ballY - bumperY; | |
| const distance = Math.sqrt(dx * dx + dy * dy); | |
| if (distance < bumperRadius + 12) { | |
| // Collision detected | |
| const angle = Math.atan2(dy, dx); | |
| const force = 10; | |
| ballSpeedX = Math.cos(angle) * force; | |
| ballSpeedY = Math.sin(angle) * force; | |
| // Visual feedback | |
| bumper.classList.add('hit', 'flash-animation'); | |
| setTimeout(() => { | |
| bumper.classList.remove('hit', 'flash-animation'); | |
| }, 300); | |
| // Score | |
| addScore(100); | |
| playSound('bumper'); | |
| } | |
| }); | |
| // Target collisions | |
| targets.forEach(target => { | |
| const rect = target.getBoundingClientRect(); | |
| const tableRect = document.getElementById('pinball-table').getBoundingClientRect(); | |
| const targetX = rect.left - tableRect.left; | |
| const targetY = rect.top - tableRect.top; | |
| const targetWidth = rect.width; | |
| const targetHeight = rect.height; | |
| if (ballX > targetX && ballX < targetX + targetWidth && | |
| ballY > targetY && ballY < targetY + targetHeight) { | |
| // Collision detected | |
| if (ballSpeedY < 0) ballSpeedY = -ballSpeedY * 1.2; | |
| else ballSpeedY = Math.abs(ballSpeedY) * 1.2; | |
| ballSpeedX += (Math.random() - 0.5) * 5; | |
| // Visual feedback | |
| target.classList.add('bump-animation'); | |
| setTimeout(() => { | |
| target.classList.remove('bump-animation'); | |
| }, 200); | |
| // Score | |
| addScore(200); | |
| playSound('target'); | |
| } | |
| }); | |
| // Slingshot collisions | |
| slingshots.forEach(slingshot => { | |
| const rect = slingshot.getBoundingClientRect(); | |
| const tableRect = document.getElementById('pinball-table').getBoundingClientRect(); | |
| const slingX = rect.left - tableRect.left; | |
| const slingY = rect.top - tableRect.top; | |
| const slingWidth = rect.width; | |
| const slingHeight = rect.height; | |
| if (ballX > slingX && ballX < slingX + slingWidth && | |
| ballY > slingY && ballY < slingY + slingHeight) { | |
| // Determine which side | |
| const isLeft = slingshot.id === 'left-slingshot'; | |
| // Apply force | |
| ballSpeedX = isLeft ? 8 : -8; | |
| ballSpeedY = -5; | |
| // Visual feedback | |
| slingshot.classList.add('bump-animation'); | |
| setTimeout(() => { | |
| slingshot.classList.remove('bump-animation'); | |
| }, 200); | |
| // Score | |
| addScore(50); | |
| playSound('slingshot'); | |
| } | |
| }); | |
| // Update ball position | |
| updateBallPosition(); | |
| } | |
| // Check flipper collision | |
| function checkFlipperCollision(flipper, isLeft) { | |
| if (!(leftFlipperActive && isLeft) && !(rightFlipperActive && !isLeft)) return; | |
| const rect = flipper.getBoundingClientRect(); | |
| const tableRect = document.getElementById('pinball-table').getBoundingClientRect(); | |
| const flipperX = rect.left - tableRect.left; | |
| const flipperY = rect.top - tableRect.top; | |
| const flipperWidth = rect.width; | |
| const flipperHeight = rect.height; | |
| // Simple rectangular collision for flippers | |
| if (ballX > flipperX && ballX < flipperX + flipperWidth && | |
| ballY > flipperY && ballY < flipperY + flipperHeight) { | |
| // Apply force based on flipper direction | |
| const force = isLeft ? -8 : 8; | |
| ballSpeedX = force; | |
| ballSpeedY = -10; | |
| // Score | |
| addScore(25); | |
| playSound('flipper'); | |
| } | |
| } | |
| // Update ball position on screen | |
| function updateBallPosition() { | |
| ball.style.left = `${ballX - 12}px`; | |
| ball.style.top = `${ballY - 12}px`; | |
| } | |
| // Add to score | |
| function addScore(points) { | |
| score += points; | |
| updateDisplay(); | |
| } | |
| // Update display | |
| function updateDisplay() { | |
| scoreDisplay.textContent = score; | |
| ballsDisplay.textContent = balls; | |
| } | |
| // Play sound (simulated with class changes) | |
| function playSound(type) { | |
| // In a real game, you would play actual sounds here | |
| // For this demo, we'll just log the sound type | |
| console.log(`Playing sound: ${type}`); | |
| } | |
| // Event listeners | |
| startBtn.addEventListener('click', initGame); | |
| plungerBtn.addEventListener('click', launchBall); | |
| // Flipper controls | |
| function activateLeftFlipper() { | |
| leftFlipper.classList.add('active'); | |
| leftFlipperActive = true; | |
| } | |
| function deactivateLeftFlipper() { | |
| leftFlipper.classList.remove('active'); | |
| leftFlipperActive = false; | |
| } | |
| function activateRightFlipper() { | |
| rightFlipper.classList.add('active'); | |
| rightFlipperActive = true; | |
| } | |
| function deactivateRightFlipper() { | |
| rightFlipper.classList.remove('active'); | |
| rightFlipperActive = false; | |
| } | |
| leftBtn.addEventListener('mousedown', activateLeftFlipper); | |
| leftBtn.addEventListener('mouseup', deactivateLeftFlipper); | |
| leftBtn.addEventListener('mouseleave', deactivateLeftFlipper); | |
| rightBtn.addEventListener('mousedown', activateRightFlipper); | |
| rightBtn.addEventListener('mouseup', deactivateRightFlipper); | |
| rightBtn.addEventListener('mouseleave', deactivateRightFlipper); | |
| // Keyboard controls | |
| document.addEventListener('keydown', (e) => { | |
| if (e.key === 'a' || e.key === 'A') activateLeftFlipper(); | |
| if (e.key === 'l' || e.key === 'L') activateRightFlipper(); | |
| if (e.key === ' ') launchBall(); | |
| }); | |
| document.addEventListener('keyup', (e) => { | |
| if (e.key === 'a' || e.key === 'A') deactivateLeftFlipper(); | |
| if (e.key === 'l' || e.key === 'L') deactivateRightFlipper(); | |
| }); | |
| // Initial setup | |
| resetBall(); | |
| }); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=jungnerd/pinball" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |