Spaces:
Running
Running
| <html lang="pt-BR"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Neon Pong Defender</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap'); | |
| body { | |
| font-family: 'Orbitron', sans-serif; | |
| background-color: #0f0f1a; | |
| overflow: hidden; | |
| margin: 0; | |
| padding: 0; | |
| cursor: none; | |
| } | |
| .game-container { | |
| position: relative; | |
| width: 800px; | |
| height: 600px; | |
| margin: 20px auto; | |
| border: 3px solid #4f46e5; | |
| border-radius: 8px; | |
| box-shadow: 0 0 20px #4f46e5, inset 0 0 20px rgba(79, 70, 229, 0.3); | |
| overflow: hidden; | |
| } | |
| .ball { | |
| position: absolute; | |
| width: 20px; | |
| height: 20px; | |
| border-radius: 50%; | |
| background: linear-gradient(145deg, #00ff9d, #00b8ff); | |
| box-shadow: 0 0 10px #00ff9d, 0 0 20px #00b8ff; | |
| transition: all 0.3s; | |
| } | |
| .paddle { | |
| position: absolute; | |
| width: 160px; | |
| height: 15px; | |
| bottom: 10px; | |
| border-radius: 10px; | |
| background: linear-gradient(145deg, #ff00aa, #ff0066); | |
| box-shadow: 0 0 10px #ff00aa, 0 0 20px rgba(255, 0, 170, 0.5); | |
| } | |
| .score { | |
| color: #00ff9d; | |
| text-shadow: 0 0 5px #00ff9d; | |
| font-size: 24px; | |
| } | |
| .timer { | |
| color: #00b8ff; | |
| text-shadow: 0 0 5px #00b8ff; | |
| font-size: 24px; | |
| } | |
| .pulse { | |
| animation: pulse 0.5s ease-out; | |
| } | |
| @keyframes pulse { | |
| 0% { transform: scale(1); } | |
| 50% { transform: scale(1.3); } | |
| 100% { transform: scale(1); } | |
| } | |
| .explode { | |
| animation: explode 0.5s ease-out; | |
| } | |
| @keyframes explode { | |
| 0% { transform: scale(1); opacity: 1; } | |
| 100% { transform: scale(3); opacity: 0; } | |
| } | |
| .grid-lines { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: | |
| linear-gradient(to right, rgba(79, 70, 229, 0.1) 1px, transparent 1px), | |
| linear-gradient(to bottom, rgba(79, 70, 229, 0.1) 1px, transparent 1px); | |
| background-size: 20px 20px; | |
| pointer-events: none; | |
| } | |
| .game-over { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background-color: rgba(15, 15, 26, 0.8); | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| align-items: center; | |
| z-index: 100; | |
| color: #ff0066; | |
| font-size: 48px; | |
| text-shadow: 0 0 10px #ff0066; | |
| display: none; | |
| } | |
| .restart-btn { | |
| margin-top: 20px; | |
| padding: 10px 30px; | |
| background: linear-gradient(145deg, #4f46e5, #7c3aed); | |
| border: none; | |
| border-radius: 5px; | |
| color: white; | |
| font-family: 'Orbitron', sans-serif; | |
| font-size: 20px; | |
| cursor: pointer; | |
| box-shadow: 0 0 10px #4f46e5; | |
| transition: all 0.3s; | |
| } | |
| .restart-btn:hover { | |
| transform: scale(1.05); | |
| box-shadow: 0 0 20px #4f46e5; | |
| } | |
| .speed-indicator { | |
| position: absolute; | |
| top: 10px; | |
| right: 10px; | |
| color: #00b8ff; | |
| font-size: 16px; | |
| text-shadow: 0 0 5px #00b8ff; | |
| } | |
| .particle { | |
| position: absolute; | |
| width: 5px; | |
| height: 5px; | |
| border-radius: 50%; | |
| background-color: #00ff9d; | |
| pointer-events: none; | |
| } | |
| .custom-cursor { | |
| position: absolute; | |
| width: 20px; | |
| height: 20px; | |
| border-radius: 50%; | |
| background-color: rgba(255, 255, 255, 0.5); | |
| pointer-events: none; | |
| z-index: 1000; | |
| mix-blend-mode: difference; | |
| display: none; | |
| } | |
| .controls-info { | |
| position: absolute; | |
| bottom: 40px; | |
| left: 10px; | |
| color: #4f46e5; | |
| font-size: 12px; | |
| text-shadow: 0 0 5px #4f46e5; | |
| } | |
| .instructions { | |
| position: absolute; | |
| top: 40px; | |
| left: 10px; | |
| color: #4f46e5; | |
| font-size: 12px; | |
| text-shadow: 0 0 5px #4f46e5; | |
| background-color: rgba(15, 15, 26, 0.7); | |
| padding: 10px; | |
| border-radius: 5px; | |
| max-width: 200px; | |
| } | |
| </style> | |
| </head> | |
| <body class="flex flex-col items-center justify-center min-h-screen"> | |
| <h1 class="text-4xl font-bold mb-4 text-transparent bg-clip-text bg-gradient-to-r from-purple-500 to-pink-500">NEON PONG DEFENDER</h1> | |
| <div class="flex justify-between w-96 mb-4"> | |
| <div class="score">PONTOS: <span id="player-score">0</span></div> | |
| <div class="timer">TEMPO: <span id="game-timer">00:00</span></div> | |
| </div> | |
| <div class="game-container" id="game-container"> | |
| <div class="grid-lines"></div> | |
| <div class="speed-indicator">VELOCIDADE: <span id="speed-level">1x</span></div> | |
| <div class="ball" id="ball"></div> | |
| <div class="paddle" id="paddle"></div> | |
| <div class="game-over" id="game-over"> | |
| FIM DE JOGO | |
| <button class="restart-btn" id="restart-btn">JOGAR NOVAMENTE</button> | |
| </div> | |
| <div class="controls-info">CONTROLES: Teclas de seta ou mouse</div> | |
| <div class="instructions"> | |
| <p><strong>COMO JOGAR:</strong></p> | |
| <p>- Use as teclas ← → ou o mouse para mover a raquete</p> | |
| <p>- Impedir que a bola caia</p> | |
| <p>- Cada rebatida marca 1 ponto</p> | |
| <p>- A velocidade aumenta a cada minuto</p> | |
| </div> | |
| </div> | |
| <div class="mt-4 text-purple-400 text-center"> | |
| <p>Defenda a bola com sua raquete e marque o máximo de pontos!</p> | |
| <p class="text-sm text-purple-600">O jogo fica mais rápido a cada minuto!</p> | |
| </div> | |
| <div class="custom-cursor" id="custom-cursor"></div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const gameContainer = document.getElementById('game-container'); | |
| const ball = document.getElementById('ball'); | |
| const paddle = document.getElementById('paddle'); | |
| const playerScoreElement = document.getElementById('player-score'); | |
| const gameTimerElement = document.getElementById('game-timer'); | |
| const gameOverScreen = document.getElementById('game-over'); | |
| const restartBtn = document.getElementById('restart-btn'); | |
| const speedLevelElement = document.getElementById('speed-level'); | |
| const customCursor = document.getElementById('custom-cursor'); | |
| const containerWidth = gameContainer.offsetWidth; | |
| const containerHeight = gameContainer.offsetHeight; | |
| const paddleWidth = containerWidth / 5; | |
| let ballX = containerWidth / 2; | |
| let ballY = 50; | |
| let ballSpeedX = 3; | |
| let ballSpeedY = 3; | |
| let paddleX = (containerWidth - paddleWidth) / 2; | |
| let playerScore = 0; | |
| let gameTime = 0; | |
| let gameSpeed = 1; | |
| let gameRunning = true; | |
| let animationId; | |
| let mouseX = 0; | |
| let useMouse = false; | |
| // Cores aleatórias para a bola | |
| const ballColors = [ | |
| {gradient: 'linear-gradient(145deg, #00ff9d, #00b8ff)', shadow1: '#00ff9d', shadow2: '#00b8ff'}, | |
| {gradient: 'linear-gradient(145deg, #ff9d00, #ff00b8)', shadow1: '#ff9d00', shadow2: '#ff00b8'}, | |
| {gradient: 'linear-gradient(145deg, #9d00ff, #00b8ff)', shadow1: '#9d00ff', shadow2: '#00b8ff'}, | |
| {gradient: 'linear-gradient(145deg, #ff0066, #ffcc00)', shadow1: '#ff0066', shadow2: '#ffcc00'}, | |
| {gradient: 'linear-gradient(145deg, #00ff66, #6600ff)', shadow1: '#00ff66', shadow2: '#6600ff'} | |
| ]; | |
| // Set initial paddle size | |
| paddle.style.width = `${paddleWidth}px`; | |
| // Mouse movement tracking | |
| gameContainer.addEventListener('mousemove', (e) => { | |
| const rect = gameContainer.getBoundingClientRect(); | |
| mouseX = e.clientX - rect.left - paddleWidth / 2; | |
| useMouse = true; | |
| // Update custom cursor position | |
| customCursor.style.display = 'block'; | |
| customCursor.style.left = `${e.clientX - 10}px`; | |
| customCursor.style.top = `${e.clientY - 10}px`; | |
| }); | |
| gameContainer.addEventListener('mouseleave', () => { | |
| useMouse = false; | |
| customCursor.style.display = 'none'; | |
| }); | |
| // Keyboard controls | |
| const keys = { | |
| ArrowLeft: false, | |
| ArrowRight: false | |
| }; | |
| document.addEventListener('keydown', (e) => { | |
| if (e.key in keys) { | |
| keys[e.key] = true; | |
| useMouse = false; | |
| customCursor.style.display = 'none'; | |
| } | |
| }); | |
| document.addEventListener('keyup', (e) => { | |
| if (e.key in keys) { | |
| keys[e.key] = false; | |
| } | |
| }); | |
| restartBtn.addEventListener('click', resetGame); | |
| function updatePaddlePosition() { | |
| if (useMouse) { | |
| // Mouse control | |
| paddleX = Math.max(0, Math.min(mouseX, containerWidth - paddleWidth)); | |
| } else { | |
| // Keyboard control | |
| if (keys.ArrowLeft && paddleX > 0) { | |
| paddleX -= 8 * gameSpeed; | |
| } | |
| if (keys.ArrowRight && paddleX < containerWidth - paddleWidth) { | |
| paddleX += 8 * gameSpeed; | |
| } | |
| } | |
| paddle.style.left = `${paddleX}px`; | |
| } | |
| function updateBallPosition() { | |
| ballX += ballSpeedX * gameSpeed; | |
| ballY += ballSpeedY * gameSpeed; | |
| // Ball collision with walls | |
| if (ballX <= 0) { | |
| ballX = 0; | |
| ballSpeedX = -ballSpeedX; | |
| createParticles(ballX, ballY, 5); | |
| } | |
| if (ballX >= containerWidth - 20) { | |
| ballX = containerWidth - 20; | |
| ballSpeedX = -ballSpeedX; | |
| createParticles(ballX, ballY, 5); | |
| } | |
| // Ball collision with ceiling | |
| if (ballY <= 0) { | |
| ballY = 0; | |
| ballSpeedY = -ballSpeedY; | |
| createParticles(ballX, ballY, 5); | |
| } | |
| // Ball collision with paddle | |
| if ( | |
| ballY >= containerHeight - 35 && | |
| ballY <= containerHeight - 20 && | |
| ballX >= paddleX && | |
| ballX <= paddleX + paddleWidth | |
| ) { | |
| ballSpeedY = -ballSpeedY; | |
| // Add some randomness to the bounce | |
| const hitPosition = (ballX - paddleX) / paddleWidth; | |
| ballSpeedX = (hitPosition - 0.5) * 8; | |
| playerScore++; | |
| playerScoreElement.textContent = playerScore; | |
| // Paddle hit effect | |
| paddle.classList.add('pulse'); | |
| setTimeout(() => paddle.classList.remove('pulse'), 500); | |
| // Explode current ball and create new one | |
| ball.classList.add('explode'); | |
| createParticles(ballX, ballY, 20, '#ff00aa'); | |
| // Create new ball with random color | |
| setTimeout(() => { | |
| ball.classList.remove('explode'); | |
| const randomColor = ballColors[Math.floor(Math.random() * ballColors.length)]; | |
| ball.style.background = randomColor.gradient; | |
| ball.style.boxShadow = `0 0 10px ${randomColor.shadow1}, 0 0 20px ${randomColor.shadow2}`; | |
| ballX = containerWidth / 2; | |
| ballY = 50; | |
| ballSpeedX = (Math.random() > 0.5 ? 3 : -3) * gameSpeed; | |
| ballSpeedY = 3 * gameSpeed; | |
| }, 500); | |
| } | |
| // Ball out of bounds (game over) | |
| if (ballY >= containerHeight) { | |
| gameOver(); | |
| // Explosion effect | |
| ball.classList.add('explode'); | |
| createParticles(ballX, ballY, 20, '#ff0066'); | |
| } | |
| ball.style.left = `${ballX}px`; | |
| ball.style.top = `${ballY}px`; | |
| } | |
| function createParticles(x, y, count, color = '#00ff9d') { | |
| for (let i = 0; i < count; i++) { | |
| const particle = document.createElement('div'); | |
| particle.className = 'particle'; | |
| particle.style.left = `${x}px`; | |
| particle.style.top = `${y}px`; | |
| particle.style.backgroundColor = color; | |
| gameContainer.appendChild(particle); | |
| const angle = Math.random() * Math.PI * 2; | |
| const speed = 1 + Math.random() * 3; | |
| const lifetime = 500 + Math.random() * 500; | |
| const particleSpeedX = Math.cos(angle) * speed; | |
| const particleSpeedY = Math.sin(angle) * speed; | |
| let particleX = x; | |
| let particleY = y; | |
| const animateParticle = () => { | |
| particleX += particleSpeedX; | |
| particleY += particleSpeedY; | |
| particle.style.opacity = parseFloat(particle.style.opacity || 1) - 0.01; | |
| particle.style.left = `${particleX}px`; | |
| particle.style.top = `${particleY}px`; | |
| if (parseFloat(particle.style.opacity || 1) > 0) { | |
| requestAnimationFrame(animateParticle); | |
| } else { | |
| particle.remove(); | |
| } | |
| }; | |
| setTimeout(() => { | |
| animateParticle(); | |
| }, 10); | |
| setTimeout(() => { | |
| particle.remove(); | |
| }, lifetime); | |
| } | |
| } | |
| function updateTimer() { | |
| gameTime++; | |
| const minutes = Math.floor(gameTime / 60).toString().padStart(2, '0'); | |
| const seconds = (gameTime % 60).toString().padStart(2, '0'); | |
| gameTimerElement.textContent = `${minutes}:${seconds}`; | |
| // Increase game speed every minute | |
| if (gameTime % 60 === 0) { | |
| gameSpeed += 0.25; | |
| speedLevelElement.textContent = `${gameSpeed.toFixed(2)}x`; | |
| // Speed increase effect | |
| gameContainer.style.boxShadow = `0 0 30px #4f46e5, inset 0 0 30px rgba(79, 70, 229, 0.5)`; | |
| setTimeout(() => { | |
| gameContainer.style.boxShadow = `0 0 20px #4f46e5, inset 0 0 20px rgba(79, 70, 229, 0.3)`; | |
| }, 500); | |
| } | |
| } | |
| function gameOver() { | |
| gameRunning = false; | |
| gameOverScreen.style.display = 'flex'; | |
| cancelAnimationFrame(animationId); | |
| } | |
| function resetGame() { | |
| ballX = containerWidth / 2; | |
| ballY = 50; | |
| ballSpeedX = 3; | |
| ballSpeedY = 3; | |
| paddleX = (containerWidth - paddleWidth) / 2; | |
| playerScore = 0; | |
| gameTime = 0; | |
| gameSpeed = 1; | |
| // Reset ball color | |
| ball.style.background = 'linear-gradient(145deg, #00ff9d, #00b8ff)'; | |
| ball.style.boxShadow = '0 0 10px #00ff9d, 0 0 20px #00b8ff'; | |
| playerScoreElement.textContent = '0'; | |
| gameTimerElement.textContent = '00:00'; | |
| speedLevelElement.textContent = '1x'; | |
| ball.classList.remove('explode'); | |
| gameOverScreen.style.display = 'none'; | |
| gameRunning = true; | |
| // Remove all particles | |
| document.querySelectorAll('.particle').forEach(p => p.remove()); | |
| gameLoop(); | |
| } | |
| function gameLoop() { | |
| if (!gameRunning) return; | |
| updatePaddlePosition(); | |
| updateBallPosition(); | |
| animationId = requestAnimationFrame(gameLoop); | |
| } | |
| // Start the game | |
| resetGame(); | |
| // Start timer | |
| setInterval(() => { | |
| if (gameRunning) { | |
| updateTimer(); | |
| } | |
| }, 1000); | |
| }); | |
| </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=gallabs/neon-pong-defender" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |