|
<!DOCTYPE html> |
|
<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;"> |
|
|
|
<div id="ball" class="absolute w-6 h-6 bg-white rounded-full shadow-lg z-10"></div> |
|
|
|
|
|
<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> |
|
|
|
|
|
<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> |
|
|
|
|
|
<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> |
|
|
|
|
|
<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> |
|
|
|
|
|
<div class="absolute bottom-4 left-1/2 transform -translate-x-1/2 w-24 h-8 bg-yellow-600 rounded"></div> |
|
|
|
|
|
<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> |
|
|
|
|
|
<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', () => { |
|
|
|
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'); |
|
|
|
|
|
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; |
|
|
|
|
|
function initGame() { |
|
resetBall(); |
|
score = 0; |
|
balls = 3; |
|
updateDisplay(); |
|
gameActive = true; |
|
|
|
if (gameInterval) clearInterval(gameInterval); |
|
gameInterval = setInterval(updateGame, 16); |
|
} |
|
|
|
|
|
function resetBall() { |
|
ballX = 300; |
|
ballY = 750; |
|
ballSpeedX = 0; |
|
ballSpeedY = 0; |
|
updateBallPosition(); |
|
} |
|
|
|
|
|
function launchBall() { |
|
if (ballY >= 750 && !gameActive) { |
|
ballSpeedY = -15 + Math.random() * 5; |
|
ballSpeedX = (Math.random() - 0.5) * 5; |
|
gameActive = true; |
|
} |
|
} |
|
|
|
|
|
function updateGame() { |
|
if (!gameActive) return; |
|
|
|
|
|
ballSpeedY += gravity; |
|
|
|
|
|
ballSpeedX *= friction; |
|
ballSpeedY *= friction; |
|
|
|
|
|
ballX += ballSpeedX; |
|
ballY += ballSpeedY; |
|
|
|
|
|
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'); |
|
} |
|
|
|
|
|
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'); |
|
} |
|
|
|
|
|
checkFlipperCollision(leftFlipper, true); |
|
checkFlipperCollision(rightFlipper, false); |
|
|
|
|
|
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) { |
|
|
|
const angle = Math.atan2(dy, dx); |
|
const force = 10; |
|
|
|
ballSpeedX = Math.cos(angle) * force; |
|
ballSpeedY = Math.sin(angle) * force; |
|
|
|
|
|
bumper.classList.add('hit', 'flash-animation'); |
|
setTimeout(() => { |
|
bumper.classList.remove('hit', 'flash-animation'); |
|
}, 300); |
|
|
|
|
|
addScore(100); |
|
playSound('bumper'); |
|
} |
|
}); |
|
|
|
|
|
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) { |
|
|
|
|
|
if (ballSpeedY < 0) ballSpeedY = -ballSpeedY * 1.2; |
|
else ballSpeedY = Math.abs(ballSpeedY) * 1.2; |
|
|
|
ballSpeedX += (Math.random() - 0.5) * 5; |
|
|
|
|
|
target.classList.add('bump-animation'); |
|
setTimeout(() => { |
|
target.classList.remove('bump-animation'); |
|
}, 200); |
|
|
|
|
|
addScore(200); |
|
playSound('target'); |
|
} |
|
}); |
|
|
|
|
|
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) { |
|
|
|
|
|
const isLeft = slingshot.id === 'left-slingshot'; |
|
|
|
|
|
ballSpeedX = isLeft ? 8 : -8; |
|
ballSpeedY = -5; |
|
|
|
|
|
slingshot.classList.add('bump-animation'); |
|
setTimeout(() => { |
|
slingshot.classList.remove('bump-animation'); |
|
}, 200); |
|
|
|
|
|
addScore(50); |
|
playSound('slingshot'); |
|
} |
|
}); |
|
|
|
|
|
updateBallPosition(); |
|
} |
|
|
|
|
|
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; |
|
|
|
|
|
if (ballX > flipperX && ballX < flipperX + flipperWidth && |
|
ballY > flipperY && ballY < flipperY + flipperHeight) { |
|
|
|
|
|
const force = isLeft ? -8 : 8; |
|
ballSpeedX = force; |
|
ballSpeedY = -10; |
|
|
|
|
|
addScore(25); |
|
playSound('flipper'); |
|
} |
|
} |
|
|
|
|
|
function updateBallPosition() { |
|
ball.style.left = `${ballX - 12}px`; |
|
ball.style.top = `${ballY - 12}px`; |
|
} |
|
|
|
|
|
function addScore(points) { |
|
score += points; |
|
updateDisplay(); |
|
} |
|
|
|
|
|
function updateDisplay() { |
|
scoreDisplay.textContent = score; |
|
ballsDisplay.textContent = balls; |
|
} |
|
|
|
|
|
function playSound(type) { |
|
|
|
|
|
console.log(`Playing sound: ${type}`); |
|
} |
|
|
|
|
|
startBtn.addEventListener('click', initGame); |
|
plungerBtn.addEventListener('click', launchBall); |
|
|
|
|
|
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); |
|
|
|
|
|
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(); |
|
}); |
|
|
|
|
|
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> |