Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>CyberPong</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script> | |
<style> | |
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;700&display=swap'); | |
body { | |
font-family: 'Orbitron', sans-serif; | |
overflow: hidden; | |
background-color: #000; | |
color: #00ff41; | |
} | |
.game-container { | |
position: relative; | |
} | |
.particle { | |
position: absolute; | |
width: 10px; | |
height: 10px; | |
border-radius: 50%; | |
pointer-events: none; | |
} | |
.glow { | |
text-shadow: 0 0 10px #00ff41, 0 0 20px #00ff41; | |
} | |
.paddle-glow { | |
box-shadow: 0 0 15px #00ff41; | |
} | |
.ball-glow { | |
box-shadow: 0 0 20px #ff00ff, 0 0 30px #ff00ff; | |
} | |
@keyframes flicker { | |
0%, 19%, 21%, 23%, 25%, 54%, 56%, 100% { | |
opacity: 1; | |
} | |
20%, 22%, 24%, 55% { | |
opacity: 0.2; | |
} | |
} | |
.flicker { | |
animation: flicker 3s infinite; | |
} | |
.score-digit { | |
display: inline-block; | |
text-align: center; | |
width: 40px; | |
} | |
</style> | |
</head> | |
<body class="h-screen flex flex-col items-center justify-center relative"> | |
<div class="absolute top-4 left-4 flex items-center"> | |
<div class="w-4 h-4 bg-green-500 rounded-full mr-2 animate-pulse"></div> | |
<span class="text-green-500">SYSTEM: ONLINE</span> | |
</div> | |
<h1 class="text-5xl mb-6 glow flicker font-bold tracking-wider">CYBERPONG</h1> | |
<div class="flex justify-between w-full max-w-4xl mb-4 px-10"> | |
<div class="text-3xl glow"> | |
PLAYER <span class="score-digit" id="playerScore">0</span> | |
</div> | |
<div class="text-3xl glow"> | |
CPU <span class="score-digit" id="cpuScore">0</span> | |
</div> | |
</div> | |
<div class="game-container border-2 border-green-500 relative w-full max-w-4xl h-96 bg-black"> | |
<div id="paddlePlayer" class="w-4 h-20 bg-green-500 rounded-md absolute left-4 top-1/2 transform -translate-y-1/2 paddle-glow"></div> | |
<div id="paddleCPU" class="w-4 h-20 bg-purple-500 rounded-md absolute right-4 top-1/2 transform -translate-y-1/2 paddle-glow"></div> | |
<div id="ball" class="w-5 h-5 bg-white rounded-full absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 ball-glow"></div> | |
<div class="absolute top-0 left-0 w-full h-full grid grid-cols-15 grid-rows-8 pointer-events-none"> | |
<div class="border-r border-b border-gray-800 opacity-20"></div> | |
<!-- Grid pattern repeated --> | |
</div> | |
</div> | |
<div class="mt-6 text-green-500 flex gap-8"> | |
<div class="flex items-center"> | |
<i class="fas fa-gamepad mr-2"></i> | |
<span>W/S or ↑/↓ to move</span> | |
</div> | |
<div class="flex items-center"> | |
<i class="fas fa-bolt mr-2"></i> | |
<span>SPACE to launch</span> | |
</div> | |
</div> | |
<div id="gameOver" class="absolute inset-0 bg-black bg-opacity-80 hidden flex-col items-center justify-center glow"> | |
<h2 class="text-5xl mb-6">GAME OVER</h2> | |
<div class="text-2xl mb-8" id="winnerText"></div> | |
<button id="restartBtn" class="px-6 py-3 bg-green-500 text-black font-bold rounded hover:bg-green-400 transition"> | |
SYSTEM REBOOT (Play Again) | |
</button> | |
</div> | |
<script> | |
// Game elements | |
const gameContainer = document.querySelector('.game-container'); | |
const paddlePlayer = document.getElementById('paddlePlayer'); | |
const paddleCPU = document.getElementById('paddleCPU'); | |
const ball = document.getElementById('ball'); | |
const playerScoreElement = document.getElementById('playerScore'); | |
const cpuScoreElement = document.getElementById('cpuScore'); | |
const gameOverScreen = document.getElementById('gameOver'); | |
const winnerText = document.getElementById('winnerText'); | |
const restartBtn = document.getElementById('restartBtn'); | |
// Game state | |
let ballX = 400; | |
let ballY = 200; | |
let ballSpeedX = 0; | |
let ballSpeedY = 0; | |
let playerY = 160; | |
let cpuY = 160; | |
let playerScore = 0; | |
let cpuScore = 0; | |
let gameRunning = false; | |
let gameStarted = false; | |
// Game constants | |
const PADDLE_HEIGHT = 80; | |
const PADDLE_WIDTH = 16; | |
const BALL_SIZE = 10; | |
const GAME_WIDTH = gameContainer.clientWidth; | |
const GAME_HEIGHT = gameContainer.clientHeight; | |
const PADDLE_SPEED = 8; | |
const INITIAL_BALL_SPEED = 5; | |
// Initialize game | |
function init() { | |
ballX = GAME_WIDTH / 2; | |
ballY = GAME_HEIGHT / 2; | |
ballSpeedX = 0; | |
ballSpeedY = 0; | |
playerY = (GAME_HEIGHT - PADDLE_HEIGHT) / 2; | |
cpuY = (GAME_HEIGHT - PADDLE_HEIGHT) / 2; | |
gameRunning = false; | |
gameStarted = false; | |
updatePositions(); | |
} | |
// Update element positions | |
function updatePositions() { | |
// Ensure paddles stay within bounds | |
playerY = Math.max(0, Math.min(GAME_HEIGHT - PADDLE_HEIGHT, playerY)); | |
cpuY = Math.max(0, Math.min(GAME_HEIGHT - PADDLE_HEIGHT, cpuY)); | |
paddlePlayer.style.top = `${playerY}px`; | |
paddleCPU.style.top = `${cpuY}px`; | |
ball.style.left = `${ballX - BALL_SIZE/2}px`; | |
ball.style.top = `${ballY - BALL_SIZE/2}px`; | |
} | |
// Start the game | |
function startGame() { | |
if (!gameStarted) { | |
gameStarted = true; | |
ballSpeedX = Math.random() > 0.5 ? INITIAL_BALL_SPEED : -INITIAL_BALL_SPEED; | |
ballSpeedY = (Math.random() * 4 - 2); | |
gameRunning = true; | |
} | |
} | |
// Game loop | |
function gameLoop() { | |
if (!gameRunning) return; | |
// Move ball | |
ballX += ballSpeedX; | |
ballY += ballSpeedY; | |
// Ball collision with top and bottom | |
if (ballY <= 0 || ballY >= GAME_HEIGHT) { | |
ballSpeedY = -ballSpeedY; | |
createExplosion(ballX, ballY, 5, '#ff00ff'); | |
} | |
// Ball collision with paddles | |
// Player paddle | |
if ( | |
ballX <= PADDLE_WIDTH + 20 && | |
ballY >= playerY && | |
ballY <= playerY + PADDLE_HEIGHT | |
) { | |
ballSpeedX = Math.abs(ballSpeedX) * 1.1; | |
ballSpeedY = ((ballY - (playerY + PADDLE_HEIGHT/2)) / PADDLE_HEIGHT) * 8; | |
createExplosion(ballX, ballY, 10, '#00ff41'); | |
} | |
// CPU paddle | |
if ( | |
ballX >= GAME_WIDTH - PADDLE_WIDTH - 20 && | |
ballY >= cpuY && | |
ballY <= cpuY + PADDLE_HEIGHT | |
) { | |
ballSpeedX = -Math.abs(ballSpeedX) * 1.1; | |
ballSpeedY = ((ballY - (cpuY + PADDLE_HEIGHT/2)) / PADDLE_HEIGHT) * 8; | |
createExplosion(ballX, ballY, 10, '#ff00ff'); | |
} | |
// Scoring | |
if (ballX < 0) { | |
cpuScore++; | |
cpuScoreElement.textContent = cpuScore; | |
createExplosion(ballX, ballY, 30, '#ff0000'); | |
resetBall(); | |
} else if (ballX > GAME_WIDTH) { | |
playerScore++; | |
playerScoreElement.textContent = playerScore; | |
createExplosion(ballX, ballY, 30, '#00ff3c'); | |
resetBall(); | |
} | |
// AI for CPU paddle - improved to stay in bounds | |
const cpuPaddleCenter = cpuY + PADDLE_HEIGHT / 2; | |
const ballCenter = ballY; | |
if (cpuPaddleCenter < ballCenter - 15) { | |
cpuY = Math.min(cpuY + PADDLE_SPEED * 0.7, GAME_HEIGHT - PADDLE_HEIGHT); | |
} else if (cpuPaddleCenter > ballCenter + 15) { | |
cpuY = Math.max(cpuY - PADDLE_SPEED * 0.7, 0); | |
} | |
// Check game over | |
if (playerScore >= 5 || cpuScore >= 5) { | |
endGame(); | |
} | |
updatePositions(); | |
} | |
// Reset ball after scoring | |
function resetBall() { | |
if (playerScore >= 5 || cpuScore >= 5) return; | |
ballX = GAME_WIDTH / 2; | |
ballY = GAME_HEIGHT / 2; | |
ballSpeedX = 0; | |
ballSpeedY = 0; | |
gameRunning = false; | |
gameStarted = false; | |
setTimeout(() => { | |
if (!gameRunning) { | |
ballSpeedX = Math.random() > 0.5 ? INITIAL_BALL_SPEED : -INITIAL_BALL_SPEED; | |
ballSpeedY = (Math.random() * 4 - 2); | |
gameRunning = true; | |
} | |
}, 1000); | |
} | |
// Create explosion effect | |
function createExplosion(x, y, particleCount, color) { | |
for (let i = 0; i < particleCount; i++) { | |
const particle = document.createElement('div'); | |
particle.className = 'particle'; | |
particle.style.backgroundColor = color; | |
particle.style.left = `${x}px`; | |
particle.style.top = `${y}px`; | |
const angle = Math.random() * Math.PI * 2; | |
const speed = 1 + Math.random() * 3; | |
const size = 3 + Math.random() * 8; | |
particle.style.width = `${size}px`; | |
particle.style.height = `${size}px`; | |
gameContainer.appendChild(particle); | |
const duration = 500 + Math.random() * 500; | |
let startTime = null; | |
function animate(time) { | |
if (!startTime) startTime = time; | |
const elapsed = time - startTime; | |
const progress = elapsed / duration; | |
if (progress < 1) { | |
particle.style.opacity = 1 - progress; | |
particle.style.transform = `translate(${Math.sin(angle) * speed * progress * 100}px, ${Math.cos(angle) * speed * progress * 100}px)`; | |
requestAnimationFrame(animate); | |
} else { | |
particle.remove(); | |
} | |
} | |
requestAnimationFrame(animate); | |
} | |
} | |
// End the game | |
function endGame() { | |
gameRunning = false; | |
gameStarted = false; | |
if (playerScore > cpuScore) { | |
winnerText.textContent = "PLAYER VICTORY"; | |
createExplosion(GAME_WIDTH / 2, GAME_HEIGHT / 2, 100, '#00ff41'); | |
} else { | |
winnerText.textContent = "SYSTEM VICTORY"; | |
createExplosion(GAME_WIDTH / 2, GAME_HEIGHT / 2, 100, '#ff00ff'); | |
} | |
gameOverScreen.classList.remove('hidden'); | |
gameOverScreen.classList.add('flex'); | |
} | |
// Restart game | |
function restartGame() { | |
playerScore = 0; | |
cpuScore = 0; | |
playerScoreElement.textContent = '0'; | |
cpuScoreElement.textContent = '0'; | |
gameOverScreen.classList.add('hidden'); | |
gameOverScreen.classList.remove('flex'); | |
init(); | |
} | |
// Keyboard controls | |
const keys = { | |
w: false, | |
s: false, | |
ArrowUp: false, | |
ArrowDown: false, | |
' ': false | |
}; | |
window.addEventListener('keydown', (e) => { | |
if (keys.hasOwnProperty(e.key)) { | |
keys[e.key] = true; | |
if (e.key === ' ' && !gameStarted) { | |
startGame(); | |
} | |
} | |
}); | |
window.addEventListener('keyup', (e) => { | |
if (keys.hasOwnProperty(e.key)) { | |
keys[e.key] = false; | |
} | |
}); | |
// Handle paddle movement with boundary checks | |
function handleInput() { | |
if (!gameRunning) return; | |
if ((keys.w || keys.ArrowUp)) { | |
playerY = Math.max(0, playerY - PADDLE_SPEED); | |
} | |
if ((keys.s || keys.ArrowDown)) { | |
playerY = Math.min(GAME_HEIGHT - PADDLE_HEIGHT, playerY + PADDLE_SPEED); | |
} | |
updatePositions(); | |
} | |
// Event listeners | |
restartBtn.addEventListener('click', restartGame); | |
// Start the game engine | |
init(); | |
// Main animation loop | |
function main() { | |
gameLoop(); | |
handleInput(); | |
requestAnimationFrame(main); | |
} | |
main(); | |
// Generate grid pattern | |
const gridContainer = document.querySelector('.grid'); | |
if (gridContainer) { | |
for (let i = 0; i < 15 * 8; i++) { | |
const cell = document.createElement('div'); | |
cell.className = 'border-r border-b border-gray-800 opacity-20'; | |
gridContainer.appendChild(cell); | |
} | |
} | |
</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=royam0820/royam0820-pong-game" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |