Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Castlevania Arkanoid</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
<style> | |
@import url('https://fonts.googleapis.com/css2?family=Cinzel+Decorative:wght@400;700;900&family=MedievalSharp&display=swap'); | |
body { | |
font-family: 'Cinzel Decorative', cursive; | |
background-color: #000; | |
overflow: hidden; | |
touch-action: none; | |
user-select: none; | |
} | |
#gameCanvas { | |
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400" viewBox="0 0 400 400"><rect width="400" height="400" fill="%23000"/><path d="M0,0 L400,400 M400,0 L0,400" stroke="%231a0a0a" stroke-width="1"/></svg>'); | |
box-shadow: 0 0 30px rgba(186, 0, 0, 0.5); | |
border: 8px solid #3a1a1a; | |
border-image: linear-gradient(45deg, #3a1a1a, #5a2a2a, #3a1a1a) 1; | |
} | |
.gothic-text { | |
font-family: 'MedievalSharp', cursive; | |
text-shadow: 0 0 10px rgba(255, 0, 0, 0.7); | |
} | |
.blood-red { | |
background: linear-gradient(135deg, #5a0000, #3a0000); | |
} | |
.vampire-button { | |
background: linear-gradient(135deg, #5a0000, #3a0000); | |
border: 2px solid #8a0000; | |
box-shadow: 0 0 15px rgba(186, 0, 0, 0.7); | |
transition: all 0.3s; | |
} | |
.vampire-button:hover { | |
background: linear-gradient(135deg, #7a0000, #5a0000); | |
box-shadow: 0 0 25px rgba(255, 0, 0, 0.9); | |
transform: translateY(-2px); | |
} | |
.candle-flicker { | |
animation: flicker 3s infinite alternate; | |
} | |
@keyframes flicker { | |
0%, 100% { opacity: 0.8; } | |
25% { opacity: 1; } | |
50% { opacity: 0.7; } | |
75% { opacity: 0.9; } | |
} | |
.gothic-border { | |
border: 4px solid transparent; | |
border-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><path d="M0,0 L12,0 L12,12 L0,12 Z" fill="none" stroke="%238a0000" stroke-width="2" stroke-dasharray="4,2"/></svg>') 1; | |
} | |
.skull-icon { | |
filter: drop-shadow(0 0 5px rgba(255, 0, 0, 0.7)); | |
} | |
</style> | |
</head> | |
<body class="flex flex-col items-center justify-center min-h-screen bg-black text-red-600"> | |
<div class="absolute top-0 left-0 w-full h-full opacity-20 pointer-events-none" style="background: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"100\" height=\"100\" viewBox=\"0 0 100 100\"><path d=\"M20,20 L80,20 L80,80 L20,80 Z M30,30 L70,30 L70,70 L30,70 Z\" fill=\"none\" stroke=\"%238a0000\" stroke-width=\"1\"/></svg>');"></div> | |
<h1 class="text-5xl md:text-6xl font-bold mb-4 gothic-text candle-flicker"> | |
<i class="fas fa-skull skull-icon mr-2"></i> CASTLEVANIA ARKANOID | |
</h1> | |
<div class="relative mb-8"> | |
<canvas id="gameCanvas" width="800" height="600" class="rounded-lg"></canvas> | |
<div id="startScreen" class="absolute inset-0 flex flex-col items-center justify-center bg-black bg-opacity-80 rounded-lg"> | |
<div class="text-center p-8 gothic-border bg-black bg-opacity-90 max-w-md"> | |
<h2 class="text-3xl font-bold mb-6 gothic-text candle-flicker">THE NIGHT WALKER'S CHALLENGE</h2> | |
<p class="mb-6 text-gray-300">Break the cursed bricks before the night creatures overwhelm you!</p> | |
<button id="startButton" class="px-8 py-3 rounded-lg text-xl font-bold gothic-text vampire-button"> | |
<i class="fas fa-play mr-2"></i> BEGIN THE HUNT | |
</button> | |
</div> | |
</div> | |
<div id="gameOverScreen" class="absolute inset-0 hidden flex-col items-center justify-center bg-black bg-opacity-80 rounded-lg"> | |
<div class="text-center p-8 gothic-border bg-black bg-opacity-90 max-w-md"> | |
<h2 class="text-4xl font-bold mb-6 gothic-text text-red-500">YOU HAVE DIED</h2> | |
<p id="finalScore" class="text-2xl mb-6">Score: 0</p> | |
<button id="restartButton" class="px-8 py-3 rounded-lg text-xl font-bold gothic-text vampire-button"> | |
<i class="fas fa-redo mr-2"></i> TRY AGAIN | |
</button> | |
</div> | |
</div> | |
</div> | |
<div class="flex justify-between w-full max-w-2xl px-4 mb-8"> | |
<div class="bg-black bg-opacity-70 px-6 py-3 rounded-lg gothic-border"> | |
<h3 class="text-xl gothic-text mb-1">SCORE</h3> | |
<p id="scoreDisplay" class="text-3xl font-bold">0</p> | |
</div> | |
<div class="bg-black bg-opacity-70 px-6 py-3 rounded-lg gothic-border"> | |
<h3 class="text-xl gothic-text mb-1">LIVES</h3> | |
<p id="livesDisplay" class="text-3xl font-bold">3</p> | |
</div> | |
<div class="bg-black bg-opacity-70 px-6 py-3 rounded-lg gothic-border"> | |
<h3 class="text-xl gothic-text mb-1">LEVEL</h3> | |
<p id="levelDisplay" class="text-3xl font-bold">1</p> | |
</div> | |
</div> | |
<div class="absolute bottom-4 right-4 text-xs text-gray-600"> | |
<p>Beware the creatures of the night...</p> | |
</div> | |
<script> | |
document.addEventListener('DOMContentLoaded', () => { | |
const canvas = document.getElementById('gameCanvas'); | |
const ctx = canvas.getContext('2d'); | |
const startScreen = document.getElementById('startScreen'); | |
const gameOverScreen = document.getElementById('gameOverScreen'); | |
const startButton = document.getElementById('startButton'); | |
const restartButton = document.getElementById('restartButton'); | |
const scoreDisplay = document.getElementById('scoreDisplay'); | |
const livesDisplay = document.getElementById('livesDisplay'); | |
const levelDisplay = document.getElementById('levelDisplay'); | |
const finalScore = document.getElementById('finalScore'); | |
// Game variables | |
let gameRunning = false; | |
let score = 0; | |
let lives = 3; | |
let level = 1; | |
let bricks = []; | |
let brickRowCount = 5; | |
let brickColumnCount = 10; | |
let brickWidth = 75; | |
let brickHeight = 20; | |
let brickPadding = 10; | |
let brickOffsetTop = 60; | |
let brickOffsetLeft = 30; | |
// Ball variables | |
let ballRadius = 10; | |
let x = canvas.width / 2; | |
let y = canvas.height - 30; | |
let dx = 4; | |
let dy = -4; | |
// Paddle variables | |
let paddleHeight = 15; | |
let paddleWidth = 100; | |
let paddleX = (canvas.width - paddleWidth) / 2; | |
let rightPressed = false; | |
let leftPressed = false; | |
// Special effects | |
let particles = []; | |
let lastTime = 0; | |
// Initialize bricks | |
function initBricks() { | |
bricks = []; | |
for (let c = 0; c < brickColumnCount; c++) { | |
bricks[c] = []; | |
for (let r = 0; r < brickRowCount; r++) { | |
const brickX = c * (brickWidth + brickPadding) + brickOffsetLeft; | |
const brickY = r * (brickHeight + brickPadding) + brickOffsetTop; | |
const brickHealth = Math.floor(Math.random() * 3) + 1; // 1-3 hits to break | |
// Different colors based on health | |
let brickColor; | |
if (brickHealth === 1) brickColor = '#5a0000'; // Dark red | |
else if (brickHealth === 2) brickColor = '#8a0000'; // Medium red | |
else brickColor = '#ba0000'; // Bright red | |
bricks[c][r] = { x: brickX, y: brickY, health: brickHealth, color: brickColor, visible: true }; | |
} | |
} | |
} | |
// Draw bricks | |
function drawBricks() { | |
for (let c = 0; c < brickColumnCount; c++) { | |
for (let r = 0; r < brickRowCount; r++) { | |
if (bricks[c][r].visible) { | |
const brick = bricks[c][r]; | |
// Draw brick with gothic style | |
ctx.beginPath(); | |
ctx.rect(brick.x, brick.y, brickWidth, brickHeight); | |
ctx.fillStyle = brick.color; | |
ctx.fill(); | |
ctx.lineWidth = 2; | |
ctx.strokeStyle = '#3a0000'; | |
ctx.stroke(); | |
// Add gothic cross pattern | |
ctx.beginPath(); | |
ctx.moveTo(brick.x + 5, brick.y + 5); | |
ctx.lineTo(brick.x + brickWidth - 5, brick.y + brickHeight - 5); | |
ctx.moveTo(brick.x + brickWidth - 5, brick.y + 5); | |
ctx.lineTo(brick.x + 5, brick.y + brickHeight - 5); | |
ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)'; | |
ctx.stroke(); | |
// Add health indicator (skull icons) | |
if (brick.health > 1) { | |
const skulls = brick.health; | |
const spacing = brickWidth / (skulls + 1); | |
for (let i = 1; i <= skulls; i++) { | |
const skullX = brick.x + (spacing * i) - 4; | |
const skullY = brick.y + brickHeight / 2 - 4; | |
ctx.beginPath(); | |
ctx.arc(skullX + 4, skullY + 4, 3, 0, Math.PI * 2); | |
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)'; | |
ctx.fill(); | |
ctx.beginPath(); | |
ctx.arc(skullX + 4, skullY + 4, 5, 0, Math.PI, false); | |
ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)'; | |
ctx.stroke(); | |
} | |
} | |
} | |
} | |
} | |
} | |
// Draw ball | |
function drawBall() { | |
ctx.beginPath(); | |
ctx.arc(x, y, ballRadius, 0, Math.PI * 2); | |
// Create a glowing red ball effect | |
const gradient = ctx.createRadialGradient( | |
x, y, ballRadius / 2, | |
x, y, ballRadius | |
); | |
gradient.addColorStop(0, '#ff0000'); | |
gradient.addColorStop(1, '#8a0000'); | |
ctx.fillStyle = gradient; | |
ctx.fill(); | |
// Add inner glow | |
ctx.beginPath(); | |
ctx.arc(x, y, ballRadius / 2, 0, Math.PI * 2); | |
ctx.fillStyle = 'rgba(255, 150, 150, 0.5)'; | |
ctx.fill(); | |
// Add outer glow | |
ctx.beginPath(); | |
ctx.arc(x, y, ballRadius + 3, 0, Math.PI * 2); | |
const glowGradient = ctx.createRadialGradient( | |
x, y, ballRadius, | |
x, y, ballRadius + 3 | |
); | |
glowGradient.addColorStop(0, 'rgba(255, 0, 0, 0.7)'); | |
glowGradient.addColorStop(1, 'rgba(255, 0, 0, 0)'); | |
ctx.fillStyle = glowGradient; | |
ctx.fill(); | |
} | |
// Draw paddle | |
function drawPaddle() { | |
// Main paddle | |
ctx.beginPath(); | |
ctx.rect(paddleX, canvas.height - paddleHeight, paddleWidth, paddleHeight); | |
// Gradient for paddle | |
const gradient = ctx.createLinearGradient( | |
paddleX, canvas.height - paddleHeight, | |
paddleX, canvas.height | |
); | |
gradient.addColorStop(0, '#3a1a1a'); | |
gradient.addColorStop(1, '#5a2a2a'); | |
ctx.fillStyle = gradient; | |
ctx.fill(); | |
ctx.strokeStyle = '#8a0000'; | |
ctx.lineWidth = 2; | |
ctx.stroke(); | |
// Add gothic details | |
const segmentWidth = paddleWidth / 5; | |
for (let i = 1; i < 5; i++) { | |
ctx.beginPath(); | |
ctx.moveTo(paddleX + (segmentWidth * i), canvas.height - paddleHeight); | |
ctx.lineTo(paddleX + (segmentWidth * i), canvas.height); | |
ctx.strokeStyle = 'rgba(186, 0, 0, 0.5)'; | |
ctx.stroke(); | |
} | |
// Add spikes at the ends | |
ctx.beginPath(); | |
ctx.moveTo(paddleX, canvas.height - paddleHeight); | |
ctx.lineTo(paddleX - 5, canvas.height - paddleHeight - 10); | |
ctx.lineTo(paddleX + 5, canvas.height - paddleHeight - 10); | |
ctx.closePath(); | |
ctx.fillStyle = '#8a0000'; | |
ctx.fill(); | |
ctx.beginPath(); | |
ctx.moveTo(paddleX + paddleWidth, canvas.height - paddleHeight); | |
ctx.lineTo(paddleX + paddleWidth + 5, canvas.height - paddleHeight - 10); | |
ctx.lineTo(paddleX + paddleWidth - 5, canvas.height - paddleHeight - 10); | |
ctx.closePath(); | |
ctx.fillStyle = '#8a0000'; | |
ctx.fill(); | |
} | |
// Draw particles | |
function drawParticles() { | |
for (let i = 0; i < particles.length; i++) { | |
const p = particles[i]; | |
ctx.beginPath(); | |
ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2); | |
const alpha = p.lifetime / 100; | |
ctx.fillStyle = `rgba(255, ${Math.floor(100 + Math.random() * 155)}, 0, ${alpha})`; | |
ctx.fill(); | |
p.x += p.vx; | |
p.y += p.vy; | |
p.lifetime--; | |
if (p.lifetime <= 0) { | |
particles.splice(i, 1); | |
i--; | |
} | |
} | |
} | |
// Create particles | |
function createParticles(x, y, count) { | |
for (let i = 0; i < count; i++) { | |
particles.push({ | |
x: x, | |
y: y, | |
radius: Math.random() * 3 + 1, | |
vx: (Math.random() - 0.5) * 5, | |
vy: (Math.random() - 0.5) * 5, | |
lifetime: Math.floor(Math.random() * 30) + 20 | |
}); | |
} | |
} | |
// Collision detection | |
function collisionDetection() { | |
for (let c = 0; c < brickColumnCount; c++) { | |
for (let r = 0; r < brickRowCount; r++) { | |
const brick = bricks[c][r]; | |
if (brick.visible) { | |
if ( | |
x > brick.x && | |
x < brick.x + brickWidth && | |
y > brick.y && | |
y < brick.y + brickHeight | |
) { | |
dy = -dy; | |
brick.health--; | |
// Create particles on hit | |
createParticles( | |
brick.x + brickWidth / 2, | |
brick.y + brickHeight / 2, | |
10 | |
); | |
if (brick.health <= 0) { | |
brick.visible = false; | |
score += 10 * level; | |
scoreDisplay.textContent = score; | |
// Check if all bricks are cleared | |
if (checkLevelComplete()) { | |
levelUp(); | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
// Check if level is complete | |
function checkLevelComplete() { | |
for (let c = 0; c < brickColumnCount; c++) { | |
for (let r = 0; r < brickRowCount; r++) { | |
if (bricks[c][r].visible) { | |
return false; | |
} | |
} | |
} | |
return true; | |
} | |
// Level up | |
function levelUp() { | |
level++; | |
levelDisplay.textContent = level; | |
// Increase difficulty | |
brickRowCount = Math.min(8, brickRowCount + 1); | |
dx *= 1.1; | |
dy *= 1.1; | |
// Reset ball position | |
x = canvas.width / 2; | |
y = canvas.height - 30; | |
// Create new bricks | |
initBricks(); | |
// Show level up message | |
const levelUpDiv = document.createElement('div'); | |
levelUpDiv.className = 'absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-4xl font-bold gothic-text text-red-500 bg-black bg-opacity-80 px-8 py-4 rounded-lg'; | |
levelUpDiv.textContent = `LEVEL ${level}`; | |
document.body.appendChild(levelUpDiv); | |
setTimeout(() => { | |
levelUpDiv.style.opacity = '0'; | |
setTimeout(() => { | |
document.body.removeChild(levelUpDiv); | |
}, 500); | |
}, 1500); | |
} | |
// Game over | |
function gameOver() { | |
gameRunning = false; | |
finalScore.textContent = `Score: ${score}`; | |
gameOverScreen.classList.remove('hidden'); | |
gameOverScreen.classList.add('flex'); | |
} | |
// Reset game | |
function resetGame() { | |
score = 0; | |
lives = 3; | |
level = 1; | |
scoreDisplay.textContent = score; | |
livesDisplay.textContent = lives; | |
levelDisplay.textContent = level; | |
x = canvas.width / 2; | |
y = canvas.height - 30; | |
dx = 4; | |
dy = -4; | |
paddleX = (canvas.width - paddleWidth) / 2; | |
brickRowCount = 5; | |
initBricks(); | |
gameOverScreen.classList.add('hidden'); | |
gameOverScreen.classList.remove('flex'); | |
} | |
// Main draw function | |
function draw() { | |
if (!gameRunning) return; | |
// Clear canvas with dark translucent overlay for motion blur effect | |
ctx.fillStyle = 'rgba(0, 0, 0, 0.2)'; | |
ctx.fillRect(0, 0, canvas.width, canvas.height); | |
// Draw game elements | |
drawBricks(); | |
drawBall(); | |
drawPaddle(); | |
drawParticles(); | |
// Collision detection | |
collisionDetection(); | |
// Wall collision | |
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) { | |
dx = -dx; | |
createParticles(x, y, 5); | |
} | |
if (y + dy < ballRadius) { | |
dy = -dy; | |
createParticles(x, y, 5); | |
} else if (y + dy > canvas.height - ballRadius) { | |
// Paddle collision | |
if (x > paddleX && x < paddleX + paddleWidth) { | |
// Calculate angle based on where ball hits paddle | |
const hitPosition = (x - paddleX) / paddleWidth; | |
const angle = (hitPosition - 0.5) * Math.PI / 3; // -30 to 30 degrees | |
// Calculate new direction | |
const speed = Math.sqrt(dx * dx + dy * dy) * 1.05; // Slightly increase speed | |
dy = -Math.abs(Math.cos(angle) * speed); | |
dx = Math.sin(angle) * speed; | |
createParticles(x, y, 10); | |
} else { | |
// Ball missed paddle | |
lives--; | |
livesDisplay.textContent = lives; | |
if (lives <= 0) { | |
gameOver(); | |
} else { | |
// Reset ball position | |
x = canvas.width / 2; | |
y = canvas.height - 30; | |
dx = 4; | |
dy = -4; | |
paddleX = (canvas.width - paddleWidth) / 2; | |
} | |
} | |
} | |
// Paddle movement | |
if (rightPressed && paddleX < canvas.width - paddleWidth) { | |
paddleX += 7; | |
} else if (leftPressed && paddleX > 0) { | |
paddleX -= 7; | |
} | |
// Move ball | |
x += dx; | |
y += dy; | |
requestAnimationFrame(draw); | |
} | |
// Event listeners | |
document.addEventListener('keydown', keyDownHandler); | |
document.addEventListener('keyup', keyUpHandler); | |
canvas.addEventListener('mousemove', mouseMoveHandler); | |
canvas.addEventListener('touchmove', touchMoveHandler, { passive: false }); | |
function keyDownHandler(e) { | |
if (e.key === 'Right' || e.key === 'ArrowRight') { | |
rightPressed = true; | |
} else if (e.key === 'Left' || e.key === 'ArrowLeft') { | |
leftPressed = true; | |
} | |
} | |
function keyUpHandler(e) { | |
if (e.key === 'Right' || e.key === 'ArrowRight') { | |
rightPressed = false; | |
} else if (e.key === 'Left' || e.key === 'ArrowLeft') { | |
leftPressed = false; | |
} | |
} | |
function mouseMoveHandler(e) { | |
const relativeX = e.clientX - canvas.offsetLeft; | |
if (relativeX > 0 && relativeX < canvas.width) { | |
paddleX = relativeX - paddleWidth / 2; | |
} | |
} | |
function touchMoveHandler(e) { | |
e.preventDefault(); | |
const touch = e.touches[0]; | |
const relativeX = touch.clientX - canvas.offsetLeft; | |
if (relativeX > 0 && relativeX < canvas.width) { | |
paddleX = relativeX - paddleWidth / 2; | |
} | |
} | |
// Button event listeners | |
startButton.addEventListener('click', () => { | |
startScreen.classList.add('hidden'); | |
gameRunning = true; | |
resetGame(); | |
draw(); | |
}); | |
restartButton.addEventListener('click', () => { | |
resetGame(); | |
gameRunning = true; | |
draw(); | |
}); | |
// Initialize game | |
initBricks(); | |
// Add ambient candle flicker effect | |
setInterval(() => { | |
const flickerElements = document.querySelectorAll('.candle-flicker'); | |
flickerElements.forEach(el => { | |
el.style.opacity = 0.7 + Math.random() * 0.3; | |
}); | |
}, 100); | |
}); | |
</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=Xie/deepsite-arkanoid" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |