agapes's picture
Initial DeepSite commit
c01aca1 verified
Raw
History Blame Contribute Delete
28.5 kB
// ==================== STAR BACKGROUND ====================
(function createStars() {
const container = document.getElementById('starsContainer');
const count = 120;
for (let i = 0; i < count; i++) {
const star = document.createElement('div');
star.classList.add('star');
const size = Math.random() * 3 + 1;
star.style.width = size + 'px';
star.style.height = size + 'px';
star.style.left = Math.random() * 100 + '%';
star.style.top = Math.random() * 100 + '%';
star.style.setProperty('--duration', (Math.random() * 3 + 2) + 's');
star.style.setProperty('--delay', (Math.random() * 5) + 's');
container.appendChild(star);
}
})();
// ==================== HIGH SCORE MANAGEMENT ====================
function getHighScore(game) {
return parseInt(localStorage.getItem('arcade_' + game + '_highscore')) || 0;
}
function setHighScore(game, score) {
const current = getHighScore(game);
if (score > current) {
localStorage.setItem('arcade_' + game + '_highscore', score);
return true;
}
return false;
}
function updateHighScoreDisplay() {
const games = ['snake', 'pong', 'breakout', 'memory'];
const display = document.getElementById('highScoreDisplay');
if (!display) return;
let html = '🏆 HIGH SCORES: ';
games.forEach((g, i) => {
const score = getHighScore(g);
html += `${g.toUpperCase()}: ${score}`;
if (i < games.length - 1) html += ' | ';
});
display.innerHTML = html;
}
updateHighScoreDisplay();
// ==================== MODAL MANAGEMENT ====================
const modal = document.getElementById('gameModal');
const modalTitle = document.getElementById('modalTitle');
const gameArea = document.getElementById('gameArea');
const gameScoreEl = document.getElementById('gameScore');
const mobileControls = document.getElementById('mobileControls');
const restartBtn = document.getElementById('restartGame');
const closeModalBtn = document.getElementById('closeModal');
let currentGame = null;
let gameLoopId = null;
let gameState = null;
function openModal(gameName) {
modal.classList.remove('hidden');
document.body.style.overflow = 'hidden';
currentGame = gameName;
gameState = {};
mobileControls.classList.add('hidden');
restartBtn.classList.add('hidden');
gameScoreEl.textContent = '';
gameArea.innerHTML = '';
switch (gameName) {
case 'snake':
modalTitle.textContent = '🐍 SNAKE';
startSnake();
break;
case 'pong':
modalTitle.textContent = '🏓 PONG';
startPong();
break;
case 'breakout':
modalTitle.textContent = '🧱 BREAKOUT';
startBreakout();
break;
case 'memory':
modalTitle.textContent = '🧠 MEMORY MATCH';
startMemory();
break;
}
}
function closeModal() {
modal.classList.add('hidden');
document.body.style.overflow = '';
if (gameLoopId) {
cancelAnimationFrame(gameLoopId);
gameLoopId = null;
}
// Remove event listeners by cloning canvas
if (gameArea.querySelector('canvas')) {
const canvas = gameArea.querySelector('canvas');
const clone = canvas.cloneNode(true);
canvas.parentNode.replaceChild(clone, canvas);
}
currentGame = null;
gameState = null;
gameArea.innerHTML = '';
updateHighScoreDisplay();
}
closeModalBtn.addEventListener('click', closeModal);
modal.addEventListener('click', (e) => {
if (e.target === modal) closeModal();
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && currentGame) {
closeModal();
}
});
// ==================== GAME CARDS CLICK ====================
document.querySelectorAll('.game-card').forEach(card => {
card.addEventListener('click', () => {
const game = card.dataset.game;
if (game) openModal(game);
});
});
// ==================== SNAKE GAME ====================
function startSnake() {
const canvas = document.createElement('canvas');
canvas.width = 400;
canvas.height = 400;
canvas.style.background = '#0a0a1a';
canvas.style.border = '2px solid var(--neon-green)';
canvas.style.boxShadow = '0 0 10px var(--neon-green)';
gameArea.appendChild(canvas);
const ctx = canvas.getContext('2d');
const gridSize = 20;
const cellSize = canvas.width / gridSize;
let snake = [{ x: 10, y: 10 }];
let direction = { x: 1, y: 0 };
let nextDirection = { x: 1, y: 0 };
let food = spawnFood();
let score = 0;
let speed = 120;
let lastTime = 0;
let gameOver = false;
restartBtn.classList.remove('hidden');
restartBtn.onclick = () => { cancelAnimationFrame(gameLoopId); startSnake(); };
// Show mobile D-pad on touch devices
if ('ontouchstart' in window || navigator.maxTouchPoints > 0) {
mobileControls.classList.remove('hidden');
mobileControls.querySelectorAll('.mobile-btn').forEach(btn => {
btn.onclick = (e) => {
e.preventDefault();
const dir = btn.dataset.dir;
changeDir(dir);
};
});
}
function spawnFood() {
let pos;
do {
pos = {
x: Math.floor(Math.random() * gridSize),
y: Math.floor(Math.random() * gridSize)
};
} while (snake.some(seg => seg.x === pos.x && seg.y === pos.y));
return pos;
}
function changeDir(dir) {
if (gameOver) return;
switch (dir) {
case 'up': if (direction.y !== 1) nextDirection = { x: 0, y: -1 }; break;
case 'down': if (direction.y !== -1) nextDirection = { x: 0, y: 1 }; break;
case 'left': if (direction.x !== 1) nextDirection = { x: -1, y: 0 }; break;
case 'right': if (direction.x !== -1) nextDirection = { x: 1, y: 0 }; break;
}
}
document.addEventListener('keydown', handleKey);
function handleKey(e) {
if (currentGame !== 'snake') return;
switch (e.key) {
case 'ArrowUp': case 'w': case 'W': e.preventDefault(); changeDir('up'); break;
case 'ArrowDown': case 's': case 'S': e.preventDefault(); changeDir('down'); break;
case 'ArrowLeft': case 'a': case 'A': e.preventDefault(); changeDir('left'); break;
case 'ArrowRight': case 'd': case 'D': e.preventDefault(); changeDir('right'); break;
}
}
gameState.cleanup = () => document.removeEventListener('keydown', handleKey);
function gameLoop(timestamp) {
if (gameOver) return;
if (timestamp - lastTime > speed) {
lastTime = timestamp;
direction = { ...nextDirection };
const head = { x: snake[0].x + direction.x, y: snake[0].y + direction.y };
// Wall collision
if (head.x < 0 || head.x >= gridSize || head.y < 0 || head.y >= gridSize) {
endGame();
return;
}
// Self collision
if (snake.some(seg => seg.x === head.x && seg.y === head.y)) {
endGame();
return;
}
snake.unshift(head);
// Food
if (head.x === food.x && head.y === food.y) {
score += 10;
food = spawnFood();
speed = Math.max(50, speed - 2);
gameScoreEl.textContent = 'Score: ' + score;
} else {
snake.pop();
}
}
// Draw
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Grid
ctx.strokeStyle = 'rgba(57, 255, 20, 0.05)';
ctx.lineWidth = 0.5;
for (let i = 0; i < gridSize; i++) {
ctx.beginPath();
ctx.moveTo(i * cellSize, 0);
ctx.lineTo(i * cellSize, canvas.height);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, i * cellSize);
ctx.lineTo(canvas.width, i * cellSize);
ctx.stroke();
}
// Snake body
snake.forEach((seg, i) => {
const gradient = ctx.createRadialGradient(
seg.x * cellSize + cellSize / 2, seg.y * cellSize + cellSize / 2, 2,
seg.x * cellSize + cellSize / 2, seg.y * cellSize + cellSize / 2, cellSize / 1.5
);
if (i === 0) {
gradient.addColorStop(0, '#39ff14');
gradient.addColorStop(1, '#1a8a0a');
} else {
gradient.addColorStop(0, '#2ad810');
gradient.addColorStop(1, '#0d5a05');
}
ctx.fillStyle = gradient;
ctx.shadowColor = '#39ff14';
ctx.shadowBlur = i === 0 ? 12 : 6;
ctx.fillRect(seg.x * cellSize + 2, seg.y * cellSize + 2, cellSize - 4, cellSize - 4);
ctx.shadowBlur = 0;
});
// Food
ctx.fillStyle = '#ff00e5';
ctx.shadowColor = '#ff00e5';
ctx.shadowBlur = 15;
ctx.beginPath();
ctx.arc(food.x * cellSize + cellSize / 2, food.y * cellSize + cellSize / 2, cellSize / 2 - 2, 0, Math.PI * 2);
ctx.fill();
ctx.shadowBlur = 0;
gameLoopId = requestAnimationFrame(gameLoop);
}
function endGame() {
gameOver = true;
const isNew = setHighScore('snake', score);
gameScoreEl.textContent = 'GAME OVER! Score: ' + score + (isNew ? ' 🏆 NEW HIGH!' : '');
gameScoreEl.classList.add('neon-text-pink');
// Draw game over overlay
ctx.fillStyle = 'rgba(0,0,0,0.7)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.font = '24px "Press Start 2P", cursive';
ctx.fillStyle = '#ff00e5';
ctx.textAlign = 'center';
ctx.fillText('GAME OVER', canvas.width / 2, canvas.height / 2 - 10);
ctx.font = '14px "Orbitron", sans-serif';
ctx.fillText('Score: ' + score, canvas.width / 2, canvas.height / 2 + 30);
cancelAnimationFrame(gameLoopId);
}
gameScoreEl.textContent = 'Score: 0';
gameLoopId = requestAnimationFrame(gameLoop);
}
// ==================== PONG GAME ====================
function startPong() {
const canvas = document.createElement('canvas');
canvas.width = 500;
canvas.height = 350;
canvas.style.background = '#0a0a1a';
canvas.style.border = '2px solid var(--neon-pink)';
canvas.style.boxShadow = '0 0 10px var(--neon-pink)';
gameArea.appendChild(canvas);
const ctx = canvas.getContext('2d');
const paddleW = 12;
const paddleH = 70;
const ballSize = 10;
let playerY = canvas.height / 2 - paddleH / 2;
let aiY = canvas.height / 2 - paddleH / 2;
let ballX = canvas.width / 2;
let ballY = canvas.height / 2;
let ballVX = (Math.random() > 0.5 ? 1 : -1) * 4;
let ballVY = (Math.random() * 6 - 3);
let playerScore = 0;
let aiScore = 0;
const winScore = 7;
let gameOver = false;
let keys = {};
restartBtn.classList.remove('hidden');
restartBtn.onclick = () => { cancelAnimationFrame(gameLoopId); startPong(); };
document.addEventListener('keydown', (e) => { keys[e.key] = true; });
document.addEventListener('keyup', (e) => { keys[e.key] = false; });
// Mouse/touch support
canvas.addEventListener('mousemove', (e) => {
const rect = canvas.getBoundingClientRect();
const scaleY = canvas.height / rect.height;
playerY = (e.clientY - rect.top) * scaleY - paddleH / 2;
playerY = Math.max(0, Math.min(canvas.height - paddleH, playerY));
});
canvas.addEventListener('touchmove', (e) => {
e.preventDefault();
const rect = canvas.getBoundingClientRect();
const scaleY = canvas.height / rect.height;
playerY = (e.touches[0].clientY - rect.top) * scaleY - paddleH / 2;
playerY = Math.max(0, Math.min(canvas.height - paddleH, playerY));
}, { passive: false });
gameState.cleanup = () => {};
function resetBall() {
ballX = canvas.width / 2;
ballY = canvas.height / 2;
ballVX = (Math.random() > 0.5 ? 1 : -1) * 4;
ballVY = (Math.random() * 6 - 3);
}
function gameLoop() {
if (gameOver) return;
// Player movement
if (keys['ArrowUp'] || keys['w'] || keys['W']) playerY -= 6;
if (keys['ArrowDown'] || keys['s'] || keys['S']) playerY += 6;
playerY = Math.max(0, Math.min(canvas.height - paddleH, playerY));
// AI movement
const aiCenter = aiY + paddleH / 2;
if (aiCenter < ballY - 15) aiY += 3.5;
else if (aiCenter > ballY + 15) aiY -= 3.5;
aiY = Math.max(0, Math.min(canvas.height - paddleH, aiY));
// Ball movement
ballX += ballVX;
ballY += ballVY;
// Top/bottom wall bounce
if (ballY - ballSize / 2 <= 0 || ballY + ballSize / 2 >= canvas.height) {
ballVY *= -1;
ballY = Math.max(ballSize / 2, Math.min(canvas.height - ballSize / 2, ballY));
}
// Player paddle collision
if (ballX - ballSize / 2 <= 20 + paddleW &&
ballY > playerY && ballY < playerY + paddleH &&
ballVX < 0) {
const hitPos = (ballY - playerY) / paddleH;
ballVX *= -1.05;
ballVY = (hitPos - 0.5) * 8;
ballX = 20 + paddleW + ballSize / 2;
}
// AI paddle collision
if (ballX + ballSize / 2 >= canvas.width - 20 - paddleW &&
ballY > aiY && ballY < aiY + paddleH &&
ballVX > 0) {
const hitPos = (ballY - aiY) / paddleH;
ballVX *= -1.05;
ballVY = (hitPos - 0.5) * 8;
ballX = canvas.width - 20 - paddleW - ballSize / 2;
}
// Scoring
if (ballX < 0) {
aiScore++;
if (aiScore >= winScore) endPongGame();
else resetBall();
}
if (ballX > canvas.width) {
playerScore++;
if (playerScore >= winScore) endPongGame();
else resetBall();
}
// Clamp ball speed
const speed = Math.sqrt(ballVX * ballVX + ballVY * ballVY);
if (speed > 10) {
ballVX = (ballVX / speed) * 10;
ballVY = (ballVY / speed) * 10;
}
// Draw
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Center line
ctx.setLineDash([8, 12]);
ctx.strokeStyle = 'rgba(255,255,255,0.1)';
ctx.beginPath();
ctx.moveTo(canvas.width / 2, 0);
ctx.lineTo(canvas.width / 2, canvas.height);
ctx.stroke();
ctx.setLineDash([]);
// Paddles
ctx.fillStyle = '#ff00e5';
ctx.shadowColor = '#ff00e5';
ctx.shadowBlur = 10;
ctx.fillRect(20, playerY, paddleW, paddleH);
ctx.fillStyle = '#00f0ff';
ctx.shadowColor = '#00f0ff';
ctx.fillRect(canvas.width - 20 - paddleW, aiY, paddleW, paddleH);
// Ball
ctx.fillStyle = '#ffffff';
ctx.shadowColor = '#ffffff';
ctx.shadowBlur = 12;
ctx.beginPath();
ctx.arc(ballX, ballY, ballSize / 2, 0, Math.PI * 2);
ctx.fill();
ctx.shadowBlur = 0;
// Scores
ctx.font = '28px "Press Start 2P", cursive';
ctx.fillStyle = '#ff00e5';
ctx.textAlign = 'center';
ctx.fillText(playerScore, canvas.width / 4, 45);
ctx.fillStyle = '#00f0ff';
ctx.fillText(aiScore, (canvas.width / 4) * 3, 45);
gameScoreEl.textContent = `YOU ${playerScore} - ${aiScore} AI | First to ${winScore}`;
gameLoopId = requestAnimationFrame(gameLoop);
}
function endPongGame() {
gameOver = true;
const winner = playerScore >= winScore ? 'YOU WIN!' : 'AI WINS!';
const isNew = setHighScore('pong', playerScore);
gameScoreEl.textContent = winner + ' Final: ' + playerScore + '-' + aiScore + (isNew ? ' 🏆' : '');
ctx.fillStyle = 'rgba(0,0,0,0.7)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.font = '24px "Press Start 2P", cursive';
ctx.fillStyle = playerScore >= winScore ? '#39ff14' : '#ff00e5';
ctx.textAlign = 'center';
ctx.fillText(winner, canvas.width / 2, canvas.height / 2);
cancelAnimationFrame(gameLoopId);
}
gameLoopId = requestAnimationFrame(gameLoop);
}
// ==================== BREAKOUT GAME ====================
function startBreakout() {
const canvas = document.createElement('canvas');
canvas.width = 480;
canvas.height = 400;
canvas.style.background = '#0a0a1a';
canvas.style.border = '2px solid var(--neon-cyan)';
canvas.style.boxShadow = '0 0 10px var(--neon-cyan)';
gameArea.appendChild(canvas);
const ctx = canvas.getContext('2d');
const paddleW = 80;
const paddleH = 12;
const ballRadius = 7;
const brickRows = 6;
const brickCols = 8;
const brickW = (canvas.width - 40) / brickCols;
const brickH = 22;
let paddleX = canvas.width / 2 - paddleW / 2;
let ballX = canvas.width / 2;
let ballY = canvas.height - 60;
let ballVX = (Math.random() * 4 - 2);
let ballVY = -5;
let score = 0;
let gameOver = false;
let ballLaunched = false;
const brickColors = ['#ff00e5', '#ff6600', '#ffff00', '#39ff14', '#00f0ff', '#8b5cf6'];
let bricks = [];
for (let row = 0; row < brickRows; row++) {
bricks[row] = [];
for (let col = 0; col < brickCols; col++) {
bricks[row][col] = { alive: true, color: brickColors[row] };
}
}
restartBtn.classList.remove('hidden');
restartBtn.onclick = () => { cancelAnimationFrame(gameLoopId); startBreakout(); };
// Mouse/touch
canvas.addEventListener('mousemove', (e) => {
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
paddleX = (e.clientX - rect.left) * scaleX - paddleW / 2;
paddleX = Math.max(0, Math.min(canvas.width - paddleW, paddleX));
if (!ballLaunched) ballX = paddleX + paddleW / 2;
});
canvas.addEventListener('touchmove', (e) => {
e.preventDefault();
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
paddleX = (e.touches[0].clientX - rect.left) * scaleX - paddleW / 2;
paddleX = Math.max(0, Math.min(canvas.width - paddleW, paddleX));
if (!ballLaunched) ballX = paddleX + paddleW / 2;
}, { passive: false });
canvas.addEventListener('click', () => {
if (!ballLaunched && !gameOver) {
ballLaunched = true;
ballVY = -5;
ballVX = (Math.random() * 4 - 2) || 2;
}
});
gameState.cleanup = () => {};
function gameLoop() {
if (gameOver) return;
// Ball movement
if (ballLaunched) {
ballX += ballVX;
ballY += ballVY;
// Wall collisions
if (ballX - ballRadius <= 0 || ballX + ballRadius >= canvas.width) ballVX *= -1;
if (ballY - ballRadius <= 0) ballVY *= -1;
// Bottom (game over)
if (ballY + ballRadius >= canvas.height) {
endBreakout(false);
return;
}
// Paddle collision
if (ballY + ballRadius >= canvas.height - paddleH - 10 &&
ballY + ballRadius <= canvas.height - 5 &&
ballX > paddleX && ballX < paddleX + paddleW &&
ballVY > 0) {
const hitPos = (ballX - paddleX) / paddleW;
ballVY *= -1;
ballVX = (hitPos - 0.5) * 7;
ballY = canvas.height - paddleH - 10 - ballRadius;
const speed = Math.sqrt(ballVX * ballVX + ballVY * ballVY);
if (speed < 5) {
ballVX = (ballVX / speed) * 5;
ballVY = (ballVY / speed) * 5;
}
}
// Brick collisions
let allDestroyed = true;
for (let row = 0; row < brickRows; row++) {
for (let col = 0; col < brickCols; col++) {
if (!bricks[row][col].alive) continue;
allDestroyed = false;
const bx = 20 + col * brickW;
const by = 40 + row * brickH;
if (ballX + ballRadius > bx && ballX - ballRadius < bx + brickW &&
ballY + ballRadius > by && ballY - ballRadius < by + brickH) {
bricks[row][col].alive = false;
score += 10;
gameScoreEl.textContent = 'Score: ' + score;
// Determine bounce direction
const overlapLeft = (ballX + ballRadius) - bx;
const overlapRight = (bx + brickW) - (ballX - ballRadius);
const overlapTop = (ballY + ballRadius) - by;
const overlapBottom = (by + brickH) - (ballY - ballRadius);
const minOverlapX = Math.min(overlapLeft, overlapRight);
const minOverlapY = Math.min(overlapTop, overlapBottom);
if (minOverlapX < minOverlapY) ballVX *= -1;
else ballVY *= -1;
}
}
}
if (allDestroyed) {
endBreakout(true);
return;
}
}
// Clamp ball
if (ballLaunched) {
const speed = Math.sqrt(ballVX * ballVX + ballVY * ballVY);
if (speed > 9) {
ballVX = (ballVX / speed) * 9;
ballVY = (ballVY / speed) * 9;
}
}
// Draw
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Bricks
for (let row = 0; row < brickRows; row++) {
for (let col = 0; col < brickCols; col++) {
if (!bricks[row][col].alive) continue;
const bx = 20 + col * brickW;
const by = 40 + row * brickH;
ctx.fillStyle = bricks[row][col].color;
ctx.shadowColor = bricks[row][col].color;
ctx.shadowBlur = 4;
ctx.fillRect(bx + 2, by + 2, brickW - 4, brickH - 4);
ctx.shadowBlur = 0;
}
}
// Paddle
ctx.fillStyle = '#00f0ff';
ctx.shadowColor = '#00f0ff';
ctx.shadowBlur = 10;
ctx.beginPath();
ctx.roundRect(paddleX, canvas.height - paddleH - 10, paddleW, paddleH, 6);
ctx.fill();
ctx.shadowBlur = 0;
// Ball
ctx.fillStyle = '#ffffff';
ctx.shadowColor = '#ffffff';
ctx.shadowBlur = 10;
ctx.beginPath();
ctx.arc(ballX, ballY, ballRadius, 0, Math.PI * 2);
ctx.fill();
ctx.shadowBlur = 0;
if (!ballLaunched && !gameOver) {
ctx.font = '14px "Orbitron", sans-serif';
ctx.fillStyle = '#ffffff';
ctx.textAlign = 'center';
ctx.fillText('CLICK TO LAUNCH', canvas.width / 2, canvas.height - 40);
}
gameLoopId = requestAnimationFrame(gameLoop);
}
function endBreakout(won) {
gameOver = true;
const isNew = setHighScore('breakout', score);
const msg = won ? 'YOU WIN! 🎉' : 'GAME OVER';
gameScoreEl.textContent = msg + ' Score: ' + score + (isNew ? ' 🏆 NEW HIGH!' : '');
ctx.fillStyle = 'rgba(0,0,0,0.7)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.font = '22px "Press Start 2P", cursive';
ctx.fillStyle = won ? '#39ff14' : '#ff00e5';
ctx.textAlign = 'center';
ctx.fillText(won ? 'YOU WIN!' : 'GAME OVER', canvas.width / 2, canvas.height / 2 - 10);
ctx.font = '14px "Orbitron", sans-serif';
ctx.fillText('Score: ' + score, canvas.width / 2, canvas.height / 2 + 30);
cancelAnimationFrame(gameLoopId);
}
gameScoreEl.textContent = 'Score: 0';
gameLoopId = requestAnimationFrame(gameLoop);
}
// ==================== MEMORY MATCH GAME ====================
function startMemory() {
const emojis = ['🚀', '🎮', '🕹️', '👾', '💎', '🔥', '⚡', '🌟'];
const cards = [...emojis, ...emojis];
// Shuffle
for (let i = cards.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[cards[i], cards[j]] = [cards[j], cards[i]];
}
let flippedCards = [];
let matchedCount = 0;
let moves = 0;
let lockBoard = false;
let gameOver = false;
const grid = document.createElement('div');
grid.style.display = 'grid';
grid.style.gridTemplateColumns = 'repeat(4, 1fr)';
grid.style.gap = '10px';
grid.style.maxWidth = '360px';
grid.style.width = '100%';
grid.style.margin = '0 auto';
gameArea.appendChild(grid);
restartBtn.classList.remove('hidden');
restartBtn.onclick = () => { startMemory(); };
cards.forEach((emoji, index) => {
const card = document.createElement('div');
card.classList.add('memory-card');
card.dataset.index = index;
card.dataset.emoji = emoji;
card.innerHTML = `
<div class="memory-card-inner">
<div class="memory-card-back">?</div>
<div class="memory-card-front">${emoji}</div>
</div>
`;
card.addEventListener('click', () => {
if (lockBoard || gameOver) return;
if (card.classList.contains('flipped') || card.classList.contains('matched')) return;
if (flippedCards.length >= 2) return;
card.classList.add('flipped');
flippedCards.push(card);
if (flippedCards.length === 2) {
moves++;
gameScoreEl.textContent = 'Moves: ' + moves;
lockBoard = true;
const [card1, card2] = flippedCards;
if (card1.dataset.emoji === card2.dataset.emoji) {
// Match!
setTimeout(() => {
card1.classList.add('matched');
card2.classList.add('matched');
matchedCount++;
flippedCards = [];
lockBoard = false;
if (matchedCount === emojis.length) {
gameOver = true;
const isNew = setHighScore('memory', moves);
gameScoreEl.textContent = '🎉 ALL MATCHED! Moves: ' + moves + (isNew ? ' 🏆 BEST!' : '');
gameScoreEl.classList.add('neon-text-green');
}
}, 400);
} else {
// No match
setTimeout(() => {
card1.classList.remove('flipped');
card2.classList.remove('flipped');
flippedCards = [];
lockBoard = false;
}, 700);
}
}
});
grid.appendChild(card);
});
gameScoreEl.textContent = 'Moves: 0';
gameState.cleanup = () => {};
}
// ==================== INITIALIZATION ====================
console.log('🕹️ Neon Arcade Zone ready!');
console.log('🎮 Click a game card to play!');
console.log('🐍 Snake | 🏓 Pong | 🧱 Breakout | 🧠 Memory Match');