|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Medieval Pong: Siege of the Castle</title> |
|
<style> |
|
@import url('https://fonts.googleapis.com/css2?family=MedievalSharp&display=swap'); |
|
|
|
body { |
|
margin: 0; |
|
padding: 0; |
|
background-color: #111; |
|
overflow: hidden; |
|
font-family: 'MedievalSharp', cursive; |
|
color: #d4af37; |
|
text-align: center; |
|
} |
|
|
|
#game-container { |
|
position: relative; |
|
width: 800px; |
|
height: 500px; |
|
margin: 20px auto; |
|
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="800" height="500" viewBox="0 0 800 500"><rect width="800" height="500" fill="%23101010"/><rect x="50" y="100" width="700" height="300" fill="%23222" stroke="%23d4af37" stroke-width="2"/><rect x="300" y="150" width="200" height="200" fill="%23333" stroke="%23d4af37" stroke-width="2"/><rect x="350" y="200" width="100" height="150" fill="%23555" stroke="%23d4af37" stroke-width="2"/><rect x="375" y="200" width="50" height="30" fill="%23d4af37"/><polygon points="400,50 450,150 350,150" fill="%23777" stroke="%23d4af37" stroke-width="2"/><circle cx="400" cy="250" r="10" fill="%23d4af37"/><line x1="400" y1="260" x2="400" y2="350" stroke="%23d4af37" stroke-width="2"/><line x1="370" y1="290" x2="430" y2="290" stroke="%23d4af37" stroke-width="2"/></svg>'); |
|
border: 3px solid #d4af37; |
|
box-shadow: 0 0 20px rgba(212, 175, 55, 0.5); |
|
} |
|
|
|
#scoreboard { |
|
display: flex; |
|
justify-content: space-between; |
|
padding: 10px 50px; |
|
font-size: 24px; |
|
text-shadow: 2px 2px 4px #000; |
|
} |
|
|
|
.paddle { |
|
position: absolute; |
|
width: 20px; |
|
height: 100px; |
|
background: linear-gradient(to bottom, #8B0000, #B22222, #8B0000); |
|
border: 2px solid #d4af37; |
|
border-radius: 5px; |
|
box-shadow: 0 0 15px #FF4500; |
|
} |
|
|
|
#left-paddle { |
|
left: 30px; |
|
top: 200px; |
|
} |
|
|
|
#right-paddle { |
|
right: 30px; |
|
top: 200px; |
|
} |
|
|
|
.flame { |
|
position: absolute; |
|
width: 10px; |
|
height: 20px; |
|
background: radial-gradient(ellipse at center, #FF8C00 0%, #FF4500 50%, transparent 70%); |
|
border-radius: 50%; |
|
animation: flicker 0.3s infinite alternate; |
|
} |
|
|
|
@keyframes flicker { |
|
0% { opacity: 0.7; transform: scaleY(0.9); } |
|
100% { opacity: 1; transform: scaleY(1.1); } |
|
} |
|
|
|
#ball { |
|
position: absolute; |
|
width: 20px; |
|
height: 20px; |
|
background: radial-gradient(circle at 30% 30%, #FFF, #FFD700, #B22222); |
|
border-radius: 50%; |
|
box-shadow: 0 0 10px #FF4500; |
|
} |
|
|
|
.explosion { |
|
position: absolute; |
|
width: 40px; |
|
height: 40px; |
|
border-radius: 50%; |
|
background: radial-gradient(circle, rgba(255,69,0,0.8) 0%, rgba(255,140,0,0.6) 40%, rgba(255,215,0,0) 70%); |
|
animation: explode 0.5s forwards; |
|
pointer-events: none; |
|
} |
|
|
|
@keyframes explode { |
|
0% { transform: scale(0.1); opacity: 1; } |
|
100% { transform: scale(2); opacity: 0; } |
|
} |
|
|
|
.particle { |
|
position: absolute; |
|
width: 6px; |
|
height: 6px; |
|
background-color: #FF8C00; |
|
border-radius: 50%; |
|
animation: particle-fly 1s forwards; |
|
pointer-events: none; |
|
} |
|
|
|
@keyframes particle-fly { |
|
0% { transform: translate(0, 0); opacity: 1; } |
|
100% { transform: translate(var(--tx), var(--ty)); opacity: 0; } |
|
} |
|
|
|
#start-screen, #game-over { |
|
position: absolute; |
|
top: 0; |
|
left: 0; |
|
width: 100%; |
|
height: 100%; |
|
background-color: rgba(0, 0, 0, 0.7); |
|
display: flex; |
|
flex-direction: column; |
|
justify-content: center; |
|
align-items: center; |
|
z-index: 10; |
|
} |
|
|
|
#game-over { |
|
display: none; |
|
} |
|
|
|
h1 { |
|
font-size: 48px; |
|
margin-bottom: 20px; |
|
text-shadow: 3px 3px 5px #000; |
|
} |
|
|
|
button { |
|
padding: 15px 30px; |
|
font-family: 'MedievalSharp', cursive; |
|
font-size: 20px; |
|
background: linear-gradient(to bottom, #8B0000, #B22222); |
|
color: #d4af37; |
|
border: 2px solid #d4af37; |
|
border-radius: 5px; |
|
cursor: pointer; |
|
transition: all 0.3s; |
|
} |
|
|
|
button:hover { |
|
transform: scale(1.05); |
|
box-shadow: 0 0 15px #FF4500; |
|
} |
|
|
|
#instructions { |
|
margin-top: 20px; |
|
font-size: 18px; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div id="game-container"> |
|
<div id="scoreboard"> |
|
<div id="player-score">Player: 0</div> |
|
<div id="ai-score">AI: 0</div> |
|
</div> |
|
|
|
<div id="left-paddle" class="paddle"></div> |
|
<div id="right-paddle" class="paddle"></div> |
|
<div id="ball"></div> |
|
|
|
<div id="start-screen"> |
|
<h1>Medieval Pong: Siege of the Castle</h1> |
|
<button id="start-button">Begin the Siege!</button> |
|
<div id="instructions"> |
|
Defend your castle! Use W/S keys to move your flaming paddle.<br> |
|
Each hit creates an explosive impact. First to 5 wins! |
|
</div> |
|
</div> |
|
|
|
<div id="game-over"> |
|
<h1 id="result-message">Victory!</h1> |
|
<button id="restart-button">Fight Again!</button> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
|
|
const gameContainer = document.getElementById('game-container'); |
|
const leftPaddle = document.getElementById('left-paddle'); |
|
const rightPaddle = document.getElementById('right-paddle'); |
|
const ball = document.getElementById('ball'); |
|
const playerScoreDisplay = document.getElementById('player-score'); |
|
const aiScoreDisplay = document.getElementById('ai-score'); |
|
const startScreen = document.getElementById('start-screen'); |
|
const gameOverScreen = document.getElementById('game-over'); |
|
const resultMessage = document.getElementById('result-message'); |
|
const startButton = document.getElementById('start-button'); |
|
const restartButton = document.getElementById('restart-button'); |
|
|
|
|
|
let gameRunning = false; |
|
let playerScore = 0; |
|
let aiScore = 0; |
|
let ballX = 400; |
|
let ballY = 250; |
|
let ballSpeedX = 4; |
|
let ballSpeedY = 4; |
|
let leftPaddleY = 200; |
|
let rightPaddleY = 200; |
|
const paddleHeight = 100; |
|
const paddleWidth = 20; |
|
const ballSize = 20; |
|
const gameWidth = 800; |
|
const gameHeight = 500; |
|
|
|
|
|
const keys = { |
|
w: false, |
|
s: false |
|
}; |
|
|
|
|
|
function addFlames() { |
|
|
|
for (let i = 0; i < 3; i++) { |
|
const flame = document.createElement('div'); |
|
flame.className = 'flame'; |
|
flame.style.left = '-10px'; |
|
flame.style.top = `${20 + i * 30}px`; |
|
leftPaddle.appendChild(flame); |
|
} |
|
|
|
|
|
for (let i = 0; i < 3; i++) { |
|
const flame = document.createElement('div'); |
|
flame.className = 'flame'; |
|
flame.style.right = '-10px'; |
|
flame.style.top = `${20 + i * 30}px`; |
|
rightPaddle.appendChild(flame); |
|
} |
|
} |
|
|
|
|
|
function createExplosion(x, y) { |
|
const explosion = document.createElement('div'); |
|
explosion.className = 'explosion'; |
|
explosion.style.left = `${x - 20}px`; |
|
explosion.style.top = `${y - 20}px`; |
|
gameContainer.appendChild(explosion); |
|
|
|
|
|
for (let i = 0; i < 10; i++) { |
|
const particle = document.createElement('div'); |
|
particle.className = 'particle'; |
|
particle.style.left = `${x - 3}px`; |
|
particle.style.top = `${y - 3}px`; |
|
|
|
|
|
const angle = Math.random() * Math.PI * 2; |
|
const speed = 2 + Math.random() * 5; |
|
const tx = Math.cos(angle) * speed * 30; |
|
const ty = Math.sin(angle) * speed * 30; |
|
|
|
particle.style.setProperty('--tx', `${tx}px`); |
|
particle.style.setProperty('--ty', `${ty}px`); |
|
|
|
gameContainer.appendChild(particle); |
|
|
|
|
|
setTimeout(() => { |
|
particle.remove(); |
|
}, 1000); |
|
} |
|
|
|
|
|
setTimeout(() => { |
|
explosion.remove(); |
|
}, 500); |
|
} |
|
|
|
|
|
function update() { |
|
if (!gameRunning) return; |
|
|
|
|
|
if (keys.w && leftPaddleY > 0) { |
|
leftPaddleY -= 5; |
|
} |
|
if (keys.s && leftPaddleY < gameHeight - paddleHeight) { |
|
leftPaddleY += 5; |
|
} |
|
|
|
|
|
const paddleCenter = rightPaddleY + paddleHeight / 2; |
|
if (paddleCenter < ballY - 10) { |
|
rightPaddleY += 4; |
|
} else if (paddleCenter > ballY + 10) { |
|
rightPaddleY -= 4; |
|
} |
|
|
|
|
|
if (rightPaddleY < 0) rightPaddleY = 0; |
|
if (rightPaddleY > gameHeight - paddleHeight) rightPaddleY = gameHeight - paddleHeight; |
|
|
|
|
|
leftPaddle.style.top = `${leftPaddleY}px`; |
|
rightPaddle.style.top = `${rightPaddleY}px`; |
|
|
|
|
|
ballX += ballSpeedX; |
|
ballY += ballSpeedY; |
|
|
|
|
|
if (ballY <= 0 || ballY >= gameHeight - ballSize) { |
|
ballSpeedY = -ballSpeedY; |
|
createExplosion(ballX, ballY <= 0 ? ballSize : gameHeight - ballSize); |
|
} |
|
|
|
|
|
if ( |
|
ballX <= 30 + paddleWidth && |
|
ballY + ballSize >= leftPaddleY && |
|
ballY <= leftPaddleY + paddleHeight |
|
) { |
|
ballSpeedX = -ballSpeedX * 1.1; |
|
ballSpeedY += (ballY - (leftPaddleY + paddleHeight / 2)) * 0.05; |
|
createExplosion(30 + paddleWidth, ballY); |
|
} |
|
|
|
if ( |
|
ballX >= gameWidth - 30 - paddleWidth - ballSize && |
|
ballY + ballSize >= rightPaddleY && |
|
ballY <= rightPaddleY + paddleHeight |
|
) { |
|
ballSpeedX = -ballSpeedX * 1.1; |
|
ballSpeedY += (ballY - (rightPaddleY + paddleHeight / 2)) * 0.05; |
|
createExplosion(gameWidth - 30 - paddleWidth, ballY); |
|
} |
|
|
|
|
|
if (ballX < 0) { |
|
aiScore++; |
|
aiScoreDisplay.textContent = `AI: ${aiScore}`; |
|
resetBall(); |
|
if (aiScore >= 5) endGame(false); |
|
} |
|
|
|
if (ballX > gameWidth) { |
|
playerScore++; |
|
playerScoreDisplay.textContent = `Player: ${playerScore}`; |
|
resetBall(); |
|
if (playerScore >= 5) endGame(true); |
|
} |
|
|
|
|
|
ball.style.left = `${ballX}px`; |
|
ball.style.top = `${ballY}px`; |
|
|
|
|
|
requestAnimationFrame(update); |
|
} |
|
|
|
|
|
function resetBall() { |
|
ballX = gameWidth / 2 - ballSize / 2; |
|
ballY = gameHeight / 2 - ballSize / 2; |
|
ballSpeedX = (Math.random() > 0.5 ? 1 : -1) * 4; |
|
ballSpeedY = (Math.random() * 4 - 2); |
|
|
|
|
|
ball.style.left = `${ballX}px`; |
|
ball.style.top = `${ballY}px`; |
|
|
|
|
|
gameRunning = false; |
|
setTimeout(() => { |
|
gameRunning = true; |
|
update(); |
|
}, 1000); |
|
} |
|
|
|
|
|
function endGame(playerWon) { |
|
gameRunning = false; |
|
resultMessage.textContent = playerWon ? "Victory! The Castle Stands!" : "Defeat! The Castle Burns!"; |
|
gameOverScreen.style.display = "flex"; |
|
} |
|
|
|
|
|
function startGame() { |
|
playerScore = 0; |
|
aiScore = 0; |
|
playerScoreDisplay.textContent = "Player: 0"; |
|
aiScoreDisplay.textContent = "AI: 0"; |
|
startScreen.style.display = "none"; |
|
gameOverScreen.style.display = "none"; |
|
resetBall(); |
|
gameRunning = true; |
|
update(); |
|
} |
|
|
|
|
|
document.addEventListener('keydown', (e) => { |
|
if (e.key.toLowerCase() === 'w') keys.w = true; |
|
if (e.key.toLowerCase() === 's') keys.s = true; |
|
}); |
|
|
|
document.addEventListener('keyup', (e) => { |
|
if (e.key.toLowerCase() === 'w') keys.w = false; |
|
if (e.key.toLowerCase() === 's') keys.s = false; |
|
}); |
|
|
|
startButton.addEventListener('click', startGame); |
|
restartButton.addEventListener('click', startGame); |
|
|
|
|
|
addFlames(); |
|
</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> |