diff --git a/app.js b/app.js index 9f3e0a5..9c80291 100644 --- a/app.js +++ b/app.js @@ -1,2387 +1 @@ -/** - * TRAE Aurora - Crystalline Christmas Logic - * Author: Code Prism (Assistant) - */ - -document.addEventListener('DOMContentLoaded', () => { - initBackground(); - initCountdown(); - initMessageForge(); - initGame(); - initTraoom(); - initNeonPuzzle(); - initRhythmBeat(); - initCosmicArena(); - initTetris(); - initPlatformer(); -}); - -// --- Utility: Scroll to Section --- -function scrollToSection(id) { - const el = document.getElementById(id); - if (el) el.scrollIntoView({ behavior: 'smooth' }); -} - -// --- 1. Background Engine (Aurora + Snow) --- -function initBackground() { - const canvas = document.getElementById('bgCanvas'); - const ctx = canvas.getContext('2d'); - let width, height, particles = []; - - const resize = () => { - width = canvas.width = window.innerWidth; - height = canvas.height = window.innerHeight; - }; - - class Particle { - constructor() { - this.reset(); - } - reset() { - this.x = Math.random() * width; - this.y = Math.random() * height; - this.size = Math.random() * 2 + 1; - this.speedY = Math.random() * 1 + 0.5; - this.speedX = Math.random() * 0.5 - 0.25; - this.opacity = Math.random() * 0.5 + 0.2; - } - update() { - this.y += this.speedY; - this.x += this.speedX; - if (this.y > height) { - this.y = -10; - this.x = Math.random() * width; - } - } - draw() { - ctx.fillStyle = `rgba(0, 255, 102, ${this.opacity})`; - ctx.beginPath(); - ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); - ctx.fill(); - } - } - - const initParticles = () => { - particles = []; - for (let i = 0; i < 150; i++) { - particles.push(new Particle()); - } - }; - - const drawAurora = () => { - const time = Date.now() * 0.001; - const gradient = ctx.createLinearGradient(0, 0, width, height); - gradient.addColorStop(0, '#050b14'); - gradient.addColorStop(0.5, '#0a1a2f'); - gradient.addColorStop(1, '#050b14'); - - ctx.fillStyle = gradient; - ctx.fillRect(0, 0, width, height); - - // Aurora waves - ctx.save(); - ctx.globalCompositeOperation = 'screen'; - for (let i = 0; i < 3; i++) { - ctx.beginPath(); - ctx.strokeStyle = i === 0 ? 'rgba(0, 255, 102, 0.1)' : 'rgba(0, 242, 255, 0.05)'; - ctx.lineWidth = 100; - const yOffset = height * 0.3 + (i * 100); - ctx.moveTo(0, yOffset + Math.sin(time + i) * 50); - for (let x = 0; x < width; x += 50) { - ctx.lineTo(x, yOffset + Math.sin(x * 0.002 + time + i) * 80); - } - ctx.stroke(); - } - ctx.restore(); - }; - - const animate = () => { - drawAurora(); - particles.forEach(p => { - p.update(); - p.draw(); - }); - requestAnimationFrame(animate); - }; - - window.addEventListener('resize', () => { - isMobile = window.innerWidth < 768; - resize(); - }); - resize(); - initParticles(); - animate(); -} - -// --- 2. Countdown Timer --- -function initCountdown() { - const target = new Date('December 25, 2025 00:00:00').getTime(); - - const update = () => { - const now = new Date().getTime(); - const diff = target - now; - - if (diff <= 0) { - document.getElementById('countdown').innerHTML = "

MERRY CHRISTMAS!

"; - return; - } - - const d = Math.floor(diff / (1000 * 60 * 60 * 24)); - const h = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); - const m = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); - const s = Math.floor((diff % (1000 * 60)) / 1000); - - document.getElementById('days').innerText = d.toString().padStart(2, '0'); - document.getElementById('hours').innerText = h.toString().padStart(2, '0'); - document.getElementById('minutes').innerText = m.toString().padStart(2, '0'); - document.getElementById('seconds').innerText = s.toString().padStart(2, '0'); - }; - - setInterval(update, 1000); - update(); -} - -// --- 3. Magical Message Forge --- -function initMessageForge() { - const input = document.getElementById('messageInput'); - const btn = document.getElementById('forgeBtn'); - const output = document.getElementById('outputMessage'); - const canvas = document.getElementById('snowflakeCanvas'); - const cloudContainer = document.getElementById('cloudContainer'); - const ctx = canvas.getContext('2d'); - - canvas.width = 300; - canvas.height = 300; - - // Load persistent messages - let communityMessages = JSON.parse(localStorage.getItem('trae_messages') || '[]'); - - const updateCloud = () => { - cloudContainer.innerHTML = ''; - communityMessages.slice(-15).reverse().forEach(msg => { - const span = document.createElement('span'); - span.className = 'cloud-msg'; - span.innerText = msg; - cloudContainer.appendChild(span); - }); - }; - - const drawCrystal = (text) => { - ctx.clearRect(0, 0, canvas.width, canvas.height); - const centerX = canvas.width / 2; - const centerY = canvas.height / 2; - - ctx.strokeStyle = '#00ff66'; - ctx.lineWidth = 2; - const branches = 6 + (text.length % 6); - const radius = 80 + (text.length * 2); - - for (let i = 0; i < branches; i++) { - const angle = (i * 2 * Math.PI) / branches; - ctx.beginPath(); - ctx.moveTo(centerX, centerY); - ctx.lineTo(centerX + Math.cos(angle) * radius, centerY + Math.sin(angle) * radius); - ctx.stroke(); - - for (let j = 1; j < 4; j++) { - const subAngle = angle + 0.5; - const subX = centerX + Math.cos(angle) * (radius * j / 4); - const subY = centerY + Math.sin(angle) * (radius * j / 4); - ctx.beginPath(); - ctx.moveTo(subX, subY); - ctx.lineTo(subX + Math.cos(subAngle) * 20, subY + Math.sin(subAngle) * 20); - ctx.stroke(); - } - } - - output.innerText = text.toUpperCase(); - output.style.opacity = 0; - setTimeout(() => { output.style.opacity = 1; output.style.transition = 'opacity 1s'; }, 100); - }; - - btn.addEventListener('click', () => { - const text = input.value.trim(); - if (text) { - drawCrystal(text); - communityMessages.push(text); - if (communityMessages.length > 50) communityMessages.shift(); - localStorage.setItem('trae_messages', JSON.stringify(communityMessages)); - updateCloud(); - input.value = ''; - } - }); - - updateCloud(); -} - -// --- 4. Gift Catcher Mini Game --- -function initGame() { - const canvas = document.getElementById('gameCanvas'); - const ctx = canvas.getContext('2d'); - const startBtn = document.getElementById('startGameBtn'); - const scoreEl = document.getElementById('scoreVal'); - const overlay = document.getElementById('gameOverlay'); - const submitBtn = document.getElementById('submitScoreBtn'); - const nameInput = document.getElementById('playerName'); - const leaderboardList = document.getElementById('leaderboardList'); - const timerDisplay = document.getElementById('timerDisplay'); - const timeBtns = document.querySelectorAll('.time-btn'); - - let score = 0; - let gameActive = false; - let playerX = 0; - let playerWidth = 90; - let playerHeight = 25; - let playerBottomOffset = 35; - let gifts = []; - let particles = []; - let difficultyMultiplier = 1; - let combo = 0; - let lastCatchTime = 0; - let timeLeft = 15; - let timerInterval = null; - let selectedDuration = 15; - let isMobile = window.innerWidth < 768; - - timeBtns.forEach(btn => { - btn.addEventListener('click', () => { - timeBtns.forEach(b => b.classList.remove('active')); - btn.classList.add('active'); - selectedDuration = parseInt(btn.dataset.time); - timeLeft = selectedDuration; - timerDisplay.innerText = timeLeft + 's'; - }); - }); - - // Leaderboard logic - let leaderboard = JSON.parse(localStorage.getItem('trae_leaderboard') || '[]'); - - const updateLeaderboardUI = () => { - leaderboardList.innerHTML = ''; - leaderboard.sort((a, b) => b.score - a.score).slice(0, 10).forEach((entry, idx) => { - const dateStr = new Date(entry.date).toLocaleDateString(); - const durationStr = entry.duration ? `${entry.duration}s` : 'N/A'; - const div = document.createElement('div'); - div.className = 'leader-item'; - div.innerHTML = ` - #${idx + 1} - ${entry.name} - ${entry.score} - ${durationStr} - ${dateStr} - `; - leaderboardList.appendChild(div); - }); - }; - - const startTimer = () => { - timeLeft = selectedDuration; - timerDisplay.innerText = timeLeft + 's'; - timerDisplay.classList.remove('warning'); - - if (timerInterval) clearInterval(timerInterval); - - timerInterval = setInterval(() => { - timeLeft--; - timerDisplay.innerText = timeLeft + 's'; - - const warningThreshold = Math.max(5, Math.floor(selectedDuration * 0.2)); - if (timeLeft <= warningThreshold) { - timerDisplay.classList.add('warning'); - } - - if (timeLeft <= 0) { - clearInterval(timerInterval); - gameOver(); - } - }, 1000); - }; - - const gameOver = () => { - gameActive = false; - if (timerInterval) clearInterval(timerInterval); - document.body.style.overflow = ''; - overlay.classList.remove('hidden'); - document.getElementById('overlayTitle').innerText = `GAME OVER - SCORE: ${score}`; - }; - - submitBtn.addEventListener('click', () => { - const name = nameInput.value.trim() || 'Anonymous'; - leaderboard.push({ name, score, date: new Date().toISOString(), duration: selectedDuration }); - localStorage.setItem('trae_leaderboard', JSON.stringify(leaderboard)); - updateLeaderboardUI(); - overlay.classList.add('hidden'); - startBtn.style.display = 'block'; - document.body.style.overflow = ''; - }); - - const resize = () => { - canvas.width = canvas.parentElement.clientWidth; - canvas.height = canvas.parentElement.clientHeight; - playerX = canvas.width / 2; - }; - - const GIFT_TYPES = { - common: { color: '#00ff66', points: 10, speedMult: 1, size: 25, chance: 0.6 }, - rare: { color: '#00f2ff', points: 25, speedMult: 1.3, size: 20, chance: 0.3 }, - legendary: { color: '#ff00ff', points: 50, speedMult: 1.8, size: 18, chance: 0.1 } - }; - - const ARTIFACT_TYPES = { - crown: { color: '#ffd700', points: 100, speedMult: 1.5, size: 30, chance: 0.05, shape: 'crown' }, - trophy: { color: '#c0c0c0', points: 75, speedMult: 1.3, size: 28, chance: 0.08, shape: 'trophy' }, - gem: { color: '#ff69b4', points: 60, speedMult: 1.6, size: 22, chance: 0.1, shape: 'gem' }, - star: { color: '#ffff00', points: 40, speedMult: 1.4, size: 20, chance: 0.12, shape: 'star' } - }; - - class Particle { - constructor(x, y, color) { - this.x = x; - this.y = y; - this.color = color; - this.size = Math.random() * 5 + 2; - this.speedX = (Math.random() - 0.5) * 8; - this.speedY = (Math.random() - 0.5) * 8 - 3; - this.life = 1; - this.decay = Math.random() * 0.02 + 0.02; - } - update() { - this.x += this.speedX; - this.y += this.speedY; - this.life -= this.decay; - } - draw() { - ctx.save(); - ctx.globalAlpha = this.life; - ctx.fillStyle = this.color; - ctx.shadowBlur = 10; - ctx.shadowColor = this.color; - ctx.beginPath(); - ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); - ctx.fill(); - ctx.restore(); - } - } - - class Gift { - constructor() { - const rand = Math.random(); - let type; - if (rand < 0.6) type = 'common'; - else if (rand < 0.9) type = 'rare'; - else type = 'legendary'; - - const typeData = GIFT_TYPES[type]; - this.x = Math.random() * (canvas.width - 50) + 25; - this.y = -60; - this.size = typeData.size; - this.baseSpeed = (3 + Math.random() * 3) * typeData.speedMult * difficultyMultiplier; - this.speed = this.baseSpeed; - this.color = typeData.color; - this.points = typeData.points; - this.rotation = 0; - this.rotationSpeed = (Math.random() - 0.5) * 0.1; - this.type = type; - } - update() { - this.y += this.speed; - this.rotation += this.rotationSpeed; - } - draw() { - ctx.save(); - ctx.translate(this.x + this.size/2, this.y + this.size/2); - ctx.rotate(this.rotation); - ctx.shadowBlur = 20; - ctx.shadowColor = this.color; - ctx.fillStyle = this.color; - ctx.fillRect(-this.size/2, -this.size/2, this.size, this.size); - ctx.strokeStyle = '#fff'; - ctx.lineWidth = 2; - ctx.strokeRect(-this.size/2, -this.size/2, this.size, this.size); - - // Glow effect for legendary - if (this.type === 'legendary') { - ctx.beginPath(); - ctx.arc(0, 0, this.size * 0.8, 0, Math.PI * 2); - ctx.strokeStyle = '#fff'; - ctx.lineWidth = 1; - ctx.stroke(); - } - ctx.restore(); - } - } - - class Artifact { - constructor() { - const types = Object.keys(ARTIFACT_TYPES); - const rand = Math.random(); - let selectedType = 'star'; - let cumulative = 0; - for (const type of types) { - cumulative += ARTIFACT_TYPES[type].chance; - if (rand < cumulative) { - selectedType = type; - break; - } - } - - const typeData = ARTIFACT_TYPES[selectedType]; - this.x = Math.random() * (canvas.width - 60) + 30; - this.y = -70; - this.size = typeData.size; - this.baseSpeed = (4 + Math.random() * 4) * typeData.speedMult * difficultyMultiplier; - this.speed = this.baseSpeed; - this.color = typeData.color; - this.points = typeData.points; - this.rotation = 0; - this.rotationSpeed = (Math.random() - 0.5) * 0.08; - this.type = selectedType; - this.shape = typeData.shape; - this.wobble = 0; - this.wobbleSpeed = 0.05; - } - update() { - this.y += this.speed; - this.rotation += this.rotationSpeed; - this.wobble += this.wobbleSpeed; - } - draw() { - ctx.save(); - ctx.translate(this.x + this.size/2, this.y + this.size/2); - ctx.rotate(this.rotation); - ctx.translate(0, Math.sin(this.wobble) * 3); - ctx.shadowBlur = 25; - ctx.shadowColor = this.color; - ctx.fillStyle = this.color; - ctx.strokeStyle = '#fff'; - ctx.lineWidth = 2; - - const s = this.size; - - switch(this.shape) { - case 'crown': - this.drawCrown(s); - break; - case 'trophy': - this.drawTrophy(s); - break; - case 'gem': - this.drawGem(s); - break; - case 'star': - this.drawStar(s); - break; - } - - ctx.restore(); - } - drawCrown(s) { - ctx.beginPath(); - const base = s * 0.3; - const height = s * 0.5; - ctx.moveTo(-s/2, base); - ctx.lineTo(-s/2 + s/6, -height/2); - ctx.lineTo(-s/6, 0); - ctx.lineTo(0, -height); - ctx.lineTo(s/6, 0); - ctx.lineTo(s/2 - s/6, -height/2); - ctx.lineTo(s/2, base); - ctx.closePath(); - ctx.fill(); - ctx.stroke(); - - ctx.fillStyle = '#fff'; - ctx.beginPath(); - ctx.arc(0, -height/3, s/12, 0, Math.PI * 2); - ctx.fill(); - } - drawTrophy(s) { - const baseW = s * 0.5; - const baseH = s * 0.2; - const cupW = s * 0.4; - const cupH = s * 0.4; - - ctx.fillRect(-baseW/2, s/2 - baseH, baseW, baseH); - ctx.strokeRect(-baseW/2, s/2 - baseH, baseW, baseH); - - ctx.beginPath(); - ctx.moveTo(-cupW/2, s/2 - baseH); - ctx.lineTo(-cupW/2, s/2 - baseH - cupH); - ctx.quadraticCurveTo(0, s/2 - baseH - cupH - cupW/2, cupW/2, s/2 - baseH - cupH); - ctx.lineTo(cupW/2, s/2 - baseH); - ctx.closePath(); - ctx.fill(); - ctx.stroke(); - - ctx.beginPath(); - ctx.moveTo(-cupW/3, s/2 - baseH - cupH/2); - ctx.lineTo(cupW/3, s/2 - baseH - cupH/2); - ctx.stroke(); - } - drawGem(s) { - const h = s * 0.6; - ctx.beginPath(); - ctx.moveTo(0, -h); - ctx.lineTo(s/2, -h/4); - ctx.lineTo(s/3, h/2); - ctx.lineTo(0, h/3); - ctx.lineTo(-s/3, h/2); - ctx.lineTo(-s/2, -h/4); - ctx.closePath(); - ctx.fill(); - ctx.stroke(); - - ctx.strokeStyle = 'rgba(255,255,255,0.5)'; - ctx.lineWidth = 1; - ctx.beginPath(); - ctx.moveTo(0, -h); - ctx.lineTo(0, h/3); - ctx.moveTo(-s/2, -h/4); - ctx.lineTo(s/3, h/2); - ctx.moveTo(s/2, -h/4); - ctx.lineTo(-s/3, h/2); - ctx.stroke(); - } - drawStar(s) { - const spikes = 5; - const outerRadius = s/2; - const innerRadius = s/4; - - ctx.beginPath(); - for(let i = 0; i < spikes * 2; i++) { - const radius = i % 2 === 0 ? outerRadius : innerRadius; - const angle = (i * Math.PI / spikes) - Math.PI / 2; - const x = Math.cos(angle) * radius; - const y = Math.sin(angle) * radius; - if(i === 0) ctx.moveTo(x, y); - else ctx.lineTo(x, y); - } - ctx.closePath(); - ctx.fill(); - ctx.stroke(); - } - } - - const createExplosion = (x, y, color) => { - for (let i = 0; i < 15; i++) { - particles.push(new Particle(x, y, color)); - } - }; - - const drawPlayer = () => { - ctx.save(); - const pulse = Math.sin(Date.now() * 0.005) * 0.2 + 0.8; - ctx.globalAlpha = pulse; - ctx.fillStyle = 'rgba(0, 255, 102, 0.3)'; - ctx.strokeStyle = '#00ff66'; - ctx.lineWidth = 3; - ctx.shadowBlur = 20; - ctx.shadowColor = '#00ff66'; - ctx.beginPath(); - ctx.roundRect(playerX - playerWidth / 2, canvas.height - playerBottomOffset, playerWidth, playerHeight, 8); - ctx.fill(); - ctx.stroke(); - ctx.restore(); - }; - - const updateGame = () => { - if (!gameActive) return; - - ctx.clearRect(0, 0, canvas.width, canvas.height); - - // Draw grid background - ctx.save(); - ctx.strokeStyle = 'rgba(0, 255, 102, 0.05)'; - ctx.lineWidth = 1; - for (let x = 0; x < canvas.width; x += 30) { - ctx.beginPath(); - ctx.moveTo(x, 0); - ctx.lineTo(x, canvas.height); - ctx.stroke(); - } - for (let y = 0; y < canvas.height; y += 30) { - ctx.beginPath(); - ctx.moveTo(0, y); - ctx.lineTo(canvas.width, y); - ctx.stroke(); - } - ctx.restore(); - - drawPlayer(); - - // Update & Draw Particles - particles = particles.filter(p => p.life > 0); - particles.forEach(p => { - p.update(); - p.draw(); - }); - - // Spawn gifts with increasing difficulty - const spawnRate = 0.02 + (difficultyMultiplier - 1) * 0.01; - if (Math.random() < spawnRate) gifts.push(new Gift()); - - // Spawn rare artifacts - const artifactSpawnRate = 0.005 + (difficultyMultiplier - 1) * 0.002; - if (Math.random() < artifactSpawnRate) gifts.push(new Artifact()); - - gifts.forEach((gift, index) => { - gift.update(); - gift.draw(); - - // Collision detection - if (gift.y + gift.size > canvas.height - playerBottomOffset - playerHeight / 2 && - gift.x > playerX - playerWidth / 2 - 10 && gift.x < playerX + playerWidth / 2 + 10) { - - // Combo system - const now = Date.now(); - if (now - lastCatchTime < 1000) { - combo++; - } else { - combo = 1; - } - lastCatchTime = now; - - const comboMultiplier = 1 + (combo - 1) * 0.2; - const finalPoints = Math.floor(gift.points * comboMultiplier); - - score += finalPoints; - scoreEl.innerText = score; - - createExplosion(gift.x + gift.size/2, gift.y + gift.size/2, gift.color); - gifts.splice(index, 1); - - // Increase difficulty - difficultyMultiplier = 1 + (score / 500); - } else if (gift.y > canvas.height) { - gifts.splice(index, 1); - combo = 0; - } - }); - - // Display combo - if (combo > 1) { - ctx.save(); - ctx.fillStyle = '#ff00ff'; - ctx.font = 'bold 24px Orbitron'; - ctx.textAlign = 'center'; - ctx.shadowBlur = 15; - ctx.shadowColor = '#ff00ff'; - ctx.fillText(`${combo}x COMBO!`, playerX, canvas.height - 50); - ctx.restore(); - } - - requestAnimationFrame(updateGame); - }; - - updateLeaderboardUI(); - - canvas.addEventListener('mousemove', (e) => { - const rect = canvas.getBoundingClientRect(); - playerX = e.clientX - rect.left; - playerX = Math.max(playerWidth / 2, Math.min(canvas.width - playerWidth / 2, playerX)); - }); - - canvas.addEventListener('touchstart', (e) => { - e.preventDefault(); - const rect = canvas.getBoundingClientRect(); - const touch = e.touches[0]; - playerX = touch.clientX - rect.left; - playerX = Math.max(playerWidth / 2, Math.min(canvas.width - playerWidth / 2, playerX)); - }, { passive: false }); - - canvas.addEventListener('touchmove', (e) => { - e.preventDefault(); - const rect = canvas.getBoundingClientRect(); - const touch = e.touches[0]; - playerX = touch.clientX - rect.left; - playerX = Math.max(playerWidth / 2, Math.min(canvas.width - playerWidth / 2, playerX)); - }, { passive: false }); - - canvas.addEventListener('touchend', (e) => { - e.preventDefault(); - }, { passive: false }); - - canvas.addEventListener('touchcancel', (e) => { - e.preventDefault(); - }, { passive: false }); - - startBtn.addEventListener('click', () => { - gameActive = true; - score = 0; - scoreEl.innerText = score; - gifts = []; - particles = []; - difficultyMultiplier = 1; - combo = 0; - lastCatchTime = 0; - startBtn.style.display = 'none'; - document.getElementById('gameUI').style.top = '40px'; - document.getElementById('gameUI').style.transform = 'translateX(-50%)'; - document.body.style.overflow = 'hidden'; - startTimer(); - updateGame(); - }); - - document.addEventListener('touchmove', (e) => { - if (gameActive) { - const gameCanvas = document.getElementById('gameCanvas'); - if (!gameCanvas.contains(e.target)) { - e.preventDefault(); - } - } - }, { passive: false }); - - window.addEventListener('resize', resize); - resize(); -} - -// Export/Import Leaderboard -window.exportLeaderboard = () => { - const leaderboard = JSON.parse(localStorage.getItem('trae_leaderboard') || '[]'); - const dataStr = JSON.stringify(leaderboard, null, 2); - const blob = new Blob([dataStr], { type: 'application/json' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = `trae_leaderboard_${new Date().toISOString().split('T')[0]}.json`; - a.click(); - URL.revokeObjectURL(url); -}; - -window.importLeaderboard = (event) => { - const file = event.target.files[0]; - if (!file) return; - - const reader = new FileReader(); - reader.onload = (e) => { - try { - const data = JSON.parse(e.target.result); - if (Array.isArray(data)) { - localStorage.setItem('trae_leaderboard', JSON.stringify(data)); - location.reload(); - } - } catch (err) { - alert('Invalid file format'); - } - }; - reader.readAsText(file); -}; - -// --- 5. Traoom - Bug Hunter Game --- -function initTraoom() { - const canvas = document.getElementById('traoomCanvas'); - const ctx = canvas.getContext('2d'); - const startBtn = document.getElementById('startTraoomBtn'); - const restartBtn = document.getElementById('restartTraoomBtn'); - const killEl = document.getElementById('killVal'); - const timeEl = document.getElementById('traoomTime'); - const waveEl = document.getElementById('waveVal'); - const overlay = document.getElementById('traoomOverlay'); - const finalKillsEl = document.getElementById('finalKills'); - const finalTimeEl = document.getElementById('finalTime'); - - let gameActive = false; - let kills = 0; - let wave = 1; - let gameTime = 0; - let timerInterval = null; - let lastShot = 0; - const SHOOT_COOLDOWN = 200; - - const keys = {}; - const mouse = { x: 0, y: 0 }; - - const resize = () => { - canvas.width = canvas.parentElement.clientWidth; - canvas.height = canvas.parentElement.clientHeight; - }; - - const drawRobloxBug = (x, y, size, color, health, maxHealth) => { - ctx.save(); - - const scale = size / 40; - - ctx.translate(x, y); - - ctx.fillStyle = color; - ctx.strokeStyle = '#ffffff'; - ctx.lineWidth = 2; - - const pulse = Math.sin(Date.now() * 0.01) * 0.1 + 1; - ctx.scale(pulse, pulse); - - ctx.beginPath(); - ctx.roundRect(-20 * scale, -20 * scale, 40 * scale, 40 * scale, 5 * scale); - ctx.fill(); - ctx.stroke(); - - ctx.fillStyle = '#ffffff'; - ctx.beginPath(); - ctx.arc(-8 * scale, -5 * scale, 4 * scale, 0, Math.PI * 2); - ctx.arc(8 * scale, -5 * scale, 4 * scale, 0, Math.PI * 2); - ctx.fill(); - - ctx.fillStyle = '#ff0000'; - ctx.beginPath(); - ctx.arc(-8 * scale, -5 * scale, 2 * scale, 0, Math.PI * 2); - ctx.arc(8 * scale, -5 * scale, 2 * scale, 0, Math.PI * 2); - ctx.fill(); - - ctx.strokeStyle = '#ffffff'; - ctx.lineWidth = 3; - ctx.beginPath(); - ctx.moveTo(-10 * scale, 8 * scale); - ctx.lineTo(10 * scale, 8 * scale); - ctx.stroke(); - - const healthPercent = health / maxHealth; - ctx.fillStyle = 'rgba(255, 0, 0, 0.7)'; - ctx.fillRect(-15 * scale, -30 * scale, 30 * scale, 5 * scale); - ctx.fillStyle = '#00ff66'; - ctx.fillRect(-15 * scale, -30 * scale, 30 * scale * healthPercent, 5 * scale); - - ctx.restore(); - }; - - const drawPlayer = (x, y, size) => { - ctx.save(); - ctx.translate(x, y); - - const angle = Math.atan2(mouse.y - y, mouse.x - x); - ctx.rotate(angle); - - ctx.fillStyle = '#00ff66'; - ctx.strokeStyle = '#ffffff'; - ctx.lineWidth = 2; - - ctx.beginPath(); - ctx.roundRect(-size / 2, -size / 2, size, size, 5); - ctx.fill(); - ctx.stroke(); - - ctx.fillStyle = '#ffffff'; - ctx.beginPath(); - ctx.arc(3, -3, 4, 0, Math.PI * 2); - ctx.fill(); - - ctx.fillStyle = '#00f2ff'; - ctx.fillRect(size / 2 - 5, -3, 15, 6); - - ctx.restore(); - }; - - const drawBullet = (x, y, size) => { - ctx.save(); - ctx.fillStyle = '#00f2ff'; - ctx.shadowColor = '#00f2ff'; - ctx.shadowBlur = 10; - ctx.beginPath(); - ctx.arc(x, y, size, 0, Math.PI * 2); - ctx.fill(); - ctx.restore(); - }; - - const drawParticle = (p) => { - ctx.save(); - ctx.globalAlpha = p.life / p.maxLife; - ctx.fillStyle = p.color; - ctx.beginPath(); - ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2); - ctx.fill(); - ctx.restore(); - }; - - class Bug { - constructor() { - const side = Math.floor(Math.random() * 4); - switch(side) { - case 0: - this.x = -30; - this.y = Math.random() * canvas.height; - break; - case 1: - this.x = canvas.width + 30; - this.y = Math.random() * canvas.height; - break; - case 2: - this.x = Math.random() * canvas.width; - this.y = -30; - break; - case 3: - this.x = Math.random() * canvas.width; - this.y = canvas.height + 30; - break; - } - this.size = 30 + Math.random() * 20; - this.speed = 1 + Math.random() * (1 + wave * 0.3); - this.health = 2 + Math.floor(wave * 0.5); - this.maxHealth = this.health; - - const bugTypes = [ - { color: '#ff0000', name: 'SyntaxError' }, - { color: '#ff6600', name: 'TypeError' }, - { color: '#ff00ff', name: 'LogicBug' }, - { color: '#ffff00', name: 'MemoryLeak' }, - { color: '#00ffff', name: 'NullPointer' } - ]; - this.type = bugTypes[Math.floor(Math.random() * bugTypes.length)]; - } - - update(playerX, playerY) { - const dx = playerX - this.x; - const dy = playerY - this.y; - const dist = Math.sqrt(dx * dx + dy * dy); - - if (dist > 0) { - this.x += (dx / dist) * this.speed; - this.y += (dy / dist) * this.speed; - } - } - - draw() { - drawRobloxBug(this.x, this.y, this.size, this.type.color, this.health, this.maxHealth); - } - - takeDamage(amount) { - this.health -= amount; - return this.health <= 0; - } - } - - class Bullet { - constructor(x, y, targetX, targetY) { - this.x = x; - this.y = y; - const dx = targetX - x; - const dy = targetY - y; - const dist = Math.sqrt(dx * dx + dy * dy); - this.vx = (dx / dist) * 12; - this.vy = (dy / dist) * 12; - this.size = 5; - this.life = 60; - } - - update() { - this.x += this.vx; - this.y += this.vy; - this.life--; - return this.life > 0 && - this.x > 0 && this.x < canvas.width && - this.y > 0 && this.y < canvas.height; - } - - draw() { - drawBullet(this.x, this.y, this.size); - } - } - - class Particle { - constructor(x, y, color) { - this.x = x; - this.y = y; - this.vx = (Math.random() - 0.5) * 8; - this.vy = (Math.random() - 0.5) * 8; - this.size = 3 + Math.random() * 4; - this.color = color; - this.life = 30; - this.maxLife = 30; - } - - update() { - this.x += this.vx; - this.y += this.vy; - this.vy += 0.2; - this.life--; - return this.life > 0; - } - - draw() { - drawParticle(this); - } - } - - let player = { x: 0, y: 0, size: 30, speed: 5, health: 100 }; - let bugs = []; - let bullets = []; - let particles = []; - let spawnTimer = 0; - let spawnRate = 120; - - const spawnBug = () => { - bugs.push(new Bug()); - }; - - const createExplosion = (x, y, color) => { - for (let i = 0; i < 10; i++) { - particles.push(new Particle(x, y, color)); - } - }; - - const checkCollisions = () => { - bullets.forEach((bullet, bi) => { - bugs.forEach((bug, gi) => { - const dx = bullet.x - bug.x; - const dy = bullet.y - bug.y; - const dist = Math.sqrt(dx * dx + dy * dy); - - if (dist < bug.size / 2 + bullet.size) { - bullets.splice(bi, 1); - - if (bug.takeDamage(1)) { - bugs.splice(gi, 1); - kills++; - killEl.innerText = kills; - createExplosion(bug.x, bug.y, bug.type.color); - - if (kills % 10 === 0) { - wave++; - waveEl.innerText = wave; - spawnRate = Math.max(30, 120 - wave * 10); - } - } else { - createExplosion(bullet.x, bullet.y, '#ffffff'); - } - } - }); - }); - - bugs.forEach((bug) => { - const dx = player.x - bug.x; - const dy = player.y - bug.y; - const dist = Math.sqrt(dx * dx + dy * dy); - - if (dist < player.size / 2 + bug.size / 2) { - player.health -= 10; - createExplosion(player.x, player.y, '#ff0000'); - - if (player.health <= 0) { - gameOver(); - } - } - }); - }; - - const updateGame = () => { - if (!gameActive) return; - - ctx.fillStyle = 'rgba(5, 11, 20, 0.3)'; - ctx.fillRect(0, 0, canvas.width, canvas.height); - - if (keys['KeyW'] || keys['ArrowUp']) player.y = Math.max(player.size / 2, player.y - player.speed); - if (keys['KeyS'] || keys['ArrowDown']) player.y = Math.min(canvas.height - player.size / 2, player.y + player.speed); - if (keys['KeyA'] || keys['ArrowLeft']) player.x = Math.max(player.size / 2, player.x - player.speed); - if (keys['KeyD'] || keys['ArrowRight']) player.x = Math.min(canvas.width - player.size / 2, player.x + player.speed); - - drawPlayer(player.x, player.y, player.size); - - spawnTimer++; - if (spawnTimer >= spawnRate) { - spawnBug(); - spawnTimer = 0; - } - - bullets = bullets.filter(b => b.update()); - bullets.forEach(b => b.draw()); - - bugs.forEach(bug => { - bug.update(player.x, player.y); - bug.draw(); - }); - - particles = particles.filter(p => p.update()); - particles.forEach(p => p.draw()); - - checkCollisions(); - - requestAnimationFrame(updateGame); - }; - - const formatTime = (seconds) => { - const mins = Math.floor(seconds / 60); - const secs = seconds % 60; - return `${mins}:${secs.toString().padStart(2, '0')}`; - }; - - const startTimer = () => { - gameTime = 0; - timerInterval = setInterval(() => { - gameTime++; - timeEl.innerText = formatTime(gameTime); - }, 1000); - }; - - const gameOver = () => { - gameActive = false; - clearInterval(timerInterval); - - finalKillsEl.innerText = kills; - finalTimeEl.innerText = formatTime(gameTime); - overlay.classList.remove('hidden'); - }; - - const startGame = () => { - gameActive = true; - kills = 0; - wave = 1; - gameTime = 0; - player = { x: canvas.width / 2, y: canvas.height / 2, size: 30, speed: 5, health: 100 }; - bugs = []; - bullets = []; - particles = []; - spawnTimer = 0; - spawnRate = 120; - - killEl.innerText = '0'; - waveEl.innerText = '1'; - timeEl.innerText = '0:00'; - overlay.classList.add('hidden'); - - startTimer(); - updateGame(); - }; - - window.addEventListener('keydown', (e) => { - keys[e.code] = true; - if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Space'].includes(e.code)) { - e.preventDefault(); - } - }); - - window.addEventListener('keyup', (e) => { - keys[e.code] = false; - }); - - canvas.addEventListener('mousemove', (e) => { - const rect = canvas.getBoundingClientRect(); - mouse.x = e.clientX - rect.left; - mouse.y = e.clientY - rect.top; - }); - - canvas.addEventListener('click', (e) => { - if (!gameActive) return; - - const now = Date.now(); - if (now - lastShot >= SHOOT_COOLDOWN) { - bullets.push(new Bullet(player.x, player.y, mouse.x, mouse.y)); - lastShot = now; - } - }); - - canvas.addEventListener('touchstart', (e) => { - e.preventDefault(); - const rect = canvas.getBoundingClientRect(); - const touch = e.touches[0]; - mouse.x = touch.clientX - rect.left; - mouse.y = touch.clientY - rect.top; - - const now = Date.now(); - if (now - lastShot >= SHOOT_COOLDOWN) { - bullets.push(new Bullet(player.x, player.y, mouse.x, mouse.y)); - lastShot = now; - } - }, { passive: false }); - - canvas.addEventListener('touchmove', (e) => { - e.preventDefault(); - const rect = canvas.getBoundingClientRect(); - const touch = e.touches[0]; - mouse.x = touch.clientX - rect.left; - mouse.y = touch.clientY - rect.top; - }, { passive: false }); - - startBtn.addEventListener('click', startGame); - restartBtn.addEventListener('click', startGame); - - window.addEventListener('resize', resize); - resize(); -} - -// --- 6. Neon Puzzle Game --- -function initNeonPuzzle() { - const canvas = document.getElementById('neonpuzzleCanvas'); - const ctx = canvas.getContext('2d'); - const startBtn = document.getElementById('startPuzzleBtn'); - const nextBtn = document.getElementById('nextPuzzleBtn'); - const levelEl = document.getElementById('puzzleLevel'); - const movesEl = document.getElementById('puzzleMoves'); - const overlay = document.getElementById('neonpuzzleOverlay'); - const finalMovesEl = document.getElementById('finalMoves'); - - let gameActive = false; - let level = 1; - let moves = 0; - let nodes = []; - let connections = []; - - const resize = () => { - canvas.width = canvas.parentElement.clientWidth; - canvas.height = canvas.parentElement.clientHeight; - }; - - class Node { - constructor(x, y) { - this.x = x; - this.y = y; - this.connections = []; - this.angle = 0; - } - - draw() { - ctx.save(); - ctx.translate(this.x, this.y); - ctx.rotate(this.angle); - - ctx.fillStyle = '#ff0066'; - ctx.shadowColor = '#ff0066'; - ctx.shadowBlur = 15; - ctx.beginPath(); - ctx.arc(0, 0, 20, 0, Math.PI * 2); - ctx.fill(); - - ctx.fillStyle = '#ffffff'; - ctx.shadowBlur = 0; - ctx.beginPath(); - ctx.arc(0, 0, 8, 0, Math.PI * 2); - ctx.fill(); - - ctx.restore(); - } - - rotate() { - this.angle += Math.PI / 4; - moves++; - movesEl.innerText = moves; - } - } - - const generateLevel = () => { - nodes = []; - connections = []; - moves = 0; - movesEl.innerText = '0'; - - const nodeCount = 4 + level; - const positions = []; - - for (let i = 0; i < nodeCount; i++) { - let x, y; - let valid = false; - let attempts = 0; - - while (!valid && attempts < 100) { - x = 100 + Math.random() * (canvas.width - 200); - y = 100 + Math.random() * (canvas.height - 200); - valid = true; - - for (const pos of positions) { - const dist = Math.sqrt((x - pos.x) ** 2 + (y - pos.y) ** 2); - if (dist < 80) valid = false; - } - attempts++; - } - - if (valid) { - positions.push({ x, y }); - nodes.push(new Node(x, y)); - } - } - - for (let i = 0; i < nodes.length - 1; i++) { - connections.push([i, i + 1]); - nodes[i].connections.push(i + 1); - nodes[i + 1].connections.push(i); - } - - connections.push([nodes.length - 1, 0]); - nodes[0].connections.push(nodes.length - 1); - nodes[nodes.length - 1].connections.push(0); - }; - - const checkWin = () => { - for (const [from, to] of connections) { - const nodeA = nodes[from]; - const nodeB = nodes[to]; - const targetAngle = Math.atan2(nodeB.y - nodeA.y, nodeB.x - nodeA.x); - let currentAngle = nodeA.angle % (Math.PI * 2); - if (currentAngle < 0) currentAngle += Math.PI * 2; - let diff = Math.abs(targetAngle - currentAngle); - if (diff > Math.PI) diff = Math.PI * 2 - diff; - - if (diff > 0.3) return false; - } - - return true; - }; - - const updateGame = () => { - if (!gameActive) return; - - ctx.fillStyle = 'rgba(5, 11, 20, 0.3)'; - ctx.fillRect(0, 0, canvas.width, canvas.height); - - connections.forEach(([from, to]) => { - const nodeA = nodes[from]; - const nodeB = nodes[to]; - const aligned = Math.abs(nodeA.angle - Math.atan2(nodeB.y - nodeA.y, nodeB.x - nodeA.x)) < 0.3; - - ctx.strokeStyle = aligned ? '#00ff66' : '#ff6600'; - ctx.lineWidth = 3; - ctx.shadowColor = aligned ? '#00ff66' : '#ff6600'; - ctx.shadowBlur = aligned ? 20 : 10; - ctx.beginPath(); - ctx.moveTo(nodeA.x, nodeA.y); - ctx.lineTo(nodeB.x, nodeB.y); - ctx.stroke(); - }); - - nodes.forEach(node => node.draw()); - - requestAnimationFrame(updateGame); - }; - - const startGame = () => { - gameActive = true; - level = 1; - levelEl.innerText = level; - generateLevel(); - overlay.classList.add('hidden'); - updateGame(); - }; - - const nextLevel = () => { - level++; - levelEl.innerText = level; - generateLevel(); - overlay.classList.add('hidden'); - }; - - canvas.addEventListener('click', (e) => { - if (!gameActive) return; - - const rect = canvas.getBoundingClientRect(); - const x = e.clientX - rect.left; - const y = e.clientY - rect.top; - - for (const node of nodes) { - const dist = Math.sqrt((x - node.x) ** 2 + (y - node.y) ** 2); - if (dist < 25) { - node.rotate(); - break; - } - } - - if (checkWin()) { - gameActive = false; - finalMovesEl.innerText = moves; - overlay.classList.remove('hidden'); - } - }); - - startBtn.addEventListener('click', startGame); - nextBtn.addEventListener('click', nextLevel); - - window.addEventListener('resize', resize); - resize(); -} - -// --- 7. Rhythm Beat Game --- -function initRhythmBeat() { - const canvas = document.getElementById('rhythmCanvas'); - const ctx = canvas.getContext('2d'); - const startBtn = document.getElementById('startRhythmBtn'); - const restartBtn = document.getElementById('restartRhythmBtn'); - const scoreEl = document.getElementById('rhythmScore'); - const comboEl = document.getElementById('rhythmCombo'); - const streakEl = document.getElementById('rhythmStreak'); - const overlay = document.getElementById('rhythmOverlay'); - const finalScoreEl = document.getElementById('finalRhythmScore'); - - let gameActive = false; - let score = 0; - let combo = 0; - let streak = 0; - let beats = []; - let beatInterval = 0; - let totalBeats = 0; - let maxBeats = 50; - let keyFeedback = { D: 0, F: 0, J: 0, K: 0 }; - const keyLabels = ['D', 'F', 'J', 'K']; - const keyColors = ['#ff0066', '#ff6600', '#00ff66', '#00f2ff']; - const laneX = []; - - const resize = () => { - canvas.width = canvas.parentElement.clientWidth; - canvas.height = canvas.parentElement.clientHeight; - const laneWidth = canvas.width / 4; - for (let i = 0; i < 4; i++) { - laneX[i] = laneWidth * i + laneWidth / 2; - } - }; - - const drawKeyIndicators = () => { - keyLabels.forEach((label, i) => { - const x = laneX[i]; - const y = canvas.height - 50; - const feedback = keyFeedback[label]; - - ctx.save(); - ctx.strokeStyle = keyColors[i]; - ctx.shadowColor = keyColors[i]; - ctx.shadowBlur = 15 + feedback * 20; - ctx.lineWidth = 4; - ctx.beginPath(); - ctx.arc(x, y, 30, 0, Math.PI * 2); - ctx.stroke(); - - ctx.fillStyle = feedback > 0 ? keyColors[i] : 'rgba(255,255,255,0.3)'; - ctx.shadowBlur = feedback * 20; - ctx.beginPath(); - ctx.arc(x, y, 25, 0, Math.PI * 2); - ctx.fill(); - - ctx.fillStyle = '#ffffff'; - ctx.shadowBlur = 0; - ctx.font = 'bold 20px Arial'; - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.fillText(label, x, y); - ctx.restore(); - - if (feedback > 0) keyFeedback[label] -= 0.1; - }); - }; - - const drawBeat = (beat) => { - const x = laneX[beat.lane]; - const y = canvas.height - 50 - beat.y; - const progress = beat.y / (canvas.height - 100); - - ctx.save(); - ctx.strokeStyle = keyColors[beat.lane]; - ctx.shadowColor = keyColors[beat.lane]; - ctx.shadowBlur = 20; - ctx.lineWidth = 4; - ctx.beginPath(); - ctx.arc(x, y, 20, 0, Math.PI * 2); - ctx.stroke(); - - ctx.fillStyle = keyColors[beat.lane]; - ctx.beginPath(); - ctx.arc(x, y, 12, 0, Math.PI * 2); - ctx.fill(); - ctx.restore(); - }; - - const spawnBeat = () => { - if (totalBeats >= maxBeats) return; - const lane = Math.floor(Math.random() * 4); - beats.push({ y: 0, lane: lane, hit: false }); - totalBeats++; - }; - - const updateGame = () => { - if (!gameActive) return; - - ctx.fillStyle = 'rgba(5, 11, 20, 0.3)'; - ctx.fillRect(0, 0, canvas.width, canvas.height); - - for (let i = 0; i < 4; i++) { - ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)'; - ctx.lineWidth = 1; - ctx.beginPath(); - ctx.moveTo(laneX[i], 0); - ctx.lineTo(laneX[i], canvas.height); - ctx.stroke(); - } - - beatInterval++; - if (beatInterval % 40 === 0) { - spawnBeat(); - } - - beats = beats.filter(beat => { - if (beat.hit) return false; - beat.y += 4; - drawBeat(beat); - - if (beat.y > canvas.height - 50) { - combo = 0; - streak = 0; - comboEl.innerText = '0'; - streakEl.innerText = '0'; - return false; - } - return true; - }); - - drawKeyIndicators(); - - if (totalBeats >= maxBeats && beats.length === 0) { - gameActive = false; - finalScoreEl.innerText = score; - overlay.classList.remove('hidden'); - } - - requestAnimationFrame(updateGame); - }; - - const startGame = () => { - gameActive = true; - score = 0; - combo = 0; - streak = 0; - beats = []; - beatInterval = 0; - totalBeats = 0; - keyFeedback = { D: 0, F: 0, J: 0, K: 0 }; - scoreEl.innerText = '0'; - comboEl.innerText = '0'; - streakEl.innerText = '0'; - overlay.classList.add('hidden'); - updateGame(); - }; - - window.addEventListener('keydown', (e) => { - if (!gameActive) return; - const keyIdx = keyLabels.indexOf(e.code.replace('Key', '')); - if (keyIdx === -1) return; - - keyFeedback[keyLabels[keyIdx]] = 1; - - for (const beat of beats) { - if (beat.lane === keyIdx && !beat.hit) { - const distance = Math.abs(beat.y - (canvas.height - 100)); - if (distance < 40) { - beat.hit = true; - if (distance < 15) { - score += 100 + combo * 20; - combo++; - streak++; - } else if (distance < 25) { - score += 50 + combo * 10; - combo++; - streak++; - } else { - score += 20; - combo = 0; - } - scoreEl.innerText = score; - comboEl.innerText = combo; - streakEl.innerText = streak; - break; - } - } - } - }); - - startBtn.addEventListener('click', startGame); - restartBtn.addEventListener('click', startGame); - - window.addEventListener('resize', resize); - resize(); -} - -// --- 8. Cosmic Arena Game --- -function initCosmicArena() { - const canvas = document.getElementById('arenaCanvas'); - const ctx = canvas.getContext('2d'); - const startBtn = document.getElementById('startArenaBtn'); - const restartBtn = document.getElementById('restartArenaBtn'); - const hpEl = document.getElementById('arenaHP'); - const killsEl = document.getElementById('arenaKills'); - const waveEl = document.getElementById('arenaWave'); - const overlay = document.getElementById('arenaOverlay'); - const finalKillsEl = document.getElementById('finalArenaKills'); - - let gameActive = false; - let player = { x: 0, y: 0, hp: 100, speed: 4, size: 30 }; - let enemies = []; - let bullets = []; - let particles = []; - let kills = 0; - let wave = 1; - let enemySpawnTimer = 0; - let spawnRate = 90; - - const keys = {}; - const mouse = { x: 0, y: 0 }; - - const resize = () => { - canvas.width = canvas.parentElement.clientWidth; - canvas.height = canvas.parentElement.clientHeight; - }; - - const drawPlayer = () => { - ctx.save(); - ctx.translate(player.x, player.y); - - const angle = Math.atan2(mouse.y - player.y, mouse.x - player.x); - ctx.rotate(angle); - - ctx.fillStyle = '#00ffcc'; - ctx.shadowColor = '#00ffcc'; - ctx.shadowBlur = 15; - ctx.beginPath(); - ctx.moveTo(20, 0); - ctx.lineTo(-15, -10); - ctx.lineTo(-15, 10); - ctx.closePath(); - ctx.fill(); - - ctx.restore(); - }; - - const drawEnemy = (enemy) => { - ctx.save(); - ctx.translate(enemy.x, enemy.y); - - ctx.fillStyle = enemy.type; - ctx.shadowColor = enemy.type; - ctx.shadowBlur = 10; - - const pulse = Math.sin(Date.now() * 0.01) * 0.2 + 1; - ctx.scale(pulse, pulse); - - ctx.beginPath(); - for (let i = 0; i < 6; i++) { - const angle = (i / 6) * Math.PI * 2; - const x = Math.cos(angle) * enemy.size; - const y = Math.sin(angle) * enemy.size; - if (i === 0) ctx.moveTo(x, y); - else ctx.lineTo(x, y); - } - ctx.closePath(); - ctx.fill(); - - ctx.restore(); - }; - - const drawBullet = (bullet) => { - ctx.save(); - ctx.fillStyle = '#ffffff'; - ctx.shadowColor = '#ffffff'; - ctx.shadowBlur = 10; - ctx.beginPath(); - ctx.arc(bullet.x, bullet.y, 4, 0, Math.PI * 2); - ctx.fill(); - ctx.restore(); - }; - - class Particle { - constructor(x, y, color) { - this.x = x; - this.y = y; - this.color = color; - this.vx = (Math.random() - 0.5) * 6; - this.vy = (Math.random() - 0.5) * 6; - this.life = 1; - this.decay = 0.03 + Math.random() * 0.02; - } - update() { - this.x += this.vx; - this.y += this.vy; - this.life -= this.decay; - } - draw() { - ctx.save(); - ctx.globalAlpha = this.life; - ctx.fillStyle = this.color; - ctx.shadowColor = this.color; - ctx.shadowBlur = 10; - ctx.beginPath(); - ctx.arc(this.x, this.y, 3, 0, Math.PI * 2); - ctx.fill(); - ctx.restore(); - } - } - - const createExplosion = (x, y, color) => { - for (let i = 0; i < 15; i++) { - particles.push(new Particle(x, y, color)); - } - }; - - const spawnEnemy = () => { - const side = Math.floor(Math.random() * 4); - let x, y; - switch(side) { - case 0: x = -30; y = Math.random() * canvas.height; break; - case 1: x = canvas.width + 30; y = Math.random() * canvas.height; break; - case 2: x = Math.random() * canvas.width; y = -30; break; - case 3: x = Math.random() * canvas.width; y = canvas.height + 30; break; - } - - const types = ['#ff0066', '#ff6600', '#ffcc00']; - enemies.push({ - x, y, - size: 15 + Math.random() * 10, - speed: 1 + wave * 0.2, - hp: 2 + Math.floor(wave * 0.5), - type: types[Math.floor(Math.random() * types.length)] - }); - }; - - const updateGame = () => { - if (!gameActive) return; - - ctx.fillStyle = 'rgba(5, 11, 20, 0.2)'; - ctx.fillRect(0, 0, canvas.width, canvas.height); - - if (keys['KeyW'] || keys['ArrowUp']) player.y = Math.max(player.size, player.y - player.speed); - if (keys['KeyS'] || keys['ArrowDown']) player.y = Math.min(canvas.height - player.size, player.y + player.speed); - if (keys['KeyA'] || keys['ArrowLeft']) player.x = Math.max(player.size, player.x - player.speed); - if (keys['KeyD'] || keys['ArrowRight']) player.x = Math.min(canvas.width - player.size, player.x + player.speed); - - drawPlayer(); - - enemySpawnTimer++; - if (enemySpawnTimer >= spawnRate) { - spawnEnemy(); - enemySpawnTimer = 0; - } - - enemies = enemies.filter(enemy => { - const dx = player.x - enemy.x; - const dy = player.y - enemy.y; - const dist = Math.sqrt(dx * dx + dy * dy); - - if (dist > 0) { - enemy.x += (dx / dist) * enemy.speed; - enemy.y += (dy / dist) * enemy.speed; - } - - if (dist < player.size + enemy.size) { - player.hp -= 10; - hpEl.innerText = Math.max(0, player.hp); - - if (player.hp <= 0) { - gameActive = false; - finalKillsEl.innerText = kills; - overlay.classList.remove('hidden'); - } - } - - drawEnemy(enemy); - return true; - }); - - bullets = bullets.filter(bullet => { - bullet.x += bullet.vx; - bullet.y += bullet.vy; - - for (let i = enemies.length - 1; i >= 0; i--) { - const enemy = enemies[i]; - const dx = bullet.x - enemy.x; - const dy = bullet.y - enemy.y; - const dist = Math.sqrt(dx * dx + dy * dy); - - if (dist < enemy.size) { - enemy.hp--; - createExplosion(bullet.x, bullet.y, '#ffffff'); - if (enemy.hp <= 0) { - createExplosion(enemy.x, enemy.y, enemy.type); - enemies.splice(i, 1); - kills++; - killsEl.innerText = kills; - - if (kills % 5 === 0) { - wave++; - waveEl.innerText = wave; - spawnRate = Math.max(30, 90 - wave * 5); - } - } - return false; - } - } - - if (bullet.x < 0 || bullet.x > canvas.width || bullet.y < 0 || bullet.y > canvas.height) { - return false; - } - - drawBullet(bullet); - return true; - }); - - particles = particles.filter(p => { - p.update(); - p.draw(); - return p.life > 0; - }); - - requestAnimationFrame(updateGame); - }; - - const startGame = () => { - gameActive = true; - player = { x: canvas.width / 2, y: canvas.height / 2, hp: 100, speed: 4, size: 30 }; - enemies = []; - bullets = []; - particles = []; - kills = 0; - wave = 1; - enemySpawnTimer = 0; - spawnRate = 90; - hpEl.innerText = '100'; - killsEl.innerText = '0'; - waveEl.innerText = '1'; - overlay.classList.add('hidden'); - updateGame(); - }; - - window.addEventListener('keydown', (e) => keys[e.code] = true); - window.addEventListener('keyup', (e) => keys[e.code] = false); - - canvas.addEventListener('mousemove', (e) => { - const rect = canvas.getBoundingClientRect(); - mouse.x = e.clientX - rect.left; - mouse.y = e.clientY - rect.top; - }); - - canvas.addEventListener('click', (e) => { - if (!gameActive) return; - const dx = mouse.x - player.x; - const dy = mouse.y - player.y; - const dist = Math.sqrt(dx * dx + dy * dy); - bullets.push({ - x: player.x, y: player.y, - vx: (dx / dist) * 10, - vy: (dy / dist) * 10 - }); - }); - - startBtn.addEventListener('click', startGame); - restartBtn.addEventListener('click', startGame); - - window.addEventListener('resize', resize); - resize(); -} - -// --- 9. Crystal Tetris Game --- -function initTetris() { - const canvas = document.getElementById('tetrisCanvas'); - const ctx = canvas.getContext('2d'); - const startBtn = document.getElementById('startTetrisBtn'); - const restartBtn = document.getElementById('restartTetrisBtn'); - const scoreEl = document.getElementById('tetrisScore'); - const linesEl = document.getElementById('tetrisLines'); - const levelEl = document.getElementById('tetrisLevel'); - const overlay = document.getElementById('tetrisOverlay'); - const finalScoreEl = document.getElementById('finalTetrisScore'); - - let gameActive = false; - let score = 0; - let lines = 0; - let level = 1; - let grid = []; - let currentPiece = null; - let frameCount = 0; - let animationId = null; - const COLS = 10; - const ROWS = 20; - const BLOCK_SIZE = 25; - const TARGET_FPS = 120; - const FRAME_INTERVAL = 1000 / TARGET_FPS; - let lastTime = 0; - - const COLORS = ['#ff0066', '#ff6600', '#ffcc00', '#00ff66', '#00f2ff', '#7000ff', '#ff00ff']; - const SHAPES = [ - [[1, 1, 1, 1]], - [[1, 1, 1], [1]], - [[1, 1], [1, 1]], - [[0, 1, 0], [1, 1, 1]], - [[1, 0, 0], [1, 1, 1]], - [[0, 1, 1], [1, 1, 0]], - [[0, 1, 1, 1], [1, 0, 0, 0]] - ]; - - const getDropSpeed = () => { - const framesPerDrop = Math.max(2, 120 - (level - 1) * 10); - return framesPerDrop; - }; - - const resize = () => { - canvas.width = canvas.parentElement.clientWidth; - canvas.height = canvas.parentElement.clientHeight; - }; - - const createGrid = () => { - grid = []; - for (let r = 0; r < ROWS; r++) { - grid[r] = []; - for (let c = 0; c < COLS; c++) { - grid[r][c] = 0; - } - } - }; - - const createPiece = () => { - const shapeIdx = Math.floor(Math.random() * SHAPES.length); - const color = COLORS[shapeIdx]; - currentPiece = { - shape: SHAPES[shapeIdx], - color: color, - x: Math.floor(COLS / 2) - 1, - y: 0 - }; - }; - - const drawBlock = (x, y, color) => { - ctx.fillStyle = color; - ctx.shadowColor = color; - ctx.shadowBlur = 5; - ctx.fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE - 1, BLOCK_SIZE - 1); - - ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)'; - ctx.lineWidth = 2; - ctx.strokeRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE - 1, BLOCK_SIZE - 1); - }; - - const drawGrid = () => { - ctx.fillStyle = 'rgba(5, 11, 20, 0.3)'; - ctx.fillRect(0, 0, canvas.width, canvas.height); - - for (let r = 0; r < ROWS; r++) { - for (let c = 0; c < COLS; c++) { - if (grid[r][c]) { - drawBlock(c, r, grid[r][c]); - } - } - } - }; - - const drawPiece = () => { - if (!currentPiece) return; - currentPiece.shape.forEach((row, r) => { - row.forEach((value, c) => { - if (value) { - drawBlock(currentPiece.x + c, currentPiece.y + r, currentPiece.color); - } - }); - }); - }; - - const isValidMove = (piece, offsetX, offsetY) => { - return piece.shape.every((row, r) => { - return row.every((value, c) => { - if (!value) return true; - const newX = piece.x + c + offsetX; - const newY = piece.y + r + offsetY; - return newX >= 0 && newX < COLS && newY < ROWS && newY >= 0 && !grid[newY][newX]; - }); - }); - }; - - const lockPiece = () => { - currentPiece.shape.forEach((row, r) => { - row.forEach((value, c) => { - if (value) { - grid[currentPiece.y + r][currentPiece.x + c] = currentPiece.color; - } - }); - }); - - clearLines(); - createPiece(); - - if (!isValidMove(currentPiece, 0, 0)) { - gameOver(); - } - }; - - const clearLines = () => { - let linesCleared = 0; - for (let r = ROWS - 1; r >= 0; r--) { - if (grid[r].every(cell => cell !== 0)) { - grid.splice(r, 1); - grid.unshift(Array(COLS).fill(0)); - linesCleared++; - } - } - - if (linesCleared > 0) { - lines += linesCleared; - score += linesCleared * 100 * linesCleared; - level = Math.floor(lines / 10) + 1; - scoreEl.innerText = score; - linesEl.innerText = lines; - levelEl.innerText = level; - } - }; - - const updateGame = (timestamp) => { - if (!gameActive) return; - - const deltaTime = timestamp - lastTime; - - if (deltaTime >= FRAME_INTERVAL) { - lastTime = timestamp; - frameCount++; - - const dropSpeed = getDropSpeed(); - if (frameCount % dropSpeed === 0) { - if (isValidMove(currentPiece, 0, 1)) { - currentPiece.y++; - } else { - lockPiece(); - } - } - } - - draw(); - animationId = requestAnimationFrame(updateGame); - }; - - const draw = () => { - drawGrid(); - drawPiece(); - }; - - const startGame = () => { - gameActive = true; - score = 0; - lines = 0; - level = 1; - frameCount = 0; - lastTime = performance.now(); - createGrid(); - createPiece(); - scoreEl.innerText = '0'; - linesEl.innerText = '0'; - levelEl.innerText = '1'; - overlay.classList.add('hidden'); - - if (animationId) cancelAnimationFrame(animationId); - animationId = requestAnimationFrame(updateGame); - }; - - const gameOver = () => { - gameActive = false; - if (animationId) cancelAnimationFrame(animationId); - finalScoreEl.innerText = score; - overlay.classList.remove('hidden'); - }; - - window.addEventListener('keydown', (e) => { - if (!gameActive || !currentPiece) return; - - switch(e.code) { - case 'ArrowLeft': - if (isValidMove(currentPiece, -1, 0)) currentPiece.x--; - break; - case 'ArrowRight': - if (isValidMove(currentPiece, 1, 0)) currentPiece.x++; - break; - case 'ArrowDown': - if (isValidMove(currentPiece, 0, 1)) { - currentPiece.y++; - score += 1; - scoreEl.innerText = score; - if (!isValidMove(currentPiece, 0, 1)) { - lockPiece(); - } - } - break; - case 'ArrowUp': - const rotated = currentPiece.shape[0].map((_, i) => currentPiece.shape.map(row => row[i]).reverse()); - if (isValidMove({ ...currentPiece, shape: rotated }, 0, 0)) { - currentPiece.shape = rotated; - } - break; - case 'Space': - while (isValidMove(currentPiece, 0, 1)) { - currentPiece.y++; - score += 2; - } - scoreEl.innerText = score; - lockPiece(); - break; - } - - draw(); - }); - - startBtn.addEventListener('click', startGame); - restartBtn.addEventListener('click', startGame); - - window.addEventListener('resize', resize); - resize(); -} - -// --- 10. Aurora Jumper Platformer Game --- -function initPlatformer() { - const canvas = document.getElementById('platformerCanvas'); - const ctx = canvas.getContext('2d'); - const startBtn = document.getElementById('startPlatformerBtn'); - const restartBtn = document.getElementById('restartPlatformerBtn'); - const coinsEl = document.getElementById('coinsVal'); - const timeEl = document.getElementById('platformerTime'); - const overlay = document.getElementById('platformerOverlay'); - const finalCoinsEl = document.getElementById('finalCoins'); - const finalTimeEl = document.getElementById('finalPlatformerTime'); - - let gameActive = false; - let player = { x: 50, y: 300, vx: 0, vy: 0, onGround: false, size: 25 }; - let platforms = []; - let coins = []; - let collected = 0; - let gameTime = 0; - let timerInterval = null; - let gravity = 0.5; - let jumpForce = -12; - const keys = {}; - - const resize = () => { - canvas.width = canvas.parentElement.clientWidth; - canvas.height = canvas.parentElement.clientHeight; - }; - - const generateLevel = () => { - platforms = []; - coins = []; - - const platformCount = 8 + Math.floor(Math.random() * 5); - const jumpHeight = Math.abs(jumpForce * jumpForce / (2 * gravity)); - const maxReachableY = jumpHeight * 0.9; - - let prevX = 50; - let prevY = canvas.height - 80; - - for (let i = 0; i < platformCount; i++) { - let x, y; - - if (i === 0) { - x = 30; - y = canvas.height - 80; - } else if (i === platformCount - 1) { - x = canvas.width - 130; - y = prevY - maxReachableY + Math.random() * 20; - } else { - const minDeltaX = 40; - const maxDeltaX = canvas.width / platformCount - 20; - x = prevX + minDeltaX + Math.random() * maxDeltaX; - x = Math.max(50, Math.min(canvas.width - 130, x)); - - const direction = Math.random() > 0.5 ? 1 : -1; - y = prevY - (maxReachableY * 0.4 + Math.random() * (maxReachableY * 0.5)) * direction; - y = Math.max(80, Math.min(canvas.height - 80, y)); - } - - const width = 100 + Math.random() * 40; - platforms.push({ x, y, width: Math.floor(width), height: 15, type: i === platformCount - 1 ? 'goal' : 'normal' }); - - if (i < platformCount - 1 && Math.random() > 0.2) { - coins.push({ - x: x + width / 2, - y: y - 30, - size: 12, - collected: false - }); - } - - prevX = x; - prevY = y; - } - - player.x = platforms[0].x + platforms[0].width / 2; - player.y = platforms[0].y - player.size; - player.vx = 0; - player.vy = 0; - }; - - const drawPlayer = () => { - ctx.save(); - - ctx.fillStyle = '#00ff00'; - ctx.shadowColor = '#00ff00'; - ctx.shadowBlur = 15; - - ctx.beginPath(); - ctx.roundRect(player.x - player.size / 2, player.y - player.size, player.size, player.size, 5); - ctx.fill(); - - ctx.fillStyle = '#ffffff'; - ctx.beginPath(); - ctx.arc(player.x + 3, player.y - player.size + 8, 4, 0, Math.PI * 2); - ctx.fill(); - - ctx.restore(); - }; - - const drawPlatform = (platform) => { - ctx.fillStyle = platform.type === 'goal' ? '#00ff66' : '#0099cc'; - ctx.shadowColor = platform.type === 'goal' ? '#00ff00' : '#00f2ff'; - ctx.shadowBlur = 10; - ctx.fillRect(platform.x, platform.y, platform.width, platform.height); - - if (platform.type === 'goal') { - ctx.fillStyle = '#ffffff'; - ctx.font = '12px Orbitron'; - ctx.fillText('GOAL', platform.x + platform.width / 2 - 20, platform.y + 12); - } - }; - - const drawCoin = (coin) => { - if (coin.collected) return; - - ctx.save(); - const pulse = Math.sin(Date.now() * 0.01) * 0.2 + 1; - ctx.translate(coin.x, coin.y); - ctx.scale(pulse, pulse); - - ctx.fillStyle = '#ffcc00'; - ctx.shadowColor = '#ffcc00'; - ctx.shadowBlur = 15; - ctx.beginPath(); - ctx.arc(0, 0, coin.size, 0, Math.PI * 2); - ctx.fill(); - - ctx.fillStyle = '#ffffff'; - ctx.beginPath(); - ctx.arc(0, 0, coin.size / 2, 0, Math.PI * 2); - ctx.fill(); - - ctx.restore(); - }; - - const updateGame = () => { - if (!gameActive) return; - - ctx.fillStyle = 'rgba(5, 11, 20, 0.2)'; - ctx.fillRect(0, 0, canvas.width, canvas.height); - - if (keys['KeyA'] || keys['ArrowLeft']) player.vx = -5; - else if (keys['KeyD'] || keys['ArrowRight']) player.vx = 5; - else player.vx *= 0.8; - - if ((keys['Space'] || keys['ArrowUp']) && player.onGround) { - player.vy = jumpForce; - player.onGround = false; - } - - player.vy += gravity; - player.x += player.vx; - player.y += player.vy; - - player.onGround = false; - platforms.forEach(platform => { - if (player.x + player.size / 2 > platform.x && - player.x - player.size / 2 < platform.x + platform.width && - player.y >= platform.y && - player.y <= platform.y + platform.height + player.vy + 5) { - if (player.vy > 0) { - player.y = platform.y; - player.vy = 0; - player.onGround = true; - } - - if (platform.type === 'goal') { - gameActive = false; - clearInterval(timerInterval); - finalCoinsEl.innerText = collected; - finalTimeEl.innerText = timeEl.innerText; - overlay.classList.remove('hidden'); - } - } - }); - - player.x = Math.max(player.size / 2, Math.min(canvas.width - player.size / 2, player.x)); - player.y = Math.min(canvas.height, player.y); - - coins.forEach(coin => { - if (coin.collected) return; - - const dx = player.x - coin.x; - const dy = player.y - coin.y; - const dist = Math.sqrt(dx * dx + dy * dy); - - if (dist < player.size + coin.size) { - coin.collected = true; - collected++; - coinsEl.innerText = collected; - } - - drawCoin(coin); - }); - - platforms.forEach(drawPlatform); - drawPlayer(); - - if (player.y > canvas.height + 50) { - player.x = platforms[0].x + platforms[0].width / 2; - player.y = platforms[0].y - player.size; - player.vx = 0; - player.vy = 0; - collected = Math.max(0, collected - 1); - coinsEl.innerText = collected; - } - - requestAnimationFrame(updateGame); - }; - - const formatTime = (seconds) => { - const mins = Math.floor(seconds / 60); - const secs = seconds % 60; - return `${mins}:${secs.toString().padStart(2, '0')}`; - }; - - const startTimer = () => { - gameTime = 0; - timerInterval = setInterval(() => { - gameTime++; - timeEl.innerText = formatTime(gameTime); - }, 1000); - }; - - const startGame = () => { - gameActive = true; - collected = 0; - if (timerInterval) clearInterval(timerInterval); - generateLevel(); - coinsEl.innerText = '0'; - timeEl.innerText = '0:00'; - overlay.classList.add('hidden'); - startTimer(); - updateGame(); - }; - - window.addEventListener('keydown', (e) => keys[e.code] = true); - window.addEventListener('keyup', (e) => keys[e.code] = false); - - startBtn.addEventListener('click', startGame); - restartBtn.addEventListener('click', startGame); - - window.addEventListener('resize', resize); - resize(); -} - -// Export/Import Messages -window.exportMessages = () => { - const messages = JSON.parse(localStorage.getItem('trae_messages') || '[]'); - const dataStr = JSON.stringify(messages, null, 2); - const blob = new Blob([dataStr], { type: 'application/json' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = `trae_messages_${new Date().toISOString().split('T')[0]}.json`; - a.click(); - URL.revokeObjectURL(url); -}; - -window.importMessages = (event) => { - const file = event.target.files[0]; - if (!file) return; - - const reader = new FileReader(); - reader.onload = (e) => { - try { - const data = JSON.parse(e.target.result); - if (Array.isArray(data)) { - localStorage.setItem('trae_messages', JSON.stringify(data)); - location.reload(); - } - } catch (err) { - alert('Invalid file format'); - } - }; - reader.readAsText(file); }; diff --git a/index.html b/index.html index aaf0743..ccfb867 100644 --- a/index.html +++ b/index.html @@ -164,8 +164,10 @@

GAME OVER

+
+

New players get a secret code automatically

@@ -175,7 +177,17 @@
-

Leaderboard

+
+

Global Leaderboard

+ + + + + + + + +
@@ -202,7 +214,12 @@

GAME OVER

Bugs Fixed: 0

Time Survived: 0:00

- +
+ + + +
+

New players get a secret code

@@ -228,7 +245,12 @@ @@ -255,7 +277,12 @@ @@ -282,7 +309,12 @@ @@ -309,7 +341,12 @@ @@ -336,7 +373,12 @@

LEVEL COMPLETE

Coins: 0

Time: 0:00

- +
+ + + +
+

New players get a secret code

diff --git a/style.css b/style.css index 2ca21e0..555165b 100644 --- a/style.css +++ b/style.css @@ -35,7 +35,7 @@ body { } .app-container { - max-width: 1200px; + max-width: 1400px; margin: 0 auto; padding: 0 20px; width: 100%; @@ -913,30 +913,147 @@ body { text-align: left; } -.leaderboard h3 { - font-family: var(--font-accent); - text-align: center; +.leaderboard-header { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 15px; margin-bottom: 20px; +} + +.leaderboard-header h3 { + font-family: var(--font-accent); color: var(--aurora-cyan); + margin: 0; +} + +.tab-btn { + background: var(--glass-bg); + border: 1px solid var(--glass-border); + color: var(--text-primary); + padding: 8px 16px; + border-radius: 12px; + cursor: pointer; + font-family: var(--font-accent); + font-size: 0.85rem; + transition: all 0.3s ease; +} + +.tab-btn:hover { + background: rgba(0, 255, 102, 0.2); + border-color: var(--trae-green); +} + +.tab-btn.active { + background: var(--trae-green); + color: var(--bg-dark); + border-color: var(--trae-green); } .leader-item { display: flex; justify-content: space-between; - padding: 10px 20px; + padding: 12px 20px; border-bottom: 1px solid var(--glass-border); font-family: var(--font-accent); + align-items: center; + transition: background 0.3s ease; +} + +.leader-item:hover { + background: rgba(0, 255, 102, 0.1); } .leader-item:last-child { border: none; } -.leader-rank { color: var(--trae-green); } -.leader-name { flex: 1; margin-left: 20px; } -.leader-score { color: var(--aurora-cyan); } -.leader-duration { color: #ff9900; font-size: 0.85rem; margin-left: 10px; } -.leader-date { color: #888; font-size: 0.8rem; margin-left: 10px; } +.leader-rank { + color: var(--trae-green); + font-weight: bold; + min-width: 40px; +} + +.leader-name { + flex: 1; + margin-left: 20px; + min-width: 0; +} + +.leader-score { + color: var(--aurora-cyan); + font-weight: bold; + min-width: 80px; + text-align: center; +} + +.leader-game { + color: #ff6600; + font-size: 0.8rem; + padding: 4px 10px; + border-radius: 8px; + background: rgba(255, 102, 0, 0.1); + margin-right: 10px; +} + +.leader-code { + color: #7000ff; + font-size: 0.75rem; + margin-left: 10px; + padding: 4px 10px; + border-radius: 6px; + background: rgba(112, 0, 255, 0.1); + cursor: pointer; + transition: all 0.3s ease; +} + +.leader-code:hover { + background: rgba(112, 0, 255, 0.2); +} + +.leader-duration { + color: #ff9900; + font-size: 0.85rem; + margin-left: 10px; +} + +.leader-date { + color: #888; + font-size: 0.8rem; + margin-left: 10px; +} + +.code-hint { + color: #888; + font-size: 0.8rem; + text-align: center; + margin-top: 10px; + font-style: italic; +} + +.export-buttons { + display: flex; + gap: 15px; + justify-content: center; + margin-top: 20px; +} + +.export-btn { + background: var(--glass-bg); + border: 1px solid var(--glass-border); + color: var(--text-primary); + padding: 10px 20px; + border-radius: 12px; + cursor: pointer; + font-family: var(--font-accent); + transition: all 0.3s ease; +} + +.export-btn:hover { + background: rgba(0, 255, 102, 0.2); + border-color: var(--trae-green); +} @keyframes fadeIn { from { opacity: 0; transform: scale(0.9); } @@ -1039,24 +1156,132 @@ footer span, .glm-link, .author-link, .gemini-link { .forge-input-group { flex-direction: column; } .btn-secondary { margin-left: 0; margin-top: 15px; } + .app-container { padding: 0 10px; } + + .hero h1 { font-size: 2rem; } + .app-icons { gap: 15px; } + .app-icon-inner { width: 60px; height: 60px; font-size: 30px; } + .app-label { font-size: 0.75rem; } + + .game-container { + padding: 15px; + gap: 20px; + } + + .section-card { + padding: 20px; + } + + .game-container, .traoom-container, .neonpuzzle-container, .rhythm-container, .arena-container, .platformer-container { + height: 50vh; + min-height: 350px; + } + + canvas { + width: 100%; + height: 100%; + } + + .game-overlay { + padding: 25px 20px; + margin: 10px; + } + + .input-group { + flex-direction: column; + gap: 10px; + } + + .input-group input { + width: 100%; + padding: 10px; + font-size: 0.9rem; + } + + .btn-primary, .export-btn { + padding: 10px 15px; + font-size: 0.9rem; + width: 100%; + } + + .leaderboard-header { + flex-direction: column; + align-items: stretch; + } + + .tab-btn { + padding: 8px 10px; + font-size: 0.75rem; + } + + .leader-item { + flex-wrap: wrap; + padding: 10px; + gap: 5px; + } + + .leader-game, .leader-code { + display: none; + } + + .game-timer { + top: 10px; + right: 10px; + font-size: 1rem; + } + + .game-ui > div { + flex-direction: column; + gap: 5px; + font-size: 0.8rem; + } + .traoom-container { - height: 60vh; - min-height: 400px; + height: 50vh; + min-height: 350px; } .traoom-stats { flex-direction: column; - gap: 8px; - left: 10px; - top: 10px; + gap: 5px; + left: 8px; + top: 8px; } .traoom-stat { - padding: 6px 12px; + padding: 5px 10px; + font-size: 0.75rem; + } + + .traoom-controls p, .neonpuzzle-controls p, .rhythm-container p, .arena-container p { + font-size: 0.75rem; + } + + footer p { font-size: 0.85rem; } - .traoom-controls p { - font-size: 0.8rem; + .export-buttons { + flex-direction: column; + } +} + +@media (max-width: 480px) { + .hero h1 { font-size: 1.5rem; } + .app-icons { gap: 10px; } + .app-icon-inner { width: 50px; height: 50px; font-size: 24px; } + .app-label { font-size: 0.7rem; } + + .game-container, .traoom-container, .neonpuzzle-container, .rhythm-container, .arena-container, .platformer-container { + height: 45vh; + min-height: 300px; + } + + .game-overlay h3 { + font-size: 1.2rem; + } + + .leaderboard-header h3 { + font-size: 1.2rem; } }