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
1051 lines
34 KiB
JavaScript
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'); |