pong-multiplayer / index.html
SignorNessuno's picture
Add 2 files
eec3711 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Cyber Pong Explosion - Online Multiplayer</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
touch-action: manipulation;
}
body {
background-color: #0a0a1a;
color: #00fffc;
font-family: 'Orbitron', 'Arial', sans-serif;
overflow: hidden;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
-webkit-tap-highlight-color: transparent;
}
@font-face {
font-family: 'Orbitron';
src: url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap');
}
.game-container {
position: relative;
width: 100%;
max-width: 800px;
height: 100vh;
max-height: 500px;
border: 3px solid #00fffc;
box-shadow: 0 0 20px #00fffc, inset 0 0 20px rgba(0, 255, 252, 0.3);
border-radius: 10px;
overflow: hidden;
background: radial-gradient(circle at center, #0a0a1a 0%, #000 100%);
}
@media (max-width: 600px) {
.game-container {
border-radius: 0;
border: none;
max-height: none;
height: 100vh;
}
}
.paddle {
position: absolute;
width: 15px;
height: 100px;
background: linear-gradient(to bottom, #00fffc, #0084ff);
border-radius: 5px;
box-shadow: 0 0 10px #00fffc;
}
@media (max-width: 600px) {
.paddle {
width: 12px;
height: 80px;
}
}
#playerPaddle {
left: 20px;
}
#aiPaddle, #opponentPaddle {
right: 20px;
}
.ball {
position: absolute;
width: 20px;
height: 20px;
background: #ff00e4;
border-radius: 50%;
box-shadow: 0 0 15px #ff00e4;
}
@media (max-width: 600px) {
.ball {
width: 16px;
height: 16px;
}
}
.score {
position: absolute;
top: 20px;
font-size: 2rem;
font-weight: bold;
text-shadow: 0 0 10px #00fffc;
}
@media (max-width: 600px) {
.score {
font-size: 1.5rem;
top: 10px;
}
}
#playerScore {
left: 20%;
}
#aiScore, #opponentScore {
right: 20%;
}
.center-line {
position: absolute;
top: 0;
left: 50%;
width: 2px;
height: 100%;
background: linear-gradient(to bottom,
transparent 0%,
rgba(0, 255, 252, 0.3) 10%,
transparent 20%,
rgba(0, 255, 252, 0.3) 30%,
transparent 40%,
rgba(0, 255, 252, 0.3) 50%,
transparent 60%,
rgba(0, 255, 252, 0.3) 70%,
transparent 80%,
rgba(0, 255, 252, 0.3) 90%,
transparent 100%);
}
.particle {
position: absolute;
width: 5px;
height: 5px;
border-radius: 50%;
pointer-events: none;
}
.power-up {
position: absolute;
width: 20px;
height: 20px;
border-radius: 50%;
box-shadow: 0 0 10px currentColor;
animation: pulse 1.5s infinite;
}
@media (max-width: 600px) {
.power-up {
width: 16px;
height: 16px;
}
}
@keyframes pulse {
0% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.2); opacity: 0.7; }
100% { transform: scale(1); opacity: 1; }
}
.power-up.speed {
background-color: #ff00e4;
color: #ff00e4;
}
.power-up.size {
background-color: #00ff00;
color: #00ff00;
}
.power-up.freeze {
background-color: #00b4ff;
color: #00b4ff;
}
.game-over {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 10;
display: none;
}
.game-over h1 {
font-size: 2.5rem;
margin-bottom: 20px;
text-shadow: 0 0 15px #ff00e4;
color: #ff00e4;
text-align: center;
padding: 0 20px;
}
@media (max-width: 600px) {
.game-over h1 {
font-size: 2rem;
}
}
.game-over p {
font-size: 1.2rem;
margin-bottom: 30px;
text-align: center;
padding: 0 20px;
}
.btn {
padding: 10px 20px;
background: linear-gradient(to right, #00fffc, #0084ff);
border: none;
border-radius: 5px;
color: #0a0a1a;
font-family: 'Orbitron', sans-serif;
font-size: 1rem;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 0 10px #00fffc;
margin: 5px;
}
@media (max-width: 600px) {
.btn {
padding: 8px 16px;
font-size: 0.9rem;
}
}
.btn:hover {
transform: scale(1.05);
box-shadow: 0 0 20px #00fffc;
}
.start-screen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 10;
padding: 20px;
}
.start-screen h1 {
font-size: 2.5rem;
margin-bottom: 20px;
text-shadow: 0 0 15px #00fffc;
color: #00fffc;
text-align: center;
}
@media (max-width: 600px) {
.start-screen h1 {
font-size: 2rem;
}
}
.start-screen p {
font-size: 1rem;
margin-bottom: 20px;
max-width: 500px;
text-align: center;
line-height: 1.6;
}
@media (max-width: 600px) {
.start-screen p {
font-size: 0.9rem;
}
}
.instructions {
margin-top: 20px;
font-size: 0.9rem;
color: #00b4ff;
text-align: center;
}
@media (max-width: 600px) {
.instructions {
font-size: 0.8rem;
margin-top: 15px;
}
}
.power-up-indicator {
position: absolute;
bottom: 20px;
font-size: 0.8rem;
color: #00fffc;
text-align: center;
width: 100%;
display: none;
padding: 0 10px;
}
@media (max-width: 600px) {
.power-up-indicator {
font-size: 0.7rem;
bottom: 15px;
}
}
.glow {
animation: glow 1.5s infinite alternate;
}
@keyframes glow {
from {
text-shadow: 0 0 5px #00fffc;
}
to {
text-shadow: 0 0 15px #00fffc, 0 0 20px #0084ff;
}
}
.multiplayer-options {
display: flex;
gap: 15px;
margin-top: 20px;
flex-wrap: wrap;
justify-content: center;
}
@media (max-width: 600px) {
.multiplayer-options {
gap: 10px;
}
}
.room-code-container {
margin-top: 20px;
display: none;
flex-direction: column;
align-items: center;
text-align: center;
padding: 0 20px;
}
.room-code {
font-size: 1.2rem;
letter-spacing: 3px;
margin: 10px 0;
padding: 8px 15px;
background: rgba(0, 255, 252, 0.1);
border: 1px solid #00fffc;
border-radius: 5px;
}
@media (max-width: 600px) {
.room-code {
font-size: 1rem;
letter-spacing: 2px;
padding: 6px 12px;
}
}
.waiting-message {
margin-top: 15px;
color: #00b4ff;
font-size: 1rem;
}
@media (max-width: 600px) {
.waiting-message {
font-size: 0.9rem;
}
}
.connection-status {
position: absolute;
top: 10px;
right: 10px;
font-size: 0.7rem;
display: flex;
align-items: center;
}
.connection-status .indicator {
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 5px;
}
.connection-status .connected {
background-color: #00ff00;
box-shadow: 0 0 5px #00ff00;
}
.connection-status .disconnected {
background-color: #ff0000;
box-shadow: 0 0 5px #ff0000;
}
.connection-status .connecting {
background-color: #ffff00;
box-shadow: 0 0 5px #ffff00;
animation: pulse 1s infinite;
}
.player-name {
position: absolute;
top: 50px;
font-size: 0.8rem;
}
@media (max-width: 600px) {
.player-name {
top: 40px;
font-size: 0.7rem;
}
}
#playerName {
left: 20%;
}
#opponentName {
right: 20%;
}
.touch-controls {
position: absolute;
bottom: 20px;
left: 0;
width: 100%;
display: flex;
justify-content: space-between;
padding: 0 30px;
z-index: 5;
display: none;
}
@media (max-width: 600px) {
.touch-controls {
display: flex;
}
}
.touch-btn {
width: 70px;
height: 70px;
background: rgba(0, 255, 252, 0.2);
border: 2px solid #00fffc;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #00fffc;
font-size: 1.5rem;
user-select: none;
-webkit-user-select: none;
touch-action: manipulation;
}
@media (max-width: 400px) {
.touch-btn {
width: 60px;
height: 60px;
font-size: 1.2rem;
}
}
.orientation-warning {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #0a0a1a;
color: #00fffc;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 100;
display: none;
text-align: center;
padding: 20px;
}
.orientation-warning i {
font-size: 3rem;
margin-bottom: 20px;
color: #ff00e4;
}
.orientation-warning h2 {
font-size: 1.5rem;
margin-bottom: 15px;
}
.orientation-warning p {
font-size: 1rem;
max-width: 300px;
line-height: 1.5;
}
</style>
</head>
<body>
<div class="game-container">
<div class="center-line"></div>
<div class="paddle" id="playerPaddle"></div>
<div class="paddle" id="opponentPaddle" style="display: none;"></div>
<div class="paddle" id="aiPaddle"></div>
<div class="ball"></div>
<div class="score" id="playerScore">0</div>
<div class="score" id="opponentScore" style="display: none;">0</div>
<div class="score" id="aiScore">0</div>
<div class="player-name" id="playerName">YOU</div>
<div class="player-name" id="opponentName" style="display: none;">OPPONENT</div>
<div class="power-up-indicator" id="powerUpIndicator"></div>
<div class="touch-controls" id="touchControls">
<div class="touch-btn" id="upBtn"><i class="fas fa-arrow-up"></i></div>
<div class="touch-btn" id="downBtn"><i class="fas fa-arrow-down"></i></div>
</div>
<div class="game-over">
<h1>GAME OVER</h1>
<p id="gameResult"></p>
<button class="btn" id="restartBtn">PLAY AGAIN</button>
</div>
<div class="start-screen">
<h1>CYBER PONG EXPLOSION</h1>
<p>Experience the ultimate futuristic pong battle with explosive effects and power-ups!</p>
<div class="multiplayer-options">
<button class="btn" id="singlePlayerBtn">SINGLE PLAYER</button>
<button class="btn" id="multiplayerBtn">ONLINE MULTIPLAYER</button>
</div>
<div class="room-code-container" id="roomCodeContainer">
<p>Share this code with your friend:</p>
<div class="room-code" id="roomCode"></div>
<div class="waiting-message" id="waitingMessage">Waiting for opponent to join...</div>
</div>
<div class="instructions">
<p><i class="fas fa-arrow-up"></i> <i class="fas fa-arrow-down"></i> Move your paddle with UP/DOWN keys or touch controls</p>
<p>Collect power-ups to gain advantages!</p>
</div>
</div>
</div>
<div class="orientation-warning" id="orientationWarning">
<i class="fas fa-rotate"></i>
<h2>Please rotate your device</h2>
<p>For the best gaming experience, please play in landscape mode.</p>
</div>
<div class="connection-status" id="connectionStatus">
<div class="indicator disconnected"></div>
<span>Disconnected</span>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
// Game elements
const gameContainer = document.querySelector('.game-container');
const playerPaddle = document.getElementById('playerPaddle');
const aiPaddle = document.getElementById('aiPaddle');
const opponentPaddle = document.getElementById('opponentPaddle');
const ball = document.querySelector('.ball');
const playerScore = document.getElementById('playerScore');
const aiScore = document.getElementById('aiScore');
const opponentScore = document.getElementById('opponentScore');
const startScreen = document.querySelector('.start-screen');
const gameOverScreen = document.querySelector('.game-over');
const gameResult = document.getElementById('gameResult');
const singlePlayerBtn = document.getElementById('singlePlayerBtn');
const multiplayerBtn = document.getElementById('multiplayerBtn');
const startBtn = document.getElementById('startBtn');
const restartBtn = document.getElementById('restartBtn');
const powerUpIndicator = document.getElementById('powerUpIndicator');
const roomCodeContainer = document.getElementById('roomCodeContainer');
const roomCodeDisplay = document.getElementById('roomCode');
const waitingMessage = document.getElementById('waitingMessage');
const connectionStatus = document.getElementById('connectionStatus');
const playerNameDisplay = document.getElementById('playerName');
const opponentNameDisplay = document.getElementById('opponentName');
const touchControls = document.getElementById('touchControls');
const upBtn = document.getElementById('upBtn');
const downBtn = document.getElementById('downBtn');
const orientationWarning = document.getElementById('orientationWarning');
// Check if mobile device
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
// Game state
let gameRunning = false;
let isMultiplayer = false;
let isHost = false;
let roomCode = '';
let playerName = 'Player' + Math.floor(Math.random() * 1000);
let opponentName = 'Opponent';
let playerScoreValue = 0;
let aiScoreValue = 0;
let opponentScoreValue = 0;
let gameSpeed = 1;
let lastPowerUpTime = 0;
let activePowerUp = null;
let powerUpEndTime = 0;
let touchUpActive = false;
let touchDownActive = false;
// Ball properties
let ballX = 400;
let ballY = 250;
let ballSpeedX = 3 * gameSpeed; // Velocità iniziale più lenta
let ballSpeedY = 3 * gameSpeed; // Velocità iniziale più lenta
let ballSize = 20;
let speedIncreaseFactor = 1.05; // Fattore di incremento della velocità ad ogni rimbalzo
// Paddle properties
const paddleHeight = isMobile ? 80 : 100;
const paddleWidth = isMobile ? 12 : 15;
let playerPaddleY = 200;
let aiPaddleY = 200;
let opponentPaddleY = 200;
const paddleSpeed = 8 * gameSpeed;
// Game area dimensions
const gameWidth = gameContainer.clientWidth;
const gameHeight = gameContainer.clientHeight;
// WebSocket connection
let socket = null;
let reconnectAttempts = 0;
const maxReconnectAttempts = 5;
// Power-ups
const powerUps = [
{ type: 'speed', color: '#ff00e4', duration: 5000, effect: () => { gameSpeed = 1.5; } },
{ type: 'size', color: '#00ff00', duration: 7000, effect: () => { playerPaddle.style.height = (paddleHeight * 1.5) + 'px'; } },
{ type: 'freeze', color: '#00b4ff', duration: 3000, effect: () => {
if (isMultiplayer) {
opponentPaddle.style.opacity = '0.5';
} else {
aiPaddle.style.opacity = '0.5';
}
}}
];
// Initialize game
init();
function init() {
// Set up event listeners
singlePlayerBtn.addEventListener('click', () => {
isMultiplayer = false;
startGame();
});
multiplayerBtn.addEventListener('click', startMultiplayerGame);
restartBtn.addEventListener('click', startGame);
// Generate a random player name
playerName = 'Player' + Math.floor(Math.random() * 1000);
playerNameDisplay.textContent = playerName;
// Initialize connection status
updateConnectionStatus('disconnected');
// Set up touch controls
if (isMobile) {
touchControls.style.display = 'flex';
upBtn.addEventListener('touchstart', (e) => {
e.preventDefault();
touchUpActive = true;
});
upBtn.addEventListener('touchend', (e) => {
e.preventDefault();
touchUpActive = false;
});
downBtn.addEventListener('touchstart', (e) => {
e.preventDefault();
touchDownActive = true;
});
downBtn.addEventListener('touchend', (e) => {
e.preventDefault();
touchDownActive = false;
});
// Prevent scrolling when touching game area
gameContainer.addEventListener('touchmove', (e) => {
e.preventDefault();
}, { passive: false });
}
// Check orientation on mobile
if (isMobile) {
checkOrientation();
window.addEventListener('resize', checkOrientation);
window.addEventListener('orientationchange', checkOrientation);
}
}
// Check device orientation
function checkOrientation() {
if (isMobile) {
const isPortrait = window.innerHeight > window.innerWidth;
if (Portrait) {
orientationWarning.style.display = 'flex';
gameContainer.style.display = 'none';
} else {
orientationWarning.style.display = 'none';
gameContainer.style.display = 'block';
}
}
}
// Start multiplayer game
function startMultiplayerGame() {
isMultiplayer = true;
connectToServer();
// Show room code input
roomCodeContainer.style.display = 'flex';
waitingMessage.textContent = 'Connecting to server...';
}
// Connect to WebSocket server
function connectToServer() {
updateConnectionStatus('connecting');
// In a real implementation, you would connect to your actual WebSocket server
// For this example, we'll simulate a connection with a delay
setTimeout(() => {
// Simulate successful connection
updateConnectionStatus('connected');
// Generate a random room code
roomCode = generateRoomCode();
roomCodeDisplay.textContent = roomCode;
waitingMessage.textContent = 'Waiting for opponent to join...';
// Simulate opponent joining after a delay
setTimeout(() => {
opponentName = 'Opponent' + Math.floor(Math.random() * 1000);
opponentNameDisplay.textContent = opponentName;
waitingMessage.textContent = 'Opponent joined! Starting game...';
// Start the game after a short delay
setTimeout(() => {
startGame();
}, 1000);
}, 2000);
}, 1000);
}
// Generate a random room code
function generateRoomCode() {
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
let result = '';
for (let i = 0; i < 5; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
// Update connection status UI
function updateConnectionStatus(status) {
const indicator = connectionStatus.querySelector('.indicator');
const text = connectionStatus.querySelector('span');
indicator.className = 'indicator';
switch(status) {
case 'connected':
indicator.classList.add('connected');
text.textContent = 'Connected';
break;
case 'disconnected':
indicator.classList.add('disconnected');
text.textContent = 'Disconnected';
break;
case 'connecting':
indicator.classList.add('connecting');
text.textContent = 'Connecting...';
break;
}
}
// Start game
function startGame() {
// Reset scores
playerScoreValue = 0;
aiScoreValue = 0;
opponentScoreValue = 0;
playerScore.textContent = '0';
aiScore.textContent = '0';
opponentScore.textContent = '0';
// Show/hide appropriate elements based on game mode
if (isMultiplayer) {
aiPaddle.style.display = 'none';
opponentPaddle.style.display = 'block';
aiScore.style.display = 'none';
opponentScore.style.display = 'block';
opponentNameDisplay.style.display = 'block';
} else {
aiPaddle.style.display = 'block';
opponentPaddle.style.display = 'none';
aiScore.style.display = 'block';
opponentScore.style.display = 'none';
opponentNameDisplay.style.display = 'none';
}
// Reset positions
ballX = gameWidth / 2;
ballY = gameHeight / 2;
playerPaddleY = (gameHeight - paddleHeight) / 2;
aiPaddleY = (gameHeight - paddleHeight) / 2;
opponentPaddleY = (gameHeight - paddleHeight) / 2;
// Reset ball speed and direction (più lento all'inizio)
ballSpeedX = 3 * (Math.random() > 0.5 ? 1 : -1);
ballSpeedY = 3 * (Math.random() > 0.5 ? 1 : -1);
// Reset power-ups
clearPowerUps();
activePowerUp = null;
powerUpIndicator.style.display = 'none';
// Hide screens
startScreen.style.display = 'none';
roomCodeContainer.style.display = 'none';
gameOverScreen.style.display = 'none';
// Start game loop
gameRunning = true;
requestAnimationFrame(gameLoop);
}
// Game loop
function gameLoop(timestamp) {
if (!gameRunning) return;
update();
render();
// Check for power-up expiration
if (activePowerUp && timestamp >= powerUpEndTime) {
clearPowerUps();
}
// Spawn power-ups randomly
if (timestamp - lastPowerUpTime > 10000 && Math.random() < 0.01) {
spawnPowerUp();
lastPowerUpTime = timestamp;
}
// In multiplayer mode, send paddle position updates
if (isMultiplayer && socket && socket.readyState === WebSocket.OPEN) {
// Simulate sending data to server
const data = {
type: 'paddleUpdate',
y: playerPaddleY,
room: roomCode
};
// In a real implementation: socket.send(JSON.stringify(data));
}
requestAnimationFrame(gameLoop);
}
// Update game state
function update() {
// Move player paddle
if ((keys.ArrowUp || touchUpActive) && playerPaddleY > 0) {
playerPaddleY -= paddleSpeed;
}
if ((keys.ArrowDown || touchDownActive) && playerPaddleY < gameHeight - paddleHeight) {
playerPaddleY += paddleSpeed;
}
// AI or opponent paddle movement
if (isMultiplayer) {
// In a real multiplayer game, opponent paddle position would come from the server
// For this example, we'll just simulate some movement
if (Math.random() < 0.02) {
const moveDirection = Math.random() > 0.5 ? 1 : -1;
opponentPaddleY += paddleSpeed * 0.7 * moveDirection;
opponentPaddleY = Math.max(0, Math.min(gameHeight - paddleHeight, opponentPaddleY));
}
} else {
// AI paddle movement (simple tracking)
const aiPaddleCenter = aiPaddleY + paddleHeight / 2;
if (aiPaddleCenter < ballY - 15) {
aiPaddleY += paddleSpeed * 0.7 * gameSpeed;
} else if (aiPaddleCenter > ballY + 15) {
aiPaddleY -= paddleSpeed * 0.7 * gameSpeed;
}
// Keep AI paddle in bounds
aiPaddleY = Math.max(0, Math.min(gameHeight - paddleHeight, aiPaddleY));
}
// Move ball
ballX += ballSpeedX * gameSpeed;
ballY += ballSpeedY * gameSpeed;
// Ball collision with top and bottom walls
if (ballY <= 0 || ballY >= gameHeight - ballSize) {
ballSpeedY = -ballSpeedY;
createParticles(ballX, ballY, 10, '#ff00e4');
}
// Ball collision with player paddle
if (
ballX <= 20 + paddleWidth &&
ballY + ballSize >= playerPaddleY &&
ballY <= playerPaddleY + paddleHeight
) {
// Aumenta la velocità della pallina ad ogni rimbalzo
ballSpeedX = Math.abs(ballSpeedX) * speedIncreaseFactor;
// Add angle based on where ball hits paddle
const hitPosition = (ballY - playerPaddleY) / paddleHeight;
ballSpeedY = (hitPosition - 0.5) * 10;
createParticles(ballX, ballY, 15, '#00fffc');
// In multiplayer, notify server about the hit
if (isMultiplayer && socket) {
const data = {
type: 'ballHit',
x: ballX,
y: ballY,
speedX: ballSpeedX,
speedY: ballSpeedY,
room: roomCode
};
// In a real implementation: socket.send(JSON.stringify(data));
}
}
// Ball collision with opponent/AI paddle
const opponentPaddleToUse = isMultiplayer ? opponentPaddle : aiPaddle;
const opponentPaddleYToUse = isMultiplayer ? opponentPaddleY : aiPaddleY;
if (
ballX >= gameWidth - 20 - paddleWidth - ballSize &&
ballY + ballSize >= opponentPaddleYToUse &&
ballY <= opponentPaddleYToUse + paddleHeight
) {
// Aumenta la velocità della pallina ad ogni rimbalzo
ballSpeedX = -Math.abs(ballSpeedX) * speedIncreaseFactor;
// Add angle based on where ball hits paddle
const hitPosition = (ballY - opponentPaddleYToUse) / paddleHeight;
ballSpeedY = (hitPosition - 0.5) * 10;
createParticles(ballX, ballY, 15, '#ff00e4');
}
// Ball out of bounds (score)
if (ballX < 0) {
if (isMultiplayer) {
opponentScoreValue++;
opponentScore.textContent = opponentScoreValue;
if (socket) {
const data = {
type: 'score',
player: 'opponent',
room: roomCode
};
// In a real implementation: socket.send(JSON.stringify(data));
}
} else {
aiScoreValue++;
aiScore.textContent = aiScoreValue;
}
resetBall();
const winningScore = isMultiplayer ? 5 : 5;
if ((isMultiplayer && opponentScoreValue >= winningScore) ||
(!isMultiplayer && aiScoreValue >= winningScore)) {
endGame(false);
}
}
if (ballX > gameWidth) {
if (isMultiplayer) {
playerScoreValue++;
playerScore.textContent = playerScoreValue;
if (socket) {
const data = {
type: 'score',
player: 'player',
room: roomCode
};
// In a real implementation: socket.send(JSON.stringify(data));
}
} else {
playerScoreValue++;
playerScore.textContent = playerScoreValue;
}
resetBall();
const winningScore = isMultiplayer ? 5 : 5;
if (playerScoreValue >= winningScore) {
endGame(true);
}
}
// Update power-ups
document.querySelectorAll('.power-up').forEach(powerUp => {
const rect = powerUp.getBoundingClientRect();
const gameRect = gameContainer.getBoundingClientRect();
const powerUpX = rect.left - gameRect.left;
const powerUpY = rect.top - gameRect.top;
const powerUpSize = 20;
// Check collision with ball
if (
ballX + ballSize > powerUpX &&
ballX < powerUpX + powerUpSize &&
ballY + ballSize > powerUpY &&
ballY < powerUpY + powerUpSize
) {
const powerUpType = powerUp.classList.contains('speed') ? 'speed' :
powerUp.classList.contains('size') ? 'size' : 'freeze';
activatePowerUp(powerUpType);
powerUp.remove();
// In multiplayer, notify server about power-up collection
if (isMultiplayer && socket) {
const data = {
type: 'powerUp',
powerUpType: powerUpType,
room: roomCode
};
// In a real implementation: socket.send(JSON.stringify(data));
}
}
});
}
// Render game
function render() {
// Update paddle positions
playerPaddle.style.top = playerPaddleY + 'px';
if (isMultiplayer) {
opponentPaddle.style.top = opponentPaddleY + 'px';
} else {
aiPaddle.style.top = aiPaddleY + 'px';
}
// Update ball position
ball.style.left = ballX + 'px';
ball.style.top = ballY + 'px';
ball.style.width = ballSize + 'px';
ball.style.height = ballSize + 'px';
// Update particles
document.querySelectorAll('.particle').forEach(particle => {
const opacity = parseFloat(particle.style.opacity);
particle.style.opacity = opacity - 0.02;
if (opacity <= 0) {
particle.remove();
}
});
}
// Reset ball after scoring
function resetBall() {
ballX = gameWidth / 2;
ballY = gameHeight / 2;
// Resetta la velocità a quella iniziale più lenta
ballSpeedX = 3 * (Math.random() > 0.5 ? 1 : -1);
ballSpeedY = 3 * (Math.random() > 0.5 ? 1 : -1);
gameSpeed = 1;
// In multiplayer, notify server about ball reset
if (isMultiplayer && socket) {
const data = {
type: 'ballReset',
room: roomCode
};
// In a real implementation: socket.send(JSON.stringify(data));
}
}
// End game
function endGame(playerWon) {
gameRunning = false;
if (isMultiplayer) {
gameResult.textContent = playerWon ?
`YOU WIN! ${opponentName.toUpperCase()} WAS NO MATCH!` :
`DEFEAT! ${opponentName.toUpperCase()} TRIUMPHED!`;
// Notify server about game end
if (socket) {
const data = {
type: 'gameOver',
winner: playerWon ? playerName : opponentName,
room: roomCode
};
// In a real implementation: socket.send(JSON.stringify(data));
}
} else {
gameResult.textContent = playerWon ?
'YOU WIN! CYBER DOMINATION ACHIEVED!' :
'DEFEAT! THE AI HAS TRIUMPHED!';
}
gameOverScreen.style.display = 'flex';
}
// Create explosion particles
function createParticles(x, y, count, color) {
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;
particle.style.opacity = '1';
// Random direction and speed
const angle = Math.random() * Math.PI * 2;
const speed = Math.random() * 3 + 1;
const vx = Math.cos(angle) * speed;
const vy = Math.sin(angle) * speed;
gameContainer.appendChild(particle);
// Animate particle
let frame = 0;
const animateParticle = () => {
frame++;
const currentX = parseFloat(particle.style.left);
const currentY = parseFloat(particle.style.top);
particle.style.left = (currentX + vx) + 'px';
particle.style.top = (currentY + vy) + 'px';
if (frame < 30) {
requestAnimationFrame(animateParticle);
}
};
animateParticle();
}
}
// Spawn power-up
function spawnPowerUp() {
if (activePowerUp) return;
const powerUp = powerUps[Math.floor(Math.random() * powerUps.length)];
const powerUpElement = document.createElement('div');
powerUpElement.className = `power-up ${powerUp.type}`;
powerUpElement.style.left = (Math.random() * (gameWidth - 50) + 25) + 'px';
powerUpElement.style.top = (Math.random() * (gameHeight - 50) + 25) + 'px';
powerUpElement.style.backgroundColor = powerUp.color;
powerUpElement.style.boxShadow = `0 0 10px ${powerUp.color}`;
gameContainer.appendChild(powerUpElement);
// Remove power-up after 5 seconds if not collected
setTimeout(() => {
if (powerUpElement.parentNode) {
powerUpElement.remove();
}
}, 5000);
// In multiplayer, notify server about power-up spawn
if (isMultiplayer && socket) {
const rect = powerUpElement.getBoundingClientRect();
const gameRect = gameContainer.getBoundingClientRect();
const x = rect.left - gameRect.left;
const y = rect.top - gameRect.top;
const data = {
type: 'powerUpSpawn',
powerUpType: powerUp.type,
x: x,
y: y,
room: roomCode
};
// In a real implementation: socket.send(JSON.stringify(data));
}
}
// Activate power-up
function activatePowerUp(type) {
clearPowerUps();
const powerUp = powerUps.find(p => p.type === type);
if (!powerUp) return;
powerUp.effect();
activePowerUp = type;
powerUpEndTime = performance.now() + powerUp.duration;
// Show indicator
powerUpIndicator.textContent = `POWER UP: ${type.toUpperCase()}!`;
powerUpIndicator.style.color = powerUp.color;
powerUpIndicator.style.display = 'block';
powerUpIndicator.classList.add('glow');
// Remove indicator after duration
setTimeout(() => {
powerUpIndicator.style.display = 'none';
powerUpIndicator.classList.remove('glow');
}, powerUp.duration);
}
// Clear active power-ups
function clearPowerUps() {
if (!activePowerUp) return;
// Reset game speed
gameSpeed = 1;
// Reset paddle size
playerPaddle.style.height = paddleHeight + 'px';
// Reset opponent/AI paddle opacity
if (isMultiplayer) {
opponentPaddle.style.opacity = '1';
} else {
aiPaddle.style.opacity = '1';
}
activePowerUp = null;
}
// Handle WebSocket messages
function handleSocketMessage(event) {
try {
const data = JSON.parse(event.data);
switch(data.type) {
case 'paddleUpdate':
if (data.player !== playerName) {
opponentPaddleY = data.y;
}
break;
case 'ballUpdate':
ballX = data.x;
ballY = data.y;
ballSpeedX = data.speedX;
ballSpeedY = data.speedY;
break;
case 'powerUpSpawn':
// Spawn power-up at specified position
const powerUpElement = document.createElement('div');
powerUpElement.className = `power-up ${data.powerUpType}`;
powerUpElement.style.left = data.x + 'px';
powerUpElement.style.top = data.y + 'px';
powerUpElement.style.backgroundColor = powerUps.find(p => p.type === data.powerUpType).color;
powerUpElement.style.boxShadow = `0 0 10px ${powerUps.find(p => p.type === data.powerUpType).color}`;
gameContainer.appendChild(powerUpElement);
break;
case 'score':
if (data.player === 'player') {
playerScoreValue = data.score;
playerScore.textContent = playerScoreValue;
} else {
opponentScoreValue = data.score;
opponentScore.textContent = opponentScoreValue;
}
break;
case 'gameOver':
gameRunning = false;
gameResult.textContent = data.winner === playerName ?
'YOU WIN! CYBER DOMINATION ACHIEVED!' :
'DEFEAT! YOUR OPPONENT TRIUMPHED!';
gameOverScreen.style.display = 'flex';
break;
case 'playerJoined':
opponentName = data.playerName;
opponentNameDisplay.textContent = opponentName;
waitingMessage.textContent = `${opponentName} has joined! Starting game...`;
setTimeout(() => {
startGame();
}, 1000);
break;
}
} catch (e) {
console.error('Error processing message:', e);
}
}
// Keyboard input
const keys = {};
document.addEventListener('keydown', (e) => {
keys[e.key] = true;
});
document.addEventListener('keyup', (e) => {
keys[e.key] = false;
});
});
</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 <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body>
</html>