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> |