Spaces:
Running
Running
Download index.html from Malicious7/stack-jumper: direct link, hf CLI and curl.
- Browser
- Download file 71.8 kB
-
https://huggingface.co/spaces/Malicious7/stack-jumper/resolve/main/index.html
- Command line
-
hf download hf://spaces/Malicious7/stack-jumper/index.html
-
curl -L -o index.html https://huggingface.co/spaces/Malicious7/stack-jumper/resolve/main/index.html
71.8 kB
| <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> |