stack-jumper / index.html
Malicious7's picture
review the codes and let the game work - Follow Up Deployment
6605f5d verified
Raw History Blame Contribute Delete
71.8 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Stack Jumper</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Poppins', sans-serif;
overflow: hidden;
touch-action: manipulation;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.splash-fade {
animation: fadeIn 1s ease-in;
}
.platform {
transition: transform 0.1s ease-out;
}
.player {
transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
.game-container {
perspective: 1000px;
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
.pulse {
animation: pulse 1.5s infinite;
}
.fade-in {
animation: fadeIn 0.3s ease-in;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.jump {
animation: jump 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes ripple {
to {
transform: scale(1.5);
opacity: 0;
}
}
.animate-ripple {
animation: ripple 0.6s linear;
}
@keyframes jump {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-100%); }
}
</style>
</head>
<body class="bg-gradient-to-b from-blue-900 to-purple-900 text-white min-h-screen flex flex-col">
<!-- Splash Screen -->
<div id="splash-screen" class="fixed inset-0 bg-black flex items-center justify-center z-50 splash-fade">
<div class="text-center">
<div class="text-center mb-6">
<svg class="w-32 h-32 mx-auto mb-4" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="45" fill="url(#gradient)"/>
<path d="M25 50 L75 50 M50 25 L50 75 M35 35 L65 65 M65 35 L35 65" stroke="#fde047" stroke-width="4"/>
<defs>
<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#9333ea"/>
<stop offset="100%" stop-color="#3b82f6"/>
</linearGradient>
</defs>
</svg>
<h1 class="text-4xl font-bold mb-2 text-yellow-300 animate-bounce">STACK JUMPER</h1>
<p class="text-lg text-gray-300">by HyperTouch Games</p>
<div class="mt-4">
<svg class="w-20 h-20 mx-auto" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="45" fill="#3b82f6"/>
<path d="M30 40 L70 40 L50 70 Z" fill="#fde047"/>
<text x="50" y="90" text-anchor="middle" font-family="Arial" font-size="10" fill="white">HTG</text>
</svg>
</div>
</div>
<div class="flex justify-center space-x-8 mb-8">
<div class="text-center">
<div class="w-16 h-16 bg-white rounded-full flex items-center justify-center mx-auto mb-2">
<span class="text-2xl">🖥️</span>
</div>
<p>HTML5</p>
</div>
<div class="text-center">
<div class="w-16 h-16 bg-white rounded-full flex items-center justify-center mx-auto mb-2">
<span class="text-2xl">🎨</span>
</div>
<p>Tailwind CSS</p>
</div>
<div class="text-center">
<div class="w-16 h-16 bg-white rounded-full flex items-center justify-center mx-auto mb-2">
<span class="text-2xl">⚡</span>
</div>
<p>JavaScript</p>
</div>
</div>
<p class="text-sm text-gray-400">Special thanks to Deepsite v2</p>
</div>
</div>
<!-- Loading Screen -->
<div id="loading-screen" class="fixed inset-0 bg-black flex items-center justify-center z-50">
<div class="text-center">
<div class="w-16 h-16 border-4 border-yellow-400 border-t-transparent rounded-full animate-spin mx-auto mb-4"></div>
<p class="text-xl text-yellow-300">Loading Game Assets...</p>
<p id="progress-text" class="text-gray-400">0%</p>
</div>
</div>
<!-- Welcome Screen -->
<div id="welcome-screen" class="flex flex-col items-center justify-center flex-grow p-6 fade-in">
<div class="text-center mb-8">
<h1 class="text-5xl font-bold mb-2 text-yellow-300">STACK JUMPER</h1>
<p class="text-xl text-gray-300">HyperTouch Games</p>
</div>
<div class="relative w-full max-w-md h-64 mb-8 bg-black rounded-xl overflow-hidden">
<video autoplay loop muted class="w-full h-full object-cover">
<source src="https://assets.mixkit.co/videos/preview/mixkit-game-background-loop-482-large.mp4" type="video/mp4">
</video>
<div class="absolute inset-0 flex items-center justify-center">
<div class="w-12 h-12 bg-white rounded-full jump"></div>
</div>
</div>
<p class="text-xl mb-8 text-center max-w-md px-4">Jump, climb and reach new heights in this exciting platformer!</p>
<button id="get-started-btn" class="bg-yellow-400 hover:bg-yellow-500 text-purple-900 font-bold py-4 px-8 rounded-full text-xl shadow-lg transform transition hover:scale-105">
GET STARTED
</button>
</div>
<!-- Registration Screen -->
<div id="register-screen" class="hidden flex flex-col items-center justify-center flex-grow p-6">
<h1 class="text-4xl font-bold mb-8 text-yellow-300">REGISTER</h1>
<div class="w-full max-w-xs mb-6">
<input type="text" id="username" placeholder="Username" class="w-full p-3 rounded-lg bg-gray-800 text-white mb-3">
<input type="email" id="email" placeholder="Email" class="w-full p-3 rounded-lg bg-gray-800 text-white mb-3">
<input type="password" id="password" placeholder="Password" class="w-full p-3 rounded-lg bg-gray-800 text-white">
</div>
<button id="register-btn" class="bg-yellow-400 hover:bg-yellow-500 text-purple-900 font-bold py-3 px-8 rounded-full text-lg w-full max-w-xs mb-4 shadow-lg transform transition hover:scale-105">
REGISTER
</button>
<p class="text-gray-400 text-sm">Already registered? <button id="login-btn" class="text-yellow-300 hover:underline">Login</button></p>
</div>
<!-- Home Screen -->
<div id="home-screen" class="hidden flex flex-col items-center justify-center flex-grow fade-in">
<div class="text-center mb-8">
<svg class="w-24 h-24 mx-auto mb-4" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="45" fill="url(#gradient)"/>
<path d="M25 50 L75 50 M50 25 L50 75 M35 35 L65 65 M65 35 L35 65" stroke="#fde047" stroke-width="4"/>
<defs>
<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#9333ea"/>
<stop offset="100%" stop-color="#3b82f6"/>
</linearGradient>
</defs>
</svg>
<h1 class="text-6xl font-bold mb-2 text-yellow-300">STACK</h1>
<h1 class="text-6xl font-bold text-yellow-300">JUMPER</h1>
</div>
<div class="flex flex-col items-center space-y-4 w-full max-w-xs">
<button id="play-btn" class="bg-yellow-400 hover:bg-yellow-500 text-purple-900 font-bold py-4 px-8 rounded-full text-xl w-full pulse shadow-lg transform transition hover:scale-105">
PLAY
</button>
<button id="how-to-play-btn" class="bg-purple-700 hover:bg-purple-600 text-white font-bold py-3 px-6 rounded-full text-lg w-full shadow transform transition hover:scale-105">
HOW TO PLAY
</button>
<button id="mute-btn" class="bg-gray-700 hover:bg-gray-600 text-white font-bold py-2 px-4 rounded-full text-sm mt-8">
<span id="mute-icon">🔊</span> SOUND ON
</button>
</div>
<div class="mt-8 text-center text-gray-300 text-sm">
<p>Tap to jump and reach new heights!</p>
</div>
<!-- Studio Footer -->
<div class="mt-auto mb-4 flex items-center justify-center space-x-2 text-gray-400">
<svg class="w-6 h-6" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="45" fill="#3b82f6"/>
<path d="M30 40 L70 40 L50 70 Z" fill="#fde047"/>
</svg>
<span>HyperTouch Games © 2023</span>
</div>
</div>
<!-- Level Selection Screen -->
<div id="level-screen" class="hidden flex-col items-center justify-center flex-grow p-6">
<h2 class="text-3xl font-bold mb-8 text-yellow-300">SELECT LEVEL</h2>
<div class="grid grid-cols-3 gap-4 mb-8 w-full max-w-md">
<button class="level-btn bg-green-500 hover:bg-green-600 text-white font-bold py-4 px-6 rounded-full text-xl" data-level="1">1</button>
<button class="level-btn bg-yellow-500 hover:bg-yellow-600 text-white font-bold py-4 px-6 rounded-full text-xl" data-level="2">2</button>
<button class="level-btn bg-red-500 hover:bg-red-600 text-white font-bold py-4 px-6 rounded-full text-xl" data-level="3">3</button>
<button class="level-btn bg-purple-500 hover:bg-purple-600 text-white font-bold py-4 px-6 rounded-full text-xl" data-level="4">4</button>
<button class="level-btn bg-blue-500 hover:bg-blue-600 text-white font-bold py-4 px-6 rounded-full text-xl" data-level="5">5</button>
<button class="level-btn bg-gray-500 hover:bg-gray-600 text-white font-bold py-4 px-6 rounded-full text-xl" data-level="6">6</button>
</div>
<button id="back-to-home-btn" class="bg-purple-700 hover:bg-purple-600 text-white font-bold py-3 px-8 rounded-full text-lg shadow transform transition hover:scale-105">
BACK
</button>
</div>
<!-- How to Play Screen -->
<div id="how-to-screen" class="hidden flex-col items-center justify-center flex-grow p-6">
<h2 class="text-3xl font-bold mb-8 text-yellow-300">HOW TO PLAY</h2>
<div class="bg-black bg-opacity-40 rounded-xl p-6 mb-8 max-w-md">
<div class="flex items-start mb-4">
<div class="bg-yellow-400 text-purple-900 font-bold rounded-full w-8 h-8 flex items-center justify-center mr-3">1</div>
<p class="flex-1">Tap anywhere to make your character jump</p>
</div>
<div class="flex items-start mb-4">
<div class="bg-yellow-400 text-purple-900 font-bold rounded-full w-8 h-8 flex items-center justify-center mr-3">2</div>
<p class="flex-1">Land on the moving platforms to keep climbing</p>
</div>
<div class="flex items-start">
<div class="bg-yellow-400 text-purple-900 font-bold rounded-full w-8 h-8 flex items-center justify-center mr-3">3</div>
<p class="flex-1">Don't fall off! The game gets harder as you go higher</p>
</div>
</div>
<div class="relative w-40 h-40 mb-8">
<!-- Simple animation showing gameplay -->
<div class="absolute bottom-0 left-1/2 transform -translate-x-1/2 w-10 h-10 bg-white rounded-full player"></div>
<div class="absolute bottom-12 left-1/2 transform -translate-x-1/2 w-20 h-4 bg-green-500 rounded platform"></div>
<div class="absolute bottom-24 left-1/2 transform -translate-x-1/2 w-16 h-4 bg-red-500 rounded platform"></div>
<div class="absolute bottom-36 left-1/2 transform -translate-x-1/2 w-14 h-4 bg-yellow-500 rounded platform"></div>
</div>
<button id="back-btn" class="bg-purple-700 hover:bg-purple-600 text-white font-bold py-3 px-8 rounded-full text-lg shadow transform transition hover:scale-105">
BACK
</button>
<!-- AdMob Banner Ad -->
<div id="ad-banner" class="mt-auto mb-4 w-full h-16 rounded-lg flex items-center justify-center">
<!-- AdMob banner will be loaded here -->
<!-- Replace with your actual AdMob banner ID in production -->
<script>
// Production code would initialize AdMob here
// Example:
// document.addEventListener('deviceready', function() {
// admob.banner.config({
// id: 'ca-app-pub-xxxxxxxxxxxxxxxx/yyyyyyyyyy',
// isTesting: false
// });
// admob.banner.prepare();
// });
</script>
</div>
</div>
<!-- Game Screen -->
<div id="game-screen" class="hidden relative w-full h-screen overflow-hidden game-container">
<div class="absolute top-4 left-0 right-0 flex justify-center">
<div class="bg-black bg-opacity-50 text-white font-bold py-2 px-6 rounded-full text-xl">
Score: <span id="score">0</span>
</div>
</div>
<div class="absolute top-4 right-4">
<button id="game-mute-btn" class="bg-black bg-opacity-50 text-white font-bold py-2 px-3 rounded-full text-sm">
<span id="game-mute-icon">🔊</span>
</button>
</div>
<!-- Game elements will be added here dynamically -->
<div id="game-world" class="relative w-full h-full overflow-hidden">
<div id="player" class="absolute bottom-20 left-1/2 transform -translate-x-1/2 w-10 h-10 bg-white rounded-full shadow-lg player"></div>
</div>
<div class="absolute bottom-4 left-0 right-0 flex justify-center">
<button id="pause-btn" class="bg-black bg-opacity-50 text-white font-bold py-2 px-4 rounded-full text-sm">
PAUSE
</button>
</div>
</div>
<!-- Game Over Screen -->
<div id="game-over-screen" class="hidden flex-col items-center justify-center flex-grow p-6">
<h2 class="text-5xl font-bold mb-4 text-red-500">GAME OVER</h2>
<p class="text-lg text-gray-300 mb-6">HyperTouch Games</p>
<p class="text-lg text-gray-300 mb-6">Don't give up! You can do better next time.</p>
<div class="bg-black bg-opacity-40 rounded-xl p-6 mb-8 w-full max-w-xs text-center">
<p class="text-2xl mb-2">Game Over</p>
<p class="text-lg mb-4 text-gray-300">You did great! Every failure is a step closer to success.</p>
<div class="bg-gray-800 bg-opacity-60 rounded-lg p-4 mb-4">
<p class="text-xl mb-1">Your Score:</p>
<p id="final-score" class="text-3xl font-bold text-yellow-300 mb-3">0</p>
<p class="text-lg">High Score:</p>
<p id="high-score" class="text-2xl font-bold text-green-300">0</p>
</div>
<p class="text-sm text-gray-400 mb-6">Keep practicing to beat your high score!</p>
</div>
<div class="flex flex-col items-center space-y-4 w-full max-w-xs">
<button id="replay-btn" class="bg-yellow-400 hover:bg-yellow-500 text-purple-900 font-bold py-4 px-8 rounded-full text-xl w-full shadow-lg transform transition hover:scale-105">
TRY AGAIN
</button>
<div class="flex space-x-4 w-full">
<button id="home-btn" class="bg-purple-700 hover:bg-purple-600 text-white font-bold py-3 px-6 rounded-full text-lg flex-1 shadow transform transition hover:scale-105">
MENU
</button>
<button id="continue-btn" class="bg-green-600 hover:bg-green-500 text-white font-bold py-3 px-6 rounded-full text-lg flex-1 shadow transform transition hover:scale-105 flex items-center justify-center">
<span class="mr-2">🎁</span> CONTINUE
</button>
</div>
</div>
<!-- Ad Banner Placeholder -->
<div class="mt-auto mb-4 w-full h-16 bg-gray-800 rounded-lg flex items-center justify-center text-gray-400">
Interstitial Ad Placeholder
</div>
</div>
<!-- Pause Screen -->
<div id="pause-screen" class="hidden absolute inset-0 bg-black bg-opacity-80 flex flex-col items-center justify-center z-50 backdrop-blur-sm">
<div class="bg-gradient-to-br from-purple-900 to-blue-900 p-8 rounded-2xl shadow-2xl border border-purple-500 max-w-md w-full mx-4">
<div class="text-center mb-8">
<svg class="w-20 h-20 mx-auto mb-4 text-yellow-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 9v6m4-6v6m7-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<h2 class="text-5xl font-bold text-yellow-300 mb-2">PAUSED</h2>
<p class="text-gray-300">Current Score: <span id="pause-score" class="text-white font-bold">0</span></p>
</div>
<div class="flex flex-col space-y-4">
<button id="resume-btn" class="bg-gradient-to-r from-yellow-400 to-yellow-500 hover:from-yellow-500 hover:to-yellow-600 text-purple-900 font-bold py-4 px-6 rounded-full text-xl w-full shadow-lg transform transition hover:scale-105 flex items-center justify-center">
<svg class="w-6 h-6 mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
RESUME
</button>
<button id="restart-btn" class="bg-gradient-to-r from-red-500 to-red-600 hover:from-red-600 hover:to-red-700 text-white font-bold py-3 px-6 rounded-full text-lg w-full shadow transform transition hover:scale-105 flex items-center justify-center">
<svg class="w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
RESTART
</button>
<button id="pause-home-btn" class="bg-gradient-to-r from-purple-700 to-purple-800 hover:from-purple-800 hover:to-purple-900 text-white font-bold py-3 px-6 rounded-full text-lg w-full shadow transform transition hover:scale-105 flex items-center justify-center">
<svg class="w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg>
MAIN MENU
</button>
<button id="pause-settings-btn" class="bg-gradient-to-r from-gray-700 to-gray-800 hover:from-gray-800 hover:to-gray-900 text-white font-bold py-3 px-6 rounded-full text-lg w-full shadow transform transition hover:scale-105 flex items-center justify-center">
<svg class="w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
SETTINGS
</button>
</div>
</div>
</div>
<script>
// DOM elements
const welcomeScreen = document.getElementById('welcome-screen');
const getStartedBtn = document.getElementById('get-started-btn');
// Language translations
const translations = {
en: {
welcomeTitle: "STACK JUMPER",
welcomeSubtitle: "by HyperTouch Games",
getStarted: "GET STARTED",
loading: "Loading Game Assets...",
play: "PLAY",
howToPlay: "HOW TO PLAY",
soundOn: "SOUND ON",
soundOff: "SOUND OFF",
selectLevel: "SELECT LEVEL",
back: "BACK",
howToTitle: "HOW TO PLAY",
howTo1: "Tap anywhere to make your character jump",
howTo2: "Land on the moving platforms to keep climbing",
howTo3: "Don't fall off! The game gets harder as you go higher",
gameOver: "GAME OVER",
tryAgain: "TRY AGAIN",
menu: "MENU",
continue: "CONTINUE",
paused: "PAUSED",
resume: "RESUME",
score: "Score:",
highScore: "High Score:"
},
es: {
welcomeTitle: "SALTADOR",
welcomeSubtitle: "por HyperTouch Games",
getStarted: "COMENZAR",
loading: "Cargando recursos...",
play: "JUGAR",
howToPlay: "CÓMO JUGAR",
soundOn: "SONIDO ON",
soundOff: "SONIDO OFF",
selectLevel: "SELECCIONAR NIVEL",
back: "ATRÁS",
howToTitle: "CÓMO JUGAR",
howTo1: "Toca en cualquier lugar para hacer saltar a tu personaje",
howTo2: "Aterriza en las plataformas móviles para seguir subiendo",
howTo3: "¡No te caigas! El juego se vuelve más difícil cuanto más alto llegues",
gameOver: "JUEGO TERMINADO",
tryAgain: "INTENTAR DE NUEVO",
menu: "MENÚ",
continue: "CONTINUAR",
paused: "PAUSADO",
resume: "REANUDAR",
score: "Puntuación:",
highScore: "Récord:"
},
// Add other languages similarly...
};
// Game state
const gameState = {
currentLanguage: 'en',
currentScreen: 'register',
user: null,
unlockedLevels: 1,
currentLevel: 1,
levelSettings: {
1: { speed: 2, platformWidth: 80, powerUpChance: 0.1 },
2: { speed: 2.5, platformWidth: 70, powerUpChance: 0.15 },
3: { speed: 3, platformWidth: 60, powerUpChance: 0.2 },
4: { speed: 3.5, platformWidth: 50, powerUpChance: 0.25 },
5: { speed: 4, platformWidth: 40, powerUpChance: 0.3 },
6: { speed: 5, platformWidth: 30, powerUpChance: 0.4 }
},
score: 0,
highScore: localStorage.getItem('highScore') || 0,
isMuted: false,
isPaused: false,
isGameOver: false,
platforms: [],
player: {
x: 0,
y: 0,
width: 40,
height: 40,
velocityY: 0,
isJumping: false,
canDoubleJump: false,
comboCount: 0,
isInvincible: false
},
gameSpeed: 2,
platformSpeed: 2,
platformWidth: 80,
platformHeight: 15,
lastPlatformY: 0,
powerUps: [],
lastJumpTime: 0
};
// DOM elements
const screens = {
home: document.getElementById('home-screen'),
howTo: document.getElementById('how-to-screen'),
game: document.getElementById('game-screen'),
gameOver: document.getElementById('game-over-screen'),
pause: document.getElementById('pause-screen')
};
const buttons = {
play: document.getElementById('play-btn'),
howToPlay: document.getElementById('how-to-play-btn'),
mute: document.getElementById('mute-btn'),
muteIcon: document.getElementById('mute-icon'),
back: document.getElementById('back-btn'),
gameMute: document.getElementById('game-mute-btn'),
gameMuteIcon: document.getElementById('game-mute-icon'),
pause: document.getElementById('pause-btn'),
resume: document.getElementById('resume-btn'),
pauseHome: document.getElementById('pause-home-btn'),
replay: document.getElementById('replay-btn'),
home: document.getElementById('home-btn'),
continue: document.getElementById('continue-btn')
};
const gameElements = {
score: document.getElementById('score'),
finalScore: document.getElementById('final-score'),
highScore: document.getElementById('high-score'),
player: document.getElementById('player'),
gameWorld: document.getElementById('game-world')
};
// Audio
const audio = {
jump: new Audio('https://assets.mixkit.co/sfx/preview/mixkit-quick-jump-arcade-game-239.mp3'),
land: new Audio('https://assets.mixkit.co/sfx/preview/mixkit-arcade-game-jump-coin-216.mp3'),
gameOver: new Audio('https://assets.mixkit.co/sfx/preview/mixkit-retro-arcade-lose-2027.mp3'),
background: new Audio('https://assets.mixkit.co/music/preview/mixkit-game-show-suspense-waiting-668.mp3'),
combo: new Audio('https://assets.mixkit.co/sfx/preview/mixkit-achievement-bell-600.mp3'),
powerup: new Audio('https://assets.mixkit.co/sfx/preview/mixkit-extra-bonus-in-a-video-game-2045.mp3'),
button: new Audio('https://assets.mixkit.co/sfx/preview/mixkit-arcade-game-jump-coin-216.mp3')
};
// Set audio volume
Object.values(audio).forEach(sound => {
sound.volume = 0.3;
});
// Event listeners
document.getElementById('language-select').addEventListener('change', function() {
gameState.currentLanguage = this.value;
updateLanguage();
});
function updateLanguage() {
const lang = translations[gameState.currentLanguage];
// Update welcome screen
document.querySelector('#welcome-screen h1').textContent = lang.welcomeTitle;
document.querySelector('#welcome-screen p').textContent = lang.welcomeSubtitle;
document.getElementById('get-started-btn').textContent = lang.getStarted;
// Update loading screen
document.querySelector('#loading-screen p').textContent = lang.loading;
// Update home screen
document.querySelector('#home-screen h1').textContent = lang.welcomeTitle;
document.getElementById('play-btn').textContent = lang.play;
document.getElementById('how-to-play-btn').textContent = lang.howToPlay;
document.getElementById('mute-btn').innerHTML = `<span id="mute-icon">${gameState.isMuted ? '🔇' : '🔊'}</span> ${gameState.isMuted ? lang.soundOff : lang.soundOn}`;
// Update level screen
document.querySelector('#level-screen h2').textContent = lang.selectLevel;
document.getElementById('back-to-home-btn').textContent = lang.back;
// Update how to play screen
document.querySelector('#how-to-screen h2').textContent = lang.howToTitle;
document.querySelectorAll('#how-to-screen p')[0].textContent = lang.howTo1;
document.querySelectorAll('#how-to-screen p')[1].textContent = lang.howTo2;
document.querySelectorAll('#how-to-screen p')[2].textContent = lang.howTo3;
document.getElementById('back-btn').textContent = lang.back;
// Update game screen
document.querySelector('#game-screen .bg-black').innerHTML = `${lang.score} <span id="score">0</span>`;
// Update game over screen
document.querySelector('#game-over-screen h2').textContent = lang.gameOver;
document.getElementById('replay-btn').textContent = lang.tryAgain;
document.getElementById('home-btn').textContent = lang.menu;
document.getElementById('continue-btn').innerHTML = `<span class="mr-2">🎁</span> ${lang.continue}`;
document.querySelector('#game-over-screen .bg-gray-800 p:nth-child(1)').textContent = lang.score;
document.querySelector('#game-over-screen .bg-gray-800 p:nth-child(3)').textContent = lang.highScore;
// Update pause screen
document.querySelector('#pause-screen h2').textContent = lang.paused;
document.getElementById('resume-btn').textContent = lang.resume;
document.getElementById('pause-home-btn').textContent = lang.menu;
}
getStartedBtn.addEventListener('click', () => {
welcomeScreen.classList.add('hidden');
document.getElementById('register-screen').classList.remove('hidden');
});
// Button click handlers with feedback
buttons.play.addEventListener('click', function() {
// Visual feedback
this.classList.add('animate-pulse', 'transform', 'scale-95');
if (!gameState.isMuted) {
audio.button.play();
audio.button.currentTime = 0; // Reset sound to start
}
// Ripple effect
const ripple = document.createElement('span');
ripple.className = 'absolute inset-0 rounded-full bg-white opacity-20 animate-ripple';
this.appendChild(ripple);
setTimeout(() => ripple.remove(), 600);
setTimeout(() => {
this.classList.remove('animate-pulse', 'scale-95');
showScreen('level');
}, 200);
});
document.getElementById('register-btn').addEventListener('click', function() {
this.classList.add('bg-yellow-500');
if (!gameState.isMuted) audio.button.play();
setTimeout(() => {
this.classList.remove('bg-yellow-500');
registerUser();
}, 100);
});
document.getElementById('login-btn').addEventListener('click', function() {
this.classList.add('underline');
if (!gameState.isMuted) audio.button.play();
setTimeout(() => {
this.classList.remove('underline');
alert("Login functionality coming soon!");
}, 100);
});
document.getElementById('back-to-home-btn').addEventListener('click', function() {
this.classList.add('bg-purple-800');
if (!gameState.isMuted) audio.button.play();
setTimeout(() => {
this.classList.remove('bg-purple-800');
showScreen('home');
}, 100);
});
// Level selection
document.querySelectorAll('.level-btn').forEach(btn => {
btn.addEventListener('click', function() {
const level = parseInt(this.dataset.level);
if (level <= gameState.unlockedLevels) {
gameState.currentLevel = level;
showScreen('game');
} else {
alert(`Complete level ${gameState.unlockedLevels} first!`);
}
});
});
buttons.howToPlay.addEventListener('click', () => showScreen('howTo'));
buttons.back.addEventListener('click', () => showScreen('home'));
buttons.mute.addEventListener('click', toggleMute);
buttons.gameMute.addEventListener('click', toggleMute);
buttons.pause.addEventListener('click', pauseGame);
buttons.resume.addEventListener('click', resumeGame);
buttons.pauseHome.addEventListener('click', () => {
if (!gameState.isMuted) audio.button.play();
resumeGame();
showScreen('home');
});
buttons.replay.addEventListener('click', startGame);
buttons.home.addEventListener('click', () => showScreen('home'));
buttons.continue.addEventListener('click', continueAfterAd);
// Game world click/tap handler
gameElements.gameWorld.addEventListener('click', handleGameTap);
gameElements.gameWorld.addEventListener('touchstart', handleGameTap);
gameElements.gameWorld.addEventListener('touchmove', handleGameTap);
gameElements.gameWorld.addEventListener('touchend', handleGameTap);
// User registration
function registerUser() {
const username = document.getElementById('username').value.trim();
const email = document.getElementById('email').value.trim();
const password = document.getElementById('password').value.trim();
if (!username || !email || !password) {
alert("Please fill in all fields");
return;
}
// In a real app, you would send this to a backend
gameState.user = {
username,
email,
levelProgress: 1
};
// Save to localStorage for persistence
localStorage.setItem('stackJumperUser', JSON.stringify(gameState.user));
showScreen('home');
}
// Screen management
function showScreen(screenName) {
// Hide all screens
Object.values(screens).forEach(screen => {
screen.classList.add('hidden');
});
// Show the requested screen
screens[screenName].classList.remove('hidden');
gameState.currentScreen = screenName;
// Special handling for game screen
if (screenName === 'game') {
startGame();
} else if (screenName === 'home') {
stopGame();
playBackgroundMusic();
} else if (screenName === 'game-over') {
stopGame();
}
}
// Game functions
function startGame() {
// Reset game state
gameState.score = 0;
gameState.isGameOver = false;
gameState.platforms = [];
// Setup player
gameState.player = {
x: window.innerWidth / 2 - 20,
y: window.innerHeight - 120,
width: 40,
height: 40,
velocityY: 0,
isJumping: false,
canDoubleJump: false
};
// Clear game world
gameElements.gameWorld.innerHTML = '';
// Create player
gameElements.player = document.createElement('div');
gameElements.player.id = 'player';
gameElements.player.className = 'absolute w-10 h-10 bg-white rounded-full shadow-lg player';
gameElements.player.style.left = `${gameState.player.x}px`;
gameElements.player.style.bottom = `${window.innerHeight - gameState.player.y}px`;
gameElements.gameWorld.appendChild(gameElements.player);
// Create initial platform
createPlatform(window.innerWidth / 2 - 40, window.innerHeight - 60, 80, 15, 'bg-green-500');
// Start game loop
gameState.lastFrameTime = null;
gameState.gameLoopId = requestAnimationFrame(gameLoop);
// Update score display
gameElements.score.textContent = gameState.score;
}
function setupGame() {
// Reset game state for preview
gameState.score = 0;
gameState.isGameOver = false;
gameState.platforms = [];
gameState.powerUps = [];
gameState.currentLevel = 3; // Show medium difficulty
const levelSettings = gameState.levelSettings[gameState.currentLevel];
gameState.gameSpeed = levelSettings.speed * 0.8; // Slightly slower for preview
gameState.platformSpeed = levelSettings.speed * 0.8;
gameState.platformWidth = levelSettings.platformWidth;
// Auto-play demo after 5 seconds
setTimeout(autoPlayDemo, 5000);
// Reset player
gameState.player = {
x: window.innerWidth / 2 - 20,
y: window.innerHeight - 120,
width: 40,
height: 40,
velocityY: 0,
isJumping: false,
canDoubleJump: false
};
// Clear game world
gameElements.gameWorld.innerHTML = '';
// Create player
gameElements.player = document.createElement('div');
gameElements.player.id = 'player';
gameElements.player.className = 'absolute w-10 h-10 bg-white rounded-full shadow-lg player';
gameElements.player.style.left = `${gameState.player.x}px`;
gameElements.player.style.bottom = `${window.innerHeight - gameState.player.y}px`;
gameElements.gameWorld.appendChild(gameElements.player);
// Create initial platform
createPlatform(window.innerWidth / 2 - 40, window.innerHeight - 60, 80, 15, 'bg-green-500');
// Start game loop
gameState.lastFrameTime = performance.now();
gameState.gameLoopId = requestAnimationFrame(gameLoop);
// Update UI
gameElements.score.textContent = gameState.score;
// Play background music
playBackgroundMusic();
}
function stopGame() {
cancelAnimationFrame(gameState.gameLoopId);
audio.background.pause();
}
function gameLoop(timestamp) {
if (gameState.isPaused || gameState.isGameOver) {
cancelAnimationFrame(gameState.gameLoopId);
return;
}
if (!gameState.lastFrameTime) {
gameState.lastFrameTime = timestamp;
}
const deltaTime = timestamp - gameState.lastFrameTime;
gameState.lastFrameTime = timestamp;
// Update game state
updatePlayer(deltaTime);
updatePlatforms(deltaTime);
checkCollisions();
// Check for game over
if (gameState.player.y > window.innerHeight) {
gameOver();
return;
}
// Continue game loop
gameState.gameLoopId = requestAnimationFrame(gameLoop);
}
function updatePlayer(deltaTime) {
// Apply gravity (scaled by deltaTime)
gameState.player.velocityY += 0.0015 * deltaTime;
gameState.player.y += gameState.player.velocityY;
// Update player position
gameElements.player.style.bottom = `${window.innerHeight - gameState.player.y}px`;
// Keep player in bounds horizontally
gameState.player.x = Math.max(0,
Math.min(window.innerWidth - gameState.player.width, gameState.player.x));
gameElements.player.style.left = `${gameState.player.x}px`;
}
function updatePlayer(deltaTime) {
// Apply gravity
gameState.player.velocityY += 0.5 * (deltaTime / 16);
gameState.player.y += gameState.player.velocityY * (deltaTime / 16);
// Update player position
gameElements.player.style.bottom = `${window.innerHeight - gameState.player.y}px`;
gameElements.player.style.left = `${gameState.player.x}px`;
// Keep player in bounds horizontally
if (gameState.player.x < 0) {
gameState.player.x = 0;
} else if (gameState.player.x > window.innerWidth - gameState.player.width) {
gameState.player.x = window.innerWidth - gameState.player.width;
}
}
function updatePlatforms(deltaTime) {
// Move platforms down (simulating player moving up)
gameState.platforms.forEach(platform => {
// Move platform down
platform.y -= gameState.platformSpeed * (deltaTime / 16);
// Handle moving platforms
if (platform.isMoving) {
platform.x += platform.moveSpeed * platform.moveDirection * (deltaTime / 16);
if (platform.x <= 0 || platform.x >= window.innerWidth - platform.width) {
platform.moveDirection *= -1;
}
platform.element.style.left = `${platform.x}px`;
}
platform.element.style.bottom = `${window.innerHeight - platform.y}px`;
// Remove platforms that are off screen
if (platform.y < -platform.height) {
platform.element.remove();
gameState.platforms = gameState.platforms.filter(p => p !== platform);
}
});
// Add new platforms when needed
if (gameState.lastPlatformY - gameState.player.y > 150) {
const platformX = Math.random() * (window.innerWidth - gameState.platformWidth);
const platformY = gameState.lastPlatformY - 100 - Math.random() * 100;
// Random platform color
const colors = ['bg-green-500', 'bg-red-500', 'bg-yellow-500', 'bg-blue-500'];
const color = colors[Math.floor(Math.random() * colors.length)];
createPlatform(platformX, platformY, gameState.platformWidth, gameState.platformHeight, color);
// Occasionally create a power-up
if (Math.random() < gameState.levelSettings[gameState.currentLevel].powerUpChance) {
createPowerUp(platformX + gameState.platformWidth / 2 - 15, platformY + 30);
}
gameState.lastPlatformY = platformY;
// Increase difficulty
if (gameState.score % 5 === 0) {
gameState.platformSpeed += 0.1;
gameState.platformWidth = Math.max(40, gameState.platformWidth - 2);
}
}
}
function updatePowerUps(deltaTime) {
gameState.powerUps.forEach(powerUp => {
powerUp.y -= gameState.platformSpeed * (deltaTime / 16);
powerUp.element.style.bottom = `${window.innerHeight - powerUp.y}px`;
// Remove power-ups that are off screen
if (powerUp.y < -30) {
powerUp.element.remove();
gameState.powerUps = gameState.powerUps.filter(p => p !== powerUp);
}
});
}
function createParticles(x, y, color, count = 10) {
for (let i = 0; i < count; i++) {
const particle = document.createElement('div');
particle.className = 'absolute w-2 h-2 rounded-full';
particle.style.backgroundColor = color;
particle.style.left = `${x}px`;
particle.style.bottom = `${window.innerHeight - y}px`;
particle.style.opacity = '0.8';
const angle = Math.random() * Math.PI * 2;
const speed = 1 + Math.random() * 3;
const lifetime = 500 + Math.random() * 500;
gameElements.gameWorld.appendChild(particle);
let startTime = performance.now();
function updateParticle() {
const elapsed = performance.now() - startTime;
if (elapsed > lifetime) {
particle.remove();
return;
}
const progress = elapsed / lifetime;
particle.style.transform = `translate(${Math.cos(angle) * speed * elapsed/10}px, ${Math.sin(angle) * speed * elapsed/10}px)`;
particle.style.opacity = `${0.8 * (1 - progress)}`;
requestAnimationFrame(updateParticle);
}
requestAnimationFrame(updateParticle);
}
}
function checkCollisions() {
// Check platform collisions
gameState.platforms.forEach(platform => {
const playerBottom = gameState.player.y + gameState.player.height;
const playerRight = gameState.player.x + gameState.player.width;
const platformTop = platform.y;
const platformRight = platform.x + platform.width;
// More precise collision detection
if (
playerBottom >= platformTop &&
playerBottom <= platformTop + 15 && // Landing threshold
playerRight > platform.x + 5 && // Padding to prevent edge cases
gameState.player.x < platformRight - 5 && // Padding to prevent edge cases
gameState.player.velocityY >= 0 && // Only when falling
!gameState.player.isJumping // Not already jumping
) {
// Land on platform
gameState.player.y = platform.y - gameState.player.height;
gameState.player.velocityY = 0;
gameState.player.isJumping = false;
gameState.player.canDoubleJump = true;
// Add jump effect
gameElements.player.classList.add('jump');
setTimeout(() => {
gameElements.player.classList.remove('jump');
}, 500);
// Play sound with pitch based on platform type
if (!gameState.isMuted) {
audio.land.play();
audio.land.currentTime = 0;
if (platform.element.classList.contains('bg-red-500')) {
audio.land.playbackRate = 1.2;
} else if (platform.element.classList.contains('bg-yellow-500')) {
audio.land.playbackRate = 1.4;
} else if (platform.element.classList.contains('bg-blue-500')) {
audio.land.playbackRate = 1.6;
}
}
// Platform landing particles
createParticles(
gameState.player.x + gameState.player.width/2,
platform.y,
platform.element.classList.contains('bg-red-500') ? '#ef4444' :
platform.element.classList.contains('bg-yellow-500') ? '#f59e0b' :
platform.element.classList.contains('bg-blue-500') ? '#3b82f6' : '#10b981',
15
);
// Score points based on platform color
let points = 1;
if (platform.element.classList.contains('bg-red-500')) points = 2;
if (platform.element.classList.contains('bg-yellow-500')) points = 3;
if (platform.element.classList.contains('bg-blue-500')) points = 4;
gameState.score += points;
gameElements.score.textContent = gameState.score;
// Update high score
if (gameState.score > gameState.highScore) {
gameState.highScore = gameState.score;
localStorage.setItem('highScore', gameState.highScore);
}
}
});
// Check power-up collisions
gameState.powerUps.forEach(powerUp => {
if (
gameState.player.y + gameState.player.height > powerUp.y &&
gameState.player.y < powerUp.y + 30 &&
gameState.player.x + gameState.player.width > powerUp.x &&
gameState.player.x < powerUp.x + 30
) {
// Collect power-up
powerUp.element.remove();
gameState.powerUps = gameState.powerUps.filter(p => p !== powerUp);
// Apply power-up effect
if (!gameState.isMuted) audio.powerup.play();
if (powerUp.type === 'doubleJump') {
createParticles(powerUp.x + 15, powerUp.y + 15, '#fbbf24', 20);
gameState.player.canDoubleJump = true;
gameElements.player.classList.add('bg-yellow-300');
setTimeout(() => {
gameElements.player.classList.remove('bg-yellow-300');
}, 3000);
} else if (powerUp.type === 'invincibility') {
gameState.player.isInvincible = true;
gameElements.player.classList.add('bg-blue-300', 'opacity-75');
setTimeout(() => {
gameState.player.isInvincible = false;
gameElements.player.classList.remove('bg-blue-300', 'opacity-75');
}, 5000);
}
}
});
}
function createPlatform(x, y, width, height, color, isMoving = false) {
const platform = document.createElement('div');
platform.className = `absolute ${color} rounded platform`;
platform.style.left = `${x}px`;
platform.style.bottom = `${window.innerHeight - y}px`;
platform.style.width = `${width}px`;
platform.style.height = `${height}px`;
gameElements.gameWorld.appendChild(platform);
gameState.platforms.push({
element: platform,
x,
y,
width,
height,
isMoving,
moveDirection: isMoving ? (Math.random() > 0.5 ? 1 : -1) : 0,
moveSpeed: isMoving ? 2 : 0
});
gameState.lastPlatformY = y;
}
function createPowerUp(x, y, type) {
const powerUp = document.createElement('div');
powerUp.className = 'absolute w-8 h-8 bg-yellow-400 rounded-full flex items-center justify-center text-xl';
powerUp.textContent = '⭐';
powerUp.style.left = `${x}px`;
powerUp.style.bottom = `${window.innerHeight - y}px`;
gameElements.gameWorld.appendChild(powerUp);
if (type === 'invincibility') {
powerUp.textContent = '🛡️';
powerUp.className = 'absolute w-8 h-8 bg-blue-400 rounded-full flex items-center justify-center text-xl';
}
gameState.powerUps.push({
element: powerUp,
x,
y,
width: 30,
height: 30,
type
});
}
let touchStartX = 0;
let isDragging = false;
function handleGameTap(e) {
// Prevent default for touch events
if (e.type.includes('touch')) e.preventDefault();
// Handle swipe/touch movement
if (e.type === 'touchstart') {
touchStartX = e.touches[0].clientX;
isDragging = true;
// Add touch feedback
const touchIndicator = document.createElement('div');
touchIndicator.className = 'absolute w-16 h-16 bg-white bg-opacity-20 rounded-full -translate-x-1/2 -translate-y-1/2';
touchIndicator.style.left = `${e.touches[0].clientX}px`;
touchIndicator.style.top = `${e.touches[0].clientY}px`;
document.body.appendChild(touchIndicator);
setTimeout(() => {
touchIndicator.remove();
}, 300);
return;
}
if (e.type === 'touchmove' && isDragging) {
const touchX = e.touches[0].clientX;
const diff = touchX - touchStartX;
gameState.player.x = Math.max(0, Math.min(window.innerWidth - gameState.player.width, gameState.player.x + diff * 0.5));
touchStartX = touchX;
return;
}
if (e.type === 'touchend') {
isDragging = false;
}
if (gameState.isPaused || gameState.isGameOver) return;
const now = Date.now();
const isQuickJump = now - gameState.lastJumpTime < 300;
// Jump if on platform or can double jump or has combo
if (!gameState.player.isJumping || gameState.player.canDoubleJump || isQuickJump) {
gameState.player.velocityY = -15;
gameState.player.isJumping = true;
gameState.lastJumpTime = now;
// Combo system
if (isQuickJump) {
gameState.player.comboCount++;
if (gameState.player.comboCount % 3 === 0) {
// Bonus points for combos
gameState.score += gameState.player.comboCount;
gameElements.score.textContent = gameState.score;
// Visual feedback
const comboText = document.createElement('div');
comboText.className = 'absolute text-yellow-300 font-bold text-xl';
comboText.textContent = `COMBO x${gameState.player.comboCount}!`;
comboText.style.left = `${gameState.player.x}px`;
comboText.style.bottom = `${window.innerHeight - gameState.player.y + 20}px`;
gameElements.gameWorld.appendChild(comboText);
// Animate combo text
setTimeout(() => {
comboText.style.transform = 'translateY(-30px)';
comboText.style.opacity = '0';
setTimeout(() => comboText.remove(), 500);
}, 10);
if (!gameState.isMuted) audio.combo.play();
}
} else {
gameState.player.comboCount = 0;
}
// Use up double jump if applicable
if (gameState.player.canDoubleJump && gameState.player.isJumping) {
gameState.player.canDoubleJump = false;
}
// Play sound with pitch variation
if (!gameState.isMuted) {
audio.jump.play();
audio.jump.currentTime = 0;
audio.jump.playbackRate = 1 + Math.random() * 0.2; // Random pitch
}
// Add jump effect
gameElements.player.classList.add('jump');
// Jump particles
createParticles(
gameState.player.x + gameState.player.width/2,
gameState.player.y + gameState.player.height,
'#ffffff',
5
);
setTimeout(() => {
gameElements.player.classList.remove('jump');
}, 500);
}
}
function gameOver() {
// Show interstitial ad (placeholder for actual implementation)
// In production:
// admob.interstitial.load().then(() => admob.interstitial.show())
// .then(() => {
// // Ad shown, continue with game over
// })
// Check if player completed the level
if (gameState.score >= gameState.currentLevel * 20 && gameState.currentLevel === gameState.unlockedLevels) {
gameState.unlockedLevels = Math.min(gameState.unlockedLevels + 1, 6);
alert(`Congratulations! You unlocked level ${gameState.unlockedLevels}`);
}
gameState.isGameOver = true;
cancelAnimationFrame(gameState.gameLoopId);
// Play game over sound
if (!gameState.isMuted) audio.gameOver.play();
// Update game over screen
gameElements.finalScore.textContent = gameState.score;
gameElements.highScore.textContent = gameState.highScore;
// Show game over screen
showScreen('game-over');
}
function pauseGame() {
gameState.isPaused = true;
document.getElementById('pause-score').textContent = gameState.score;
screens.pause.classList.remove('hidden');
audio.background.pause();
// Add button event listeners
document.getElementById('restart-btn').addEventListener('click', function() {
if (!gameState.isMuted) audio.button.play();
startGame();
resumeGame();
});
document.getElementById('pause-settings-btn').addEventListener('click', function() {
if (!gameState.isMuted) audio.button.play();
// Show settings modal (would be implemented separately)
alert("Settings functionality coming soon!");
});
}
function resumeGame() {
gameState.isPaused = false;
screens.pause.classList.add('hidden');
gameState.lastFrameTime = performance.now();
gameState.gameLoopId = requestAnimationFrame(gameLoop);
playBackgroundMusic();
}
function continueAfterAd() {
// Show rewarded ad (placeholder for actual implementation)
// In production:
// admob.rewarded.load().then(() => admob.rewarded.show())
// .then(() => {
// // Reward the user
// startGame();
// showScreen('game');
// })
// For now simulate watching an ad
alert("Thanks for watching the ad! Here's your continue.");
startGame();
showScreen('game');
}
function autoPlayDemo() {
if (gameState.currentScreen !== 'game') return;
// Make player jump automatically
const jumpInterval = setInterval(() => {
if (gameState.isGameOver || gameState.currentScreen !== 'game') {
clearInterval(jumpInterval);
return;
}
// Random jump timing
if (Math.random() > 0.3) {
handleGameTap({type: 'click'});
}
// Random left/right movement
gameState.player.x += (Math.random() - 0.5) * 30;
gameState.player.x = Math.max(0, Math.min(window.innerWidth - gameState.player.width, gameState.player.x));
gameElements.player.style.left = `${gameState.player.x}px`;
}, 800);
// End demo after 15 seconds
setTimeout(() => {
clearInterval(jumpInterval);
if (gameState.currentScreen === 'game') {
gameOver();
}
}, 15000);
}
function toggleMute() {
gameState.isMuted = !gameState.isMuted;
if (gameState.isMuted) {
buttons.muteIcon.textContent = '🔇';
buttons.gameMuteIcon.textContent = '🔇';
audio.background.pause();
} else {
buttons.muteIcon.textContent = '🔊';
buttons.gameMuteIcon.textContent = '🔊';
playBackgroundMusic();
}
}
function playBackgroundMusic() {
if (!gameState.isMuted && gameState.currentScreen === 'game') {
audio.background.loop = true;
audio.background.play().catch(e => console.log("Audio play failed:", e));
}
}
// Asset loading
function loadAssets() {
const assets = [
{ type: 'audio', src: 'https://assets.mixkit.co/sfx/preview/mixkit-quick-jump-arcade-game-239.mp3' },
{ type: 'audio', src: 'https://assets.mixkit.co/sfx/preview/mixkit-arcade-game-jump-coin-216.mp3' },
{ type: 'audio', src: 'https://assets.mixkit.co/sfx/preview/mixkit-retro-arcade-lose-2027.mp3' },
{ type: 'audio', src: 'https://assets.mixkit.co/music/preview/mixkit-game-show-suspense-waiting-668.mp3' },
{ type: 'audio', src: 'https://assets.mixkit.co/sfx/preview/mixkit-achievement-bell-600.mp3' },
{ type: 'audio', src: 'https://assets.mixkit.co/sfx/preview/mixkit-extra-bonus-in-a-video-game-2045.mp3' },
{ type: 'video', src: 'https://assets.mixkit.co/videos/preview/mixkit-game-background-loop-482-large.mp4' }
];
let loaded = 0;
assets.forEach(asset => {
if (asset.type === 'audio') {
const audio = new Audio();
audio.src = asset.src;
audio.addEventListener('canplaythrough', () => {
loaded++;
updateProgress(loaded, assets.length);
});
} else {
// For video/images
const element = document.createElement(asset.type === 'video' ? 'video' : 'img');
element.src = asset.src;
element.addEventListener('loadeddata', () => {
loaded++;
updateProgress(loaded, assets.length);
});
}
});
}
function updateProgress(loaded, total) {
const percent = Math.floor((loaded / total) * 100);
document.getElementById('progress-text').textContent = `${percent}%`;
if (loaded === total) {
setTimeout(() => {
document.getElementById('loading-screen').classList.add('hidden');
}, 500);
}
}
// Initialize game properly
document.addEventListener('DOMContentLoaded', () => {
// Set default language
gameState.currentLanguage = 'en';
updateLanguage();
// Hide splash screen after 3 seconds
setTimeout(() => {
document.getElementById('splash-screen').classList.add('hidden');
document.getElementById('loading-screen').classList.remove('hidden');
// Load assets
loadAssets();
}, 3000);
// Setup button event listeners
getStartedBtn.addEventListener('click', () => {
welcomeScreen.classList.add('hidden');
document.getElementById('register-screen').classList.remove('hidden');
});
// Initialize game controls
setupGameControls();
});
function setupGameControls() {
// Player jump on spacebar or click/tap
document.addEventListener('keydown', (e) => {
if (e.code === 'Space' && gameState.currentScreen === 'game') {
handleJump();
}
});
gameElements.gameWorld.addEventListener('click', handleJump);
gameElements.gameWorld.addEventListener('touchstart', (e) => {
e.preventDefault();
handleJump();
});
}
function handleJump() {
if (gameState.isPaused || gameState.isGameOver) return;
// Jump if on platform or can double jump
if (!gameState.player.isJumping || gameState.player.canDoubleJump) {
gameState.player.velocityY = -15;
gameState.player.isJumping = true;
// Use up double jump if applicable
if (gameState.player.canDoubleJump && gameState.player.isJumping) {
gameState.player.canDoubleJump = false;
}
// Play sound
if (!gameState.isMuted) {
audio.jump.play();
audio.jump.currentTime = 0;
}
// Add jump effect
gameElements.player.classList.add('jump');
setTimeout(() => {
gameElements.player.classList.remove('jump');
}, 500);
}
}
// Load assets and show welcome screen when done
function loadAssets() {
const assets = [
{ type: 'audio', src: 'https://assets.mixkit.co/sfx/preview/mixkit-quick-jump-arcade-game-239.mp3' },
{ type: 'audio', src: 'https://assets.mixkit.co/sfx/preview/mixkit-arcade-game-jump-coin-216.mp3' },
{ type: 'audio', src: 'https://assets.mixkit.co/sfx/preview/mixkit-retro-arcade-lose-2027.mp3' },
{ type: 'audio', src: 'https://assets.mixkit.co/music/preview/mixkit-game-show-suspense-waiting-668.mp3' },
{ type: 'audio', src: 'https://assets.mixkit.co/sfx/preview/mixkit-achievement-bell-600.mp3' },
{ type: 'audio', src: 'https://assets.mixkit.co/sfx/preview/mixkit-extra-bonus-in-a-video-game-2045.mp3' }
];
let loaded = 0;
const total = assets.length;
function updateProgress() {
const percent = Math.floor((loaded / total) * 100);
document.getElementById('progress-text').textContent = `${percent}%`;
if (loaded === total) {
setTimeout(() => {
document.getElementById('loading-screen').classList.add('hidden');
document.getElementById('welcome-screen').classList.remove('hidden');
}, 500);
}
}
assets.forEach(asset => {
if (asset.type === 'audio') {
const audio = new Audio();
audio.src = asset.src;
audio.addEventListener('canplaythrough', () => {
loaded++;
updateProgress();
});
audio.load();
}
});
}
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Malicious7/stack-jumper" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>