Files
happy/script.js
SimolZimol 5355e725c1 new file: .gitignore
new file:   README.md
	new file:   images/atomic-heart.png
	new file:   images/julian.png
	new file:   images/prey.png
	new file:   index.html
	new file:   keys.example.js
	new file:   script.js
	new file:   style.css
2025-10-31 16:33:22 +01:00

1051 lines
34 KiB
JavaScript

// Geburtstagskarten-Webseite für Julian - Enhanced JavaScript
document.addEventListener('DOMContentLoaded', function() {
initMinigame();
initParticleEffects();
initScrollAnimations();
});
// Space Invaders Minigame
class SpaceInvadersGame {
constructor(canvas) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.width = canvas.width;
this.height = canvas.height;
this.player = {
x: this.width / 2 - 25,
y: this.height - 60,
width: 50,
height: 30,
speed: 5
};
this.bullets = [];
this.aliens = [];
this.alienBullets = [];
this.particles = [];
this.score = 0;
this.lives = 3;
this.aliensDestroyed = 0;
this.totalAliens = 15;
this.keys = {};
this.gameRunning = false;
this.initAliens();
this.bindEvents();
}
initAliens() {
this.aliens = [];
for (let row = 0; row < 3; row++) {
for (let col = 0; col < 5; col++) {
this.aliens.push({
x: col * 80 + 100,
y: row * 60 + 50,
width: 40,
height: 30,
alive: true,
type: row // Different alien types
});
}
}
}
bindEvents() {
document.addEventListener('keydown', (e) => {
this.keys[e.keyCode] = true;
if (e.keyCode === 32 || e.keyCode === 87) { // Spacebar OR W
e.preventDefault();
this.shoot();
}
});
document.addEventListener('keyup', (e) => {
this.keys[e.keyCode] = false;
});
}
start() {
this.gameRunning = true;
this.gameLoop();
}
gameLoop() {
if (!this.gameRunning) return;
this.update();
this.draw();
requestAnimationFrame(() => this.gameLoop());
}
update() {
// Player movement - Arrow keys OR WASD
if ((this.keys[37] || this.keys[65]) && this.player.x > 0) { // Left arrow OR A
this.player.x -= this.player.speed;
}
if ((this.keys[39] || this.keys[68]) && this.player.x < this.width - this.player.width) { // Right arrow OR D
this.player.x += this.player.speed;
}
// Update bullets
this.bullets = this.bullets.filter(bullet => {
bullet.y -= 7;
return bullet.y > 0;
});
// Update alien bullets
this.alienBullets = this.alienBullets.filter(bullet => {
bullet.y += 4;
return bullet.y < this.height;
});
// Move aliens
this.moveAliens();
// Alien shooting
if (Math.random() < 0.02) {
this.alienShoot();
}
// Check collisions
this.checkCollisions();
// Update particles
this.updateParticles();
// Check win condition
if (this.aliensDestroyed >= this.totalAliens) {
this.gameWon();
}
// Check lose conditions
if (this.lives <= 0) {
this.gameLost();
}
// Check if aliens reached the bottom
this.aliens.forEach(alien => {
if (alien.alive && alien.y + alien.height >= this.player.y) {
this.gameLost();
}
});
}
moveAliens() {
// Initialize direction if not set
if (this.alienDirection === undefined) {
this.alienDirection = 1;
}
let moveDown = false;
// Check if any alien hits the edge
this.aliens.forEach(alien => {
if (!alien.alive) return;
if ((alien.x <= 0 && this.alienDirection < 0) ||
(alien.x >= this.width - alien.width && this.alienDirection > 0)) {
moveDown = true;
}
});
if (moveDown) {
// Move all aliens down and reverse direction
this.aliens.forEach(alien => {
if (alien.alive) {
alien.y += 30;
}
});
this.alienDirection *= -1;
} else {
// Move aliens horizontally
this.aliens.forEach(alien => {
if (alien.alive) {
alien.x += this.alienDirection * 1;
}
});
}
}
shoot() {
this.bullets.push({
x: this.player.x + this.player.width / 2 - 2,
y: this.player.y,
width: 4,
height: 10
});
}
alienShoot() {
const livingAliens = this.aliens.filter(alien => alien.alive);
if (livingAliens.length > 0) {
const shooter = livingAliens[Math.floor(Math.random() * livingAliens.length)];
this.alienBullets.push({
x: shooter.x + shooter.width / 2 - 2,
y: shooter.y + shooter.height,
width: 4,
height: 10
});
}
}
checkCollisions() {
// Bullet vs Aliens
this.bullets.forEach((bullet, bulletIndex) => {
this.aliens.forEach((alien, alienIndex) => {
if (alien.alive && this.isColliding(bullet, alien)) {
alien.alive = false;
this.bullets.splice(bulletIndex, 1);
this.aliensDestroyed++;
this.score += (3 - alien.type) * 10;
this.createExplosion(alien.x + alien.width/2, alien.y + alien.height/2);
this.updateUI();
}
});
});
// Alien bullets vs Player
this.alienBullets.forEach((bullet, bulletIndex) => {
if (this.isColliding(bullet, this.player)) {
this.alienBullets.splice(bulletIndex, 1);
this.lives--;
this.createExplosion(this.player.x + this.player.width/2, this.player.y + this.player.height/2);
this.updateUI();
}
});
}
isColliding(rect1, rect2) {
return rect1.x < rect2.x + rect2.width &&
rect1.x + rect1.width > rect2.x &&
rect1.y < rect2.y + rect2.height &&
rect1.y + rect1.height > rect2.y;
}
createExplosion(x, y) {
for (let i = 0; i < 8; i++) {
this.particles.push({
x: x,
y: y,
vx: (Math.random() - 0.5) * 8,
vy: (Math.random() - 0.5) * 8,
life: 30,
maxLife: 30
});
}
}
updateParticles() {
this.particles = this.particles.filter(particle => {
particle.x += particle.vx;
particle.y += particle.vy;
particle.life--;
return particle.life > 0;
});
}
draw() {
// Clear canvas
this.ctx.fillStyle = 'rgba(0, 5, 15, 0.3)';
this.ctx.fillRect(0, 0, this.width, this.height);
// Draw stars
this.drawStars();
// Draw player
this.ctx.fillStyle = '#00ffff';
this.ctx.fillRect(this.player.x, this.player.y, this.player.width, this.player.height);
// Draw aliens
this.aliens.forEach(alien => {
if (alien.alive) {
const colors = ['#ff6b6b', '#ffd700', '#00ff00'];
this.ctx.fillStyle = colors[alien.type];
this.ctx.fillRect(alien.x, alien.y, alien.width, alien.height);
// Alien eyes
this.ctx.fillStyle = '#ffffff';
this.ctx.fillRect(alien.x + 8, alien.y + 8, 6, 6);
this.ctx.fillRect(alien.x + 26, alien.y + 8, 6, 6);
}
});
// Draw bullets
this.ctx.fillStyle = '#ffff00';
this.bullets.forEach(bullet => {
this.ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
});
// Draw alien bullets
this.ctx.fillStyle = '#ff0000';
this.alienBullets.forEach(bullet => {
this.ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
});
// Draw particles
this.particles.forEach(particle => {
const alpha = particle.life / particle.maxLife;
this.ctx.fillStyle = `rgba(255, 255, 0, ${alpha})`;
this.ctx.fillRect(particle.x, particle.y, 3, 3);
});
}
drawStars() {
this.ctx.fillStyle = '#ffffff';
for (let i = 0; i < 50; i++) {
const x = (i * 37) % this.width;
const y = (i * 73) % this.height;
this.ctx.fillRect(x, y, 1, 1);
}
}
updateUI() {
document.getElementById('score').textContent = this.score;
document.getElementById('aliensLeft').textContent = this.totalAliens - this.aliensDestroyed;
document.getElementById('lives').textContent = '❤️'.repeat(this.lives);
}
gameWon() {
this.gameRunning = false;
setTimeout(() => {
document.getElementById('minigameSection').style.display = 'none';
document.getElementById('giftSection').style.display = 'block';
initScratchCards();
showCelebration();
}, 1000);
}
gameLost() {
this.gameRunning = false;
setTimeout(() => {
if (confirm('Game Over! Möchtest du es nochmal versuchen?')) {
this.reset();
this.start();
} else {
// Zurück zum Start-Button
document.getElementById('gameContainer').style.display = 'none';
document.getElementById('startGameBtn').style.display = 'block';
}
}, 1000);
}
reset() {
this.score = 0;
this.lives = 3;
this.aliensDestroyed = 0;
this.bullets = [];
this.alienBullets = [];
this.particles = [];
this.alienDirection = 1; // Reset alien direction
this.player.x = this.width / 2 - 25; // Reset player position
this.initAliens();
this.updateUI();
}
}
function initMinigame() {
const startBtn = document.getElementById('startGameBtn');
const gameContainer = document.getElementById('gameContainer');
const canvas = document.getElementById('gameCanvas');
let game;
startBtn.addEventListener('click', () => {
startBtn.style.display = 'none';
gameContainer.style.display = 'block';
game = new SpaceInvadersGame(canvas);
game.start();
});
}
function showCelebration() {
const celebration = document.createElement('div');
celebration.innerHTML = `
<div style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
background: rgba(0,0,0,0.95); color: #fff; padding: 50px;
border-radius: 25px; text-align: center; z-index: 2000;
font-family: 'Orbitron', monospace; border: 3px solid #ffd700;">
<h2 style="color: #ffd700; margin-bottom: 30px; font-size: 2.5rem;">🎉 MISSION ERFOLGREICH! 🎉</h2>
<p style="font-size: 1.3rem; margin-bottom: 20px;">Herzlichen Glückwunsch!</p>
<p style="font-size: 1.1rem; margin-bottom: 30px;">Du hast alle Aliens besiegt und deine Geschenke freigeschaltet!</p>
<button onclick="this.parentElement.parentElement.remove()"
style="padding: 15px 30px; background: #ffd700; color: #000; border: none;
border-radius: 15px; cursor: pointer; font-size: 1.2rem; font-weight: bold;">
Geschenke ansehen! 🎁
</button>
</div>
`;
document.body.appendChild(celebration);
}
// Enhanced Scratch Card Functionality
function initScratchCards() {
const scratchCards = document.querySelectorAll('.scratch-card');
scratchCards.forEach(card => {
const canvas = card.querySelector('.scratch-canvas');
const ctx = canvas.getContext('2d');
const hiddenKey = card.querySelector('.hidden-key');
const progressBar = card.querySelector('.progress-fill');
const progressText = card.querySelector('.progress-text');
let isDrawing = false;
let scratchedPixels = 0;
const totalPixels = canvas.width * canvas.height;
const scratchRadius = 15; // Smaller radius for more precise scratching
// Setup initial scratch surface
setupScratchSurface(ctx, canvas, card);
// Mouse events
canvas.addEventListener('mousedown', startScratching);
canvas.addEventListener('mousemove', scratch);
canvas.addEventListener('mouseup', stopScratching);
canvas.addEventListener('mouseleave', stopScratching);
// Touch events for mobile
canvas.addEventListener('touchstart', handleTouch, { passive: false });
canvas.addEventListener('touchmove', handleTouch, { passive: false });
canvas.addEventListener('touchend', stopScratching);
function setupScratchSurface(ctx, canvas, card) {
const isSpecial = card.classList.contains('special-scratch');
// Create scratch surface
if (isSpecial) {
// Gold gradient for special card
const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
gradient.addColorStop(0, '#FFD700');
gradient.addColorStop(0.5, '#FFA500');
gradient.addColorStop(1, '#FF8C00');
ctx.fillStyle = gradient;
} else {
// Blue/gray gradient for regular cards
const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
gradient.addColorStop(0, '#34495e');
gradient.addColorStop(0.5, '#2c3e50');
gradient.addColorStop(1, '#1a252f');
ctx.fillStyle = gradient;
}
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Add text overlay
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
ctx.font = 'bold 16px Orbitron, monospace';
ctx.textAlign = 'center';
ctx.fillText('🎮 STEAM KEY 🎮', canvas.width / 2, canvas.height / 2 - 10);
ctx.font = '12px Inter, sans-serif';
ctx.fillText('Rubbeln zum Freischalten!', canvas.width / 2, canvas.height / 2 + 15);
// Add sparkle effects
addSparkles(ctx, canvas, isSpecial);
}
function addSparkles(ctx, canvas, isSpecial) {
const sparkleCount = 15;
for (let i = 0; i < sparkleCount; i++) {
const x = Math.random() * canvas.width;
const y = Math.random() * canvas.height;
const size = Math.random() * 3 + 1;
ctx.fillStyle = isSpecial ? 'rgba(255, 255, 255, 0.8)' : 'rgba(0, 255, 255, 0.6)';
ctx.beginPath();
ctx.arc(x, y, size, 0, 2 * Math.PI);
ctx.fill();
// Add cross sparkle effect
ctx.strokeStyle = ctx.fillStyle;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(x - size * 2, y);
ctx.lineTo(x + size * 2, y);
ctx.moveTo(x, y - size * 2);
ctx.lineTo(x, y + size * 2);
ctx.stroke();
}
}
function startScratching(e) {
isDrawing = true;
scratch(e);
}
function scratch(e) {
if (!isDrawing) return;
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
scratchAt(x, y);
}
function handleTouch(e) {
e.preventDefault();
const touch = e.touches[0];
if (!touch) return;
const rect = canvas.getBoundingClientRect();
const x = touch.clientX - rect.left;
const y = touch.clientY - rect.top;
if (e.type === 'touchstart') {
isDrawing = true;
}
if (isDrawing) {
scratchAt(x, y);
}
}
function scratchAt(x, y) {
// Create circular scratch with gradient for more realistic effect
ctx.globalCompositeOperation = 'destination-out';
const gradient = ctx.createRadialGradient(x, y, 0, x, y, scratchRadius);
gradient.addColorStop(0, 'rgba(0,0,0,1)');
gradient.addColorStop(0.7, 'rgba(0,0,0,0.8)');
gradient.addColorStop(1, 'rgba(0,0,0,0.3)');
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(x, y, scratchRadius, 0, 2 * Math.PI);
ctx.fill();
// More precise calculation of scratched pixels
scratchedPixels += Math.PI * scratchRadius * scratchRadius * 0.7;
// Update progress
const scratchPercent = Math.min(scratchedPixels / (totalPixels * 0.65), 1);
progressBar.style.width = (scratchPercent * 100) + '%';
progressText.textContent = Math.floor(scratchPercent * 100) + '% freigerubbelt';
// Require 65% to be scratched for reveal
if (scratchPercent >= 1) {
revealKey(card);
}
// Add enhanced scratch particles
createScratchParticles(x, y, card);
}
function stopScratching() {
isDrawing = false;
}
function revealKey(card) {
const canvas = card.querySelector('.scratch-canvas');
const hiddenKey = card.querySelector('.hidden-key');
const instruction = card.querySelector('.scratch-instruction');
const progressContainer = card.querySelector('.scratch-progress');
// Clear entire canvas
canvas.style.opacity = '0';
// Update instruction
instruction.innerHTML = '🎉 Steam-Key freigerubbelt! 🎉';
instruction.style.background = 'rgba(0, 255, 0, 0.3)';
// Create key display below the instruction
const keyDisplay = document.createElement('div');
keyDisplay.className = 'revealed-key';
// Check if this is the special Elden Ring card
const isSpecial = card.classList.contains('special-scratch');
keyDisplay.style.cssText = `
text-align: center;
padding: 15px;
background: rgba(0, 0, 0, 0.8);
font-family: 'Orbitron', monospace;
font-weight: bold;
font-size: 1.4rem;
color: ${isSpecial ? '#ffd700' : '#00ffff'};
text-shadow: 0 0 15px ${isSpecial ? '#ffd700' : '#00ffff'};
letter-spacing: 2px;
border-top: 2px solid ${isSpecial ? '#ffd700' : '#00ffff'};
backdrop-filter: blur(5px);
`;
// Insert key display after instruction
instruction.parentNode.insertBefore(keyDisplay, instruction.nextSibling);
// Add celebration effect
createCelebrationEffect(card);
// Generate realistic looking Steam key
generateSteamKey(keyDisplay, card);
}
});
}
function createScratchParticles(x, y, card) {
const particle = document.createElement('div');
particle.className = 'scratch-particle';
particle.style.position = 'absolute';
particle.style.left = x + 'px';
particle.style.top = y + 'px';
particle.style.width = '4px';
particle.style.height = '4px';
particle.style.borderRadius = '50%';
particle.style.backgroundColor = card.classList.contains('special-scratch') ? '#FFD700' : '#34495e';
particle.style.pointerEvents = 'none';
particle.style.animation = 'scratchParticle 0.5s ease-out forwards';
card.appendChild(particle);
setTimeout(() => {
if (particle.parentNode) {
particle.parentNode.removeChild(particle);
}
}, 500);
}
function createCelebrationEffect(card) {
const celebration = document.createElement('div');
celebration.className = 'celebration-effect';
celebration.innerHTML = '🎉✨🎁✨🎉';
celebration.style.position = 'absolute';
celebration.style.top = '-20px';
celebration.style.left = '50%';
celebration.style.transform = 'translateX(-50%)';
celebration.style.fontSize = '2rem';
celebration.style.animation = 'celebrate 2s ease-out forwards';
celebration.style.pointerEvents = 'none';
celebration.style.zIndex = '100';
card.style.position = 'relative';
card.appendChild(celebration);
setTimeout(() => {
if (celebration.parentNode) {
celebration.parentNode.removeChild(celebration);
}
}, 2000);
}
function generateSteamKey(element, card) {
const gameType = card.getAttribute('data-game');
// Get key from config or generate fallback
let finalKey;
if (window.STEAM_KEYS && window.STEAM_KEYS[gameType]) {
finalKey = window.STEAM_KEYS[gameType];
} else {
// Fallback key generation if config not loaded
let keyPrefix;
switch(gameType) {
case 'atomic-heart':
keyPrefix = 'AH9X2';
break;
case 'prey':
keyPrefix = 'PREY4';
break;
case 'elden-ring':
keyPrefix = 'ELDRG';
break;
default:
keyPrefix = 'GAME1';
}
const segments = [
keyPrefix,
generateKeySegment(5),
generateKeySegment(5)
];
finalKey = segments.join('-');
}
// Animated key reveal
let displayKey = '';
let currentIndex = 0;
const revealInterval = setInterval(() => {
if (currentIndex < finalKey.length) {
displayKey += finalKey[currentIndex];
element.textContent = displayKey + '_';
currentIndex++;
} else {
element.textContent = finalKey;
clearInterval(revealInterval);
element.style.animation = 'keyReveal 1s ease-out forwards';
}
}, 100);
}
function generateKeySegment(length = 5) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
// Particle Effects
function initParticleEffects() {
createFloatingParticles();
}
function createFloatingParticles() {
const particleContainer = document.createElement('div');
particleContainer.className = 'floating-particles';
particleContainer.style.position = 'fixed';
particleContainer.style.top = '0';
particleContainer.style.left = '0';
particleContainer.style.width = '100%';
particleContainer.style.height = '100%';
particleContainer.style.pointerEvents = 'none';
particleContainer.style.zIndex = '-1';
document.body.appendChild(particleContainer);
// Create gaming-themed particles
const particles = ['🎮', '⭐', '🚀', '👾', '🎁', '✨', '🎉', '🎊', '🎈', '🎂'];
setInterval(() => {
if (particleContainer.children.length < 10) {
// Rarely (10% chance) create Julian's image instead of emoji - adjusted for better visibility
if (Math.random() < 0.05) {
createJulianParticle(particleContainer);
} else {
createParticle(particleContainer, particles);
}
}
}, 2000);
}
function createParticle(container, particles) {
const particle = document.createElement('div');
const emoji = particles[Math.floor(Math.random() * particles.length)];
particle.textContent = emoji;
particle.style.position = 'absolute';
particle.style.fontSize = Math.random() * 20 + 10 + 'px';
particle.style.left = Math.random() * 100 + '%';
particle.style.top = '100%';
particle.style.opacity = Math.random() * 0.7 + 0.3;
particle.style.animation = `floatUp ${Math.random() * 10 + 10}s linear forwards`;
container.appendChild(particle);
setTimeout(() => {
if (particle.parentNode) {
particle.parentNode.removeChild(particle);
}
}, 20000);
}
function createJulianParticle(container) {
console.log('🎂 Julian particle wird erstellt!'); // Debug
const particle = document.createElement('div');
const julianImg = document.createElement('img');
// Julian's image setup
julianImg.src = 'images/julian.png';
julianImg.style.width = Math.random() * 30 + 40 + 'px'; // 40-70px size
julianImg.style.height = 'auto';
julianImg.style.borderRadius = '50%';
julianImg.style.border = '3px solid #ffd700';
julianImg.style.boxShadow = '0 0 20px rgba(255, 215, 0, 0.8)';
julianImg.alt = '🎂';
// Success callback
julianImg.onload = function() {
console.log('✅ Julian Bild erfolgreich geladen!');
};
// Fallback if image doesn't load
julianImg.onerror = function() {
console.log('❌ Julian Bild konnte nicht geladen werden, verwende Fallback');
particle.innerHTML = '🎂';
particle.style.fontSize = '50px';
particle.style.textAlign = 'center';
particle.style.lineHeight = particle.style.width;
};
particle.appendChild(julianImg);
particle.style.position = 'absolute';
particle.style.left = Math.random() * 100 + '%';
particle.style.top = '100%';
particle.style.opacity = '1'; // Vollständig sichtbar für Debug
particle.style.zIndex = '10'; // Höhere z-index für Sichtbarkeit
particle.style.animation = `floatUp ${Math.random() * 8 + 12}s linear forwards`;
container.appendChild(particle);
// Special celebration when Julian appears
setTimeout(() => {
createJulianCelebration();
}, 1000);
setTimeout(() => {
if (particle.parentNode) {
particle.parentNode.removeChild(particle);
}
}, 20000);
}
function createJulianCelebration() {
// Create small burst of birthday emojis around Julian
const celebrationEmojis = ['🎂', '🎉', '🎊', '🎁', '🥳'];
for (let i = 0; i < 5; i++) {
setTimeout(() => {
const celebParticle = document.createElement('div');
celebParticle.textContent = celebrationEmojis[Math.floor(Math.random() * celebrationEmojis.length)];
celebParticle.style.position = 'fixed';
celebParticle.style.left = Math.random() * 100 + '%';
celebParticle.style.top = Math.random() * 100 + '%';
celebParticle.style.fontSize = '25px';
celebParticle.style.pointerEvents = 'none';
celebParticle.style.zIndex = '100';
celebParticle.style.animation = 'celebrate 2s ease-out forwards';
document.body.appendChild(celebParticle);
setTimeout(() => {
if (celebParticle.parentNode) {
celebParticle.parentNode.removeChild(celebParticle);
}
}, 2000);
}, i * 200);
}
}
// Scroll Animations
function initScrollAnimations() {
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -100px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, observerOptions);
// Observe game cards for scroll animations
document.querySelectorAll('.game-card').forEach(card => {
card.style.opacity = '0';
card.style.transform = 'translateY(50px)';
card.style.transition = 'all 0.8s ease';
observer.observe(card);
});
}
// Add CSS animations dynamically
const style = document.createElement('style');
style.textContent = `
@keyframes scratchParticle {
0% {
transform: scale(1);
opacity: 1;
}
100% {
transform: scale(0) translateY(-20px);
opacity: 0;
}
}
@keyframes celebrate {
0% {
transform: translateX(-50%) translateY(0) scale(1);
opacity: 0;
}
50% {
transform: translateX(-50%) translateY(-30px) scale(1.2);
opacity: 1;
}
100% {
transform: translateX(-50%) translateY(-50px) scale(0.8);
opacity: 0;
}
}
@keyframes keyReveal {
0% {
transform: scale(0.8);
opacity: 0;
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
opacity: 1;
}
}
@keyframes floatUp {
0% {
transform: translateY(0);
opacity: 0.7;
}
10% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
transform: translateY(-100vh);
opacity: 0;
}
}
`;
document.head.appendChild(style);
// Easter Egg: Konami Code
let konamiSequence = [];
const konamiCode = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65]; // ↑↑↓↓←→←→BA
document.addEventListener('keydown', function(e) {
konamiSequence.push(e.keyCode);
if (konamiSequence.length > konamiCode.length) {
konamiSequence.shift();
}
if (konamiSequence.length === konamiCode.length &&
konamiSequence.every((key, i) => key === konamiCode[i])) {
activateEasterEgg();
konamiSequence = [];
}
});
function activateEasterEgg() {
// Secret birthday surprise
const surprise = document.createElement('div');
surprise.innerHTML = `
<div style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
background: rgba(0,0,0,0.9); color: #fff; padding: 40px;
border-radius: 20px; text-align: center; z-index: 1000;
font-family: 'Orbitron', monospace;">
<h2 style="color: #ffd700; margin-bottom: 20px;">🎮 GEHEIMER BONUS! 🎮</h2>
<p>Du hast den Konami-Code entdeckt!</p>
<p>Extra Geburtstagswünsche für Julian! 🎉</p>
<button onclick="this.parentElement.parentElement.remove()"
style="margin-top: 20px; padding: 10px 20px; background: #ffd700;
color: #000; border: none; border-radius: 10px; cursor: pointer;">
Schließen
</button>
</div>
`;
document.body.appendChild(surprise);
// Add extra particles
for (let i = 0; i < 20; i++) {
setTimeout(() => {
createParticle(document.querySelector('.floating-particles'), ['🎉', '🎊', '🎁', '⭐']);
}, i * 100);
}
}
// Enhanced CSS Animations
const additionalStyles = `
@keyframes scratchParticle {
0% {
transform: scale(1) rotate(0deg);
opacity: 1;
}
100% {
transform: scale(0) rotate(360deg) translateY(-30px);
opacity: 0;
}
}
@keyframes celebrate {
0% {
transform: translateX(-50%) translateY(0) scale(1) rotate(0deg);
opacity: 0;
}
25% {
transform: translateX(-50%) translateY(-20px) scale(1.1) rotate(5deg);
opacity: 1;
}
75% {
transform: translateX(-50%) translateY(-40px) scale(1.2) rotate(-5deg);
opacity: 1;
}
100% {
transform: translateX(-50%) translateY(-60px) scale(0.9) rotate(0deg);
opacity: 0;
}
}
@keyframes keyReveal {
0% {
transform: scale(0.5) rotateY(90deg);
opacity: 0;
}
50% {
transform: scale(1.1) rotateY(45deg);
}
100% {
transform: scale(1) rotateY(0deg);
opacity: 1;
}
}
@keyframes floatUp {
0% {
transform: translateY(0) rotate(0deg);
opacity: 0.8;
}
50% {
opacity: 1;
}
100% {
transform: translateY(-120vh) rotate(360deg);
opacity: 0;
}
}
@keyframes gameVictory {
0% { transform: scale(1); }
25% { transform: scale(1.1); }
50% { transform: scale(0.9); }
75% { transform: scale(1.05); }
100% { transform: scale(1); }
}
@keyframes julianSpin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.scratch-particle {
animation: scratchParticle 0.8s ease-out forwards !important;
}
.celebration-effect {
animation: celebrate 3s ease-out forwards !important;
}
.victory-animation {
animation: gameVictory 2s ease-in-out infinite;
}
`;
const styleSheet = document.createElement('style');
styleSheet.textContent = additionalStyles;
document.head.appendChild(styleSheet);
console.log('🎮 Enhanced Geburtstagskarte für Julian geladen! 🎂');
console.log('🚀 Spiele Space Invaders um die geheimen Geschenke freizuschalten!');
console.log('💡 Tipp: Versuche den Konami-Code für eine Überraschung! ↑↑↓↓←→←→BA');