Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>SpaceX Simulator: Build Like Elon</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;700;900&family=Roboto:wght@300;400;500;700&display=swap'); | |
| body { | |
| font-family: 'Roboto', sans-serif; | |
| background-color: #0a0a1a; | |
| color: #e0e0e0; | |
| overflow-x: hidden; | |
| } | |
| .orbitron { | |
| font-family: 'Orbitron', sans-serif; | |
| } | |
| .space-gradient { | |
| background: linear-gradient(135deg, #000428 0%, #004e92 100%); | |
| } | |
| .rocket-part { | |
| transition: all 0.3s ease; | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| } | |
| .rocket-part:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 10px 20px rgba(0, 150, 255, 0.3); | |
| border-color: rgba(0, 150, 255, 0.5); | |
| } | |
| .selected-part { | |
| box-shadow: 0 0 0 3px rgba(0, 150, 255, 0.7); | |
| border-color: rgba(0, 150, 255, 0.9); | |
| } | |
| .planet-card { | |
| transition: all 0.3s ease; | |
| background-size: cover; | |
| background-position: center; | |
| } | |
| .planet-card:hover { | |
| transform: scale(1.05); | |
| box-shadow: 0 10px 25px rgba(0, 150, 255, 0.4); | |
| } | |
| .rocket-display { | |
| perspective: 1000px; | |
| position: relative; | |
| height: 400px; | |
| overflow: hidden; | |
| background: linear-gradient(to bottom, #000000 0%, #000428 100%); | |
| } | |
| .space-view { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: url('https://images.unsplash.com/photo-1506318137071-a8e06380a023?q=80&w=3000') no-repeat center center; | |
| background-size: cover; | |
| opacity: 0; | |
| transition: opacity 1s ease; | |
| } | |
| .space-view.active { | |
| opacity: 1; | |
| } | |
| .planet-arrival { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| width: 200px; | |
| height: 200px; | |
| border-radius: 50%; | |
| background-size: cover; | |
| background-position: center; | |
| box-shadow: 0 0 50px rgba(255, 255, 255, 0.5); | |
| opacity: 0; | |
| transition: all 1s ease; | |
| z-index: 15; | |
| } | |
| .planet-arrival.active { | |
| opacity: 1; | |
| transform: translate(-50%, -50%) scale(1.5); | |
| } | |
| .rocket-3d { | |
| transform-style: preserve-3d; | |
| transition: transform 0.5s ease; | |
| position: absolute; | |
| bottom: 0; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| z-index: 10; | |
| } | |
| .flame { | |
| position: absolute; | |
| bottom: -20px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| width: 40px; | |
| height: 60px; | |
| background: linear-gradient(to top, #ff4500, #ff8c00, #ffd700); | |
| border-radius: 50% 50% 20% 20%; | |
| filter: blur(5px); | |
| opacity: 0; | |
| transition: opacity 0.3s ease; | |
| } | |
| .launching .flame { | |
| opacity: 1; | |
| animation: flame-flicker 0.3s infinite alternate; | |
| } | |
| @keyframes flame-flicker { | |
| 0% { height: 60px; width: 40px; } | |
| 100% { height: 80px; width: 50px; } | |
| } | |
| .stars { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| z-index: -1; | |
| } | |
| .star { | |
| position: absolute; | |
| background-color: white; | |
| border-radius: 50%; | |
| animation: twinkle 5s infinite ease-in-out; | |
| } | |
| @keyframes twinkle { | |
| 0% { opacity: 0.2; } | |
| 50% { opacity: 1; } | |
| 100% { opacity: 0.2; } | |
| } | |
| .elon-avatar { | |
| transition: all 0.3s ease; | |
| } | |
| .elon-avatar:hover { | |
| transform: scale(1.05); | |
| filter: drop-shadow(0 0 10px rgba(255, 215, 0, 0.7)); | |
| } | |
| .mission-control { | |
| background: rgba(10, 20, 30, 0.9); | |
| border: 1px solid rgba(0, 150, 255, 0.3); | |
| box-shadow: 0 0 30px rgba(0, 150, 255, 0.2); | |
| } | |
| .console-text { | |
| font-family: 'Courier New', monospace; | |
| color: #00ff00; | |
| text-shadow: 0 0 5px rgba(0, 255, 0, 0.7); | |
| } | |
| .progress-bar { | |
| height: 10px; | |
| background: linear-gradient(90deg, #004e92, #00b4ff); | |
| transition: width 0.5s ease; | |
| } | |
| .blink { | |
| animation: blink 1s infinite; | |
| } | |
| @keyframes blink { | |
| 0% { opacity: 1; } | |
| 50% { opacity: 0.5; } | |
| 100% { opacity: 1; } | |
| } | |
| /* Launch effects */ | |
| .launch-smoke { | |
| position: absolute; | |
| bottom: 0; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| width: 200px; | |
| height: 100px; | |
| background: radial-gradient(circle, rgba(255,255,255,0.8) 0%, rgba(200,200,200,0.5) 50%, rgba(100,100,100,0) 100%); | |
| border-radius: 50%; | |
| filter: blur(10px); | |
| opacity: 0; | |
| z-index: 5; | |
| } | |
| .launching .launch-smoke { | |
| opacity: 1; | |
| animation: smoke-expand 2s forwards; | |
| } | |
| @keyframes smoke-expand { | |
| 0% { transform: translateX(-50%) scale(0.5); opacity: 1; } | |
| 100% { transform: translateX(-50%) scale(3); opacity: 0; } | |
| } | |
| .launch-shake { | |
| animation: shake 0.5s linear infinite; | |
| } | |
| @keyframes shake { | |
| 0% { transform: translateX(-50%) translateY(0); } | |
| 25% { transform: translateX(-50%) translateY(-2px) translateX(2px); } | |
| 50% { transform: translateX(-50%) translateY(0); } | |
| 75% { transform: translateX(-50%) translateY(2px) translateX(-2px); } | |
| 100% { transform: translateX(-50%) translateY(0); } | |
| } | |
| .rocket-ascent { | |
| animation: rocket-fly 8s forwards; | |
| } | |
| @keyframes rocket-fly { | |
| 0% { bottom: 0; transform: translateX(-50%); } | |
| 20% { bottom: 20%; transform: translateX(-50%); } | |
| 40% { bottom: 40%; transform: translateX(-50%); } | |
| 60% { bottom: 60%; transform: translateX(-50%); } | |
| 80% { bottom: 80%; transform: translateX(-50%); } | |
| 100% { bottom: 120%; transform: translateX(-50%); } | |
| } | |
| .explosion { | |
| position: absolute; | |
| width: 300px; | |
| height: 300px; | |
| background: radial-gradient(circle, rgba(255,100,0,0.8) 0%, rgba(255,50,0,0) 70%); | |
| border-radius: 50%; | |
| filter: blur(20px); | |
| opacity: 0; | |
| transform: scale(0); | |
| z-index: 10; | |
| } | |
| .explode { | |
| animation: explosion 1s forwards; | |
| } | |
| @keyframes explosion { | |
| 0% { transform: scale(0); opacity: 1; } | |
| 100% { transform: scale(1); opacity: 0; } | |
| } | |
| .countdown { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| font-size: 5rem; | |
| color: white; | |
| text-shadow: 0 0 10px red; | |
| opacity: 0; | |
| z-index: 20; | |
| } | |
| .countdown-animate { | |
| animation: countdown 1s forwards; | |
| } | |
| @keyframes countdown { | |
| 0% { transform: translate(-50%, -50%) scale(0.5); opacity: 0; } | |
| 50% { transform: translate(-50%, -50%) scale(1.2); opacity: 1; } | |
| 100% { transform: translate(-50%, -50%) scale(1); opacity: 0; } | |
| } | |
| .stage-separation { | |
| position: absolute; | |
| width: 100%; | |
| height: 2px; | |
| background: white; | |
| opacity: 0.8; | |
| z-index: 5; | |
| transform: scaleX(0); | |
| } | |
| .separate { | |
| animation: separate 0.5s forwards; | |
| } | |
| @keyframes separate { | |
| 0% { transform: scaleX(0); opacity: 0; } | |
| 100% { transform: scaleX(1); opacity: 0.8; } | |
| } | |
| .success-marker { | |
| position: absolute; | |
| top: 20px; | |
| right: 20px; | |
| width: 60px; | |
| height: 60px; | |
| background: rgba(0, 255, 0, 0.2); | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| opacity: 0; | |
| transform: scale(0); | |
| z-index: 25; | |
| } | |
| .success-animate { | |
| animation: success 1s forwards; | |
| } | |
| @keyframes success { | |
| 0% { transform: scale(0); opacity: 0; } | |
| 50% { transform: scale(1.2); opacity: 1; } | |
| 100% { transform: scale(1); opacity: 1; } | |
| } | |
| .rocket-component { | |
| width: 100%; | |
| height: 100%; | |
| background-size: contain; | |
| background-repeat: no-repeat; | |
| background-position: center; | |
| } | |
| .rocket-in-space { | |
| position: absolute; | |
| width: 100px; | |
| height: 200px; | |
| background-size: contain; | |
| background-repeat: no-repeat; | |
| background-position: center; | |
| opacity: 0; | |
| transition: all 1s ease; | |
| z-index: 15; | |
| } | |
| .rocket-in-space.active { | |
| opacity: 1; | |
| } | |
| .arrival-message { | |
| position: absolute; | |
| top: 70%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| background: rgba(0, 0, 0, 0.7); | |
| padding: 20px; | |
| border-radius: 10px; | |
| text-align: center; | |
| opacity: 0; | |
| transition: opacity 1s ease; | |
| z-index: 30; | |
| width: 80%; | |
| max-width: 500px; | |
| } | |
| .arrival-message.active { | |
| opacity: 1; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="stars" id="stars"></div> | |
| <div class="min-h-screen flex flex-col"> | |
| <!-- Header --> | |
| <header class="space-gradient py-4 px-6 shadow-lg"> | |
| <div class="container mx-auto flex justify-between items-center"> | |
| <div class="flex items-center space-x-4"> | |
| <i class="fas fa-rocket text-3xl text-yellow-400"></i> | |
| <h1 class="orbitron text-2xl md:text-3xl font-bold text-white"> | |
| <span class="text-yellow-400">SPACEX</span> SIMULATOR | |
| </h1> | |
| </div> | |
| <div class="flex items-center space-x-4"> | |
| <div class="hidden md:flex items-center space-x-2"> | |
| <i class="fas fa-coins text-yellow-400"></i> | |
| <span class="font-bold" id="funds">$100,000,000</span> | |
| </div> | |
| <div class="hidden md:flex items-center space-x-2"> | |
| <i class="fas fa-star text-blue-400"></i> | |
| <span class="font-bold" id="reputation">100</span> | |
| </div> | |
| <button class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg orbitron"> | |
| MISSION CONTROL | |
| </button> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- Main Content --> | |
| <main class="flex-grow container mx-auto px-4 py-8"> | |
| <div class="grid grid-cols-1 lg:grid-cols-3 gap-8"> | |
| <!-- Rocket Builder Panel --> | |
| <div class="lg:col-span-2 space-y-8"> | |
| <!-- Rocket Display --> | |
| <div class="bg-gray-900 rounded-xl p-6 shadow-xl border border-gray-800"> | |
| <h2 class="orbitron text-xl text-blue-400 mb-4">ROCKET DESIGN</h2> | |
| <div class="rocket-display flex justify-center items-end" id="rocket-display"> | |
| <div class="space-view" id="space-view"></div> | |
| <div class="planet-arrival" id="planet-arrival"></div> | |
| <div class="rocket-in-space" id="rocket-in-space"></div> | |
| <div class="arrival-message" id="arrival-message"></div> | |
| <div class="rocket-3d" id="rocket-model"> | |
| <div class="flame" id="rocket-flame"></div> | |
| <div class="launch-smoke"></div> | |
| </div> | |
| <div class="explosion" id="explosion"></div> | |
| <div class="countdown" id="countdown"></div> | |
| <div class="stage-separation" id="stage-separation"></div> | |
| <div class="success-marker" id="success-marker"> | |
| <i class="fas fa-check-circle text-3xl text-green-500"></i> | |
| </div> | |
| </div> | |
| <div class="mt-6 grid grid-cols-3 gap-4"> | |
| <div class="bg-gray-800 rounded-lg p-3 text-center"> | |
| <p class="text-gray-400 text-sm">DELTA-V</p> | |
| <p class="orbitron text-xl" id="delta-v">0 m/s</p> | |
| </div> | |
| <div class="bg-gray-800 rounded-lg p-3 text-center"> | |
| <p class="text-gray-400 text-sm">PAYLOAD</p> | |
| <p class="orbitron text-xl" id="payload">0 kg</p> | |
| </div> | |
| <div class="bg-gray-800 rounded-lg p-3 text-center"> | |
| <p class="text-gray-400 text-sm">COST</p> | |
| <p class="orbitron text-xl" id="rocket-cost">$0</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Parts Selection --> | |
| <div class="bg-gray-900 rounded-xl p-6 shadow-xl border border-gray-800"> | |
| <h2 class="orbitron text-xl text-blue-400 mb-4">ROCKET COMPONENTS</h2> | |
| <div class="grid grid-cols-2 md:grid-cols-4 gap-4" id="parts-container"> | |
| <!-- Parts will be added here by JavaScript --> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Mission Control Panel --> | |
| <div class="space-y-8"> | |
| <!-- Elon Musk Avatar --> | |
| <div class="bg-gray-900 rounded-xl p-6 shadow-xl border border-gray-800 flex flex-col items-center"> | |
| <div class="elon-avatar mb-4 relative"> | |
| <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/3/34/Elon_Musk_Royal_Society_%28crop2%29.jpg/1200px-Elon_Musk_Royal_Society_%28crop2%29.jpg" | |
| alt="Elon Musk" class="w-32 h-32 rounded-full object-cover border-4 border-blue-500"> | |
| <div class="absolute -bottom-2 -right-2 bg-yellow-500 text-black rounded-full w-10 h-10 flex items-center justify-center"> | |
| <i class="fas fa-crown"></i> | |
| </div> | |
| </div> | |
| <h3 class="orbitron text-xl text-center">ELON MUSK</h3> | |
| <p class="text-gray-400 text-center mb-4">CEO & Chief Engineer</p> | |
| <div class="w-full bg-gray-800 rounded-full h-2.5 mb-4"> | |
| <div class="progress-bar rounded-full h-2.5" style="width: 100%"></div> | |
| </div> | |
| <div class="mission-control w-full bg-gray-800 rounded-lg p-4 h-48 overflow-y-auto" id="mission-console"> | |
| <p class="console-text">> Welcome to SpaceX Simulator</p> | |
| <p class="console-text">> Initializing systems...</p> | |
| <p class="console-text">> Ready to build some rockets!</p> | |
| <p class="console-text blink">> _</p> | |
| </div> | |
| </div> | |
| <!-- Destination Selector --> | |
| <div class="bg-gray-900 rounded-xl p-6 shadow-xl border border-gray-800"> | |
| <h2 class="orbitron text-xl text-blue-400 mb-4">MISSION DESTINATION</h2> | |
| <div class="grid grid-cols-2 gap-4" id="destinations"> | |
| <!-- Planets will be added here by JavaScript --> | |
| </div> | |
| </div> | |
| <!-- Launch Controls --> | |
| <div class="bg-gray-900 rounded-xl p-6 shadow-xl border border-gray-800"> | |
| <h2 class="orbitron text-xl text-blue-400 mb-4">MISSION CONTROL</h2> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-gray-400 mb-2">Mission Name</label> | |
| <input type="text" class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" | |
| placeholder="e.g. Mars Colony 1" id="mission-name"> | |
| </div> | |
| <div> | |
| <label class="block text-gray-400 mb-2">Payload Type</label> | |
| <select class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" id="payload-type"> | |
| <option value="satellite">Communication Satellite</option> | |
| <option value="rover">Planetary Rover</option> | |
| <option value="probe">Scientific Probe</option> | |
| <option value="habitat">Colony Habitat</option> | |
| <option value="crew">Astronaut Crew</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label class="block text-gray-400 mb-2">Payload Mass (kg)</label> | |
| <input type="number" class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" | |
| value="1000" min="100" max="100000" id="payload-mass"> | |
| </div> | |
| <button class="w-full bg-green-600 hover:bg-green-700 text-white py-3 rounded-lg orbitron text-lg flex items-center justify-center space-x-2" id="launch-button"> | |
| <i class="fas fa-rocket"></i> | |
| <span>INITIATE LAUNCH SEQUENCE</span> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| <!-- Footer --> | |
| <footer class="space-gradient py-6 px-6"> | |
| <div class="container mx-auto"> | |
| <div class="flex flex-col md:flex-row justify-between items-center"> | |
| <div class="flex items-center space-x-4 mb-4 md:mb-0"> | |
| <i class="fab fa-twitter text-blue-400 text-xl"></i> | |
| <i class="fab fa-linkedin text-blue-400 text-xl"></i> | |
| <i class="fab fa-instagram text-purple-400 text-xl"></i> | |
| </div> | |
| <p class="text-gray-300 text-sm">© 2023 SpaceX Simulator. Not affiliated with SpaceX or Elon Musk. For educational purposes only.</p> | |
| </div> | |
| </div> | |
| </footer> | |
| </div> | |
| <script> | |
| // Create stars background | |
| function createStars() { | |
| const stars = document.getElementById('stars'); | |
| const count = 200; | |
| for (let i = 0; i < count; i++) { | |
| const star = document.createElement('div'); | |
| star.className = 'star'; | |
| // Random position | |
| const x = Math.random() * 100; | |
| const y = Math.random() * 100; | |
| // Random size | |
| const size = Math.random() * 2; | |
| // Random opacity | |
| const opacity = Math.random(); | |
| // Random animation delay | |
| const delay = Math.random() * 5; | |
| star.style.left = `${x}%`; | |
| star.style.top = `${y}%`; | |
| star.style.width = `${size}px`; | |
| star.style.height = `${size}px`; | |
| star.style.opacity = opacity; | |
| star.style.animationDelay = `${delay}s`; | |
| stars.appendChild(star); | |
| } | |
| } | |
| // Rocket parts data with actual images | |
| const rocketParts = { | |
| firstStages: [ | |
| { id: 'falcon9', name: 'Falcon 9', cost: 5000000, mass: 25000, thrust: 7607, height: 10, | |
| color: 'bg-white', image: 'https://www.spacex.com/static/images/falcon9/falcon9-1.png' }, | |
| { id: 'falcon-heavy', name: 'Falcon Heavy', cost: 9000000, mass: 40000, thrust: 22819, height: 12, | |
| color: 'bg-gray-300', image: 'https://www.spacex.com/static/images/falcon-heavy/falcon-heavy-1.png' }, | |
| { id: 'starship', name: 'Starship', cost: 15000000, mass: 120000, thrust: 75000, height: 18, | |
| color: 'bg-gray-400', image: 'https://www.spacex.com/static/images/starship/starship-1.png' }, | |
| { id: 'nova', name: 'Nova', cost: 20000000, mass: 150000, thrust: 100000, height: 20, | |
| color: 'bg-blue-200', image: 'https://www.nasa.gov/wp-content/uploads/2023/06/sls_artistconcept_0.jpg?w=1536' } | |
| ], | |
| secondStages: [ | |
| { id: 'falcon9-upper', name: 'Falcon 9 Upper', cost: 2000000, mass: 5000, thrust: 934, height: 6, | |
| color: 'bg-white', image: 'https://www.spacex.com/static/images/falcon9/falcon9-upper-stage.png' }, | |
| { id: 'falcon-heavy-upper', name: 'Falcon Heavy Upper', cost: 3000000, mass: 8000, thrust: 934, height: 8, | |
| color: 'bg-gray-300', image: 'https://www.spacex.com/static/images/falcon-heavy/falcon-heavy-upper-stage.png' }, | |
| { id: 'starship-upper', name: 'Starship Upper', cost: 5000000, mass: 15000, thrust: 3000, height: 12, | |
| color: 'bg-gray-400', image: 'https://www.spacex.com/static/images/starship/starship-upper-stage.png' }, | |
| { id: 'nova-upper', name: 'Nova Upper', cost: 7000000, mass: 20000, thrust: 5000, height: 14, | |
| color: 'bg-blue-200', image: 'https://www.nasa.gov/wp-content/uploads/2023/06/sls_upperstage_0.jpg?w=1536' } | |
| ], | |
| fairings: [ | |
| { id: 'standard', name: 'Standard Fairing', cost: 1000000, mass: 2000, height: 5, | |
| color: 'bg-white', image: 'https://www.spacex.com/static/images/falcon9/fairing.png' }, | |
| { id: 'extended', name: 'Extended Fairing', cost: 1500000, mass: 3000, height: 7, | |
| color: 'bg-gray-300', image: 'https://www.spacex.com/static/images/falcon-heavy/extended-fairing.png' }, | |
| { id: 'heavy', name: 'Heavy Fairing', cost: 2000000, mass: 4000, height: 8, | |
| color: 'bg-gray-400', image: 'https://www.spacex.com/static/images/starship/heavy-fairing.png' }, | |
| { id: 'none', name: 'No Fairing', cost: 0, mass: 0, height: 0, | |
| color: 'transparent', image: '' } | |
| ], | |
| engines: [ | |
| { id: 'merlin', name: 'Merlin', cost: 1000000, thrust: 845, isp: 282, | |
| color: 'bg-gray-500', image: 'https://www.spacex.com/static/images/falcon9/merlin-engine.png' }, | |
| { id: 'raptor', name: 'Raptor', cost: 2000000, thrust: 2000, isp: 330, | |
| color: 'bg-gray-600', image: 'https://www.spacex.com/static/images/starship/raptor-engine.png' }, | |
| { id: 'be-4', name: 'BE-4', cost: 2500000, thrust: 2400, isp: 340, | |
| color: 'bg-blue-600', image: 'https://www.blueorigin.com/assets/engines/be4/be4-engine.jpg' }, | |
| { id: 'rs-25', name: 'RS-25', cost: 3000000, thrust: 1860, isp: 366, | |
| color: 'bg-red-600', image: 'https://www.nasa.gov/wp-content/uploads/2023/06/rs25-engine.jpg?w=1536' } | |
| ] | |
| }; | |
| // Planets data with actual images | |
| const destinations = [ | |
| { id: 'leo', name: 'Low Earth Orbit', distance: 200, difficulty: 1, | |
| image: 'https://science.nasa.gov/wp-content/uploads/2023/09/iss067e092866_large.jpg?w=1536', | |
| arrivalImage: 'https://science.nasa.gov/wp-content/uploads/2023/09/earth-from-space.jpg?w=1536' }, | |
| { id: 'moon', name: 'The Moon', distance: 384400, difficulty: 2, | |
| image: 'https://science.nasa.gov/wp-content/uploads/2023/09/webb-southern-ring-nebula-1.jpg?w=1536', | |
| arrivalImage: 'https://science.nasa.gov/wp-content/uploads/2023/09/moon-surface.jpg?w=1536' }, | |
| { id: 'mars', name: 'Mars', distance: 225000000, difficulty: 3, | |
| image: 'https://science.nasa.gov/wp-content/uploads/2023/09/pia25980-16.jpg?w=1536', | |
| arrivalImage: 'https://science.nasa.gov/wp-content/uploads/2023/09/mars-surface.jpg?w=1536' }, | |
| { id: 'jupiter', name: 'Jupiter', distance: 778000000, difficulty: 4, | |
| image: 'https://science.nasa.gov/wp-content/uploads/2023/09/jupiter-1.jpg?w=1536', | |
| arrivalImage: 'https://science.nasa.gov/wp-content/uploads/2023/09/jupiter-closeup.jpg?w=1536' }, | |
| { id: 'saturn', name: 'Saturn', distance: 1400000000, difficulty: 5, | |
| image: 'https://science.nasa.gov/wp-content/uploads/2023/09/saturn-1.jpg?w=1536', | |
| arrivalImage: 'https://science.nasa.gov/wp-content/uploads/2023/09/saturn-rings.jpg?w=1536' }, | |
| { id: 'pluto', name: 'Pluto', distance: 5900000000, difficulty: 6, | |
| image: 'https://science.nasa.gov/wp-content/uploads/2023/09/pluto-1.jpg?w=1536', | |
| arrivalImage: 'https://science.nasa.gov/wp-content/uploads/2023/09/pluto-surface.jpg?w=1536' } | |
| ]; | |
| // Game state | |
| let gameState = { | |
| funds: 100000000, // $100,000,000 initial balance | |
| reputation: 100, | |
| currentRocket: { | |
| firstStage: null, | |
| secondStage: null, | |
| fairing: null, | |
| engines: null | |
| }, | |
| currentDestination: null, | |
| currentPayload: { | |
| type: 'satellite', | |
| mass: 1000 | |
| } | |
| }; | |
| // Initialize the game | |
| function initGame() { | |
| createStars(); | |
| renderParts(); | |
| renderDestinations(); | |
| updateUI(); | |
| // Set up event listeners | |
| document.getElementById('launch-button').addEventListener('click', launchRocket); | |
| document.getElementById('payload-type').addEventListener('change', updatePayload); | |
| document.getElementById('payload-mass').addEventListener('input', updatePayload); | |
| } | |
| // Render rocket parts selection | |
| function renderParts() { | |
| const partsContainer = document.getElementById('parts-container'); | |
| // First stages | |
| rocketParts.firstStages.forEach(part => { | |
| const partElement = createPartElement(part, 'firstStage'); | |
| partsContainer.appendChild(partElement); | |
| }); | |
| // Second stages | |
| rocketParts.secondStages.forEach(part => { | |
| const partElement = createPartElement(part, 'secondStage'); | |
| partsContainer.appendChild(partElement); | |
| }); | |
| // Fairings | |
| rocketParts.fairings.forEach(part => { | |
| const partElement = createPartElement(part, 'fairing'); | |
| partsContainer.appendChild(partElement); | |
| }); | |
| // Engines | |
| rocketParts.engines.forEach(part => { | |
| const partElement = createPartElement(part, 'engines'); | |
| partsContainer.appendChild(partElement); | |
| }); | |
| } | |
| // Create a part selection element | |
| function createPartElement(part, partType) { | |
| const partElement = document.createElement('div'); | |
| partElement.className = 'rocket-part bg-gray-800 rounded-lg p-4 cursor-pointer flex flex-col items-center'; | |
| partElement.dataset.partId = part.id; | |
| partElement.dataset.partType = partType; | |
| partElement.innerHTML = ` | |
| <div class="w-16 h-16 rounded-full mb-2 flex items-center justify-center"> | |
| ${part.image ? `<img src="${part.image}" alt="${part.name}" class="w-full h-full object-contain">` : '<i class="fas fa-rocket text-xl"></i>'} | |
| </div> | |
| <h3 class="font-bold text-center">${part.name}</h3> | |
| <p class="text-sm text-gray-400 text-center">$${part.cost.toLocaleString()}</p> | |
| `; | |
| partElement.addEventListener('click', () => selectPart(part, partType)); | |
| return partElement; | |
| } | |
| // Render destination selection | |
| function renderDestinations() { | |
| const destinationsContainer = document.getElementById('destinations'); | |
| destinations.forEach(destination => { | |
| const destinationElement = document.createElement('div'); | |
| destinationElement.className = 'planet-card rounded-lg overflow-hidden cursor-pointer relative h-32'; | |
| destinationElement.style.backgroundImage = `url(${destination.image})`; | |
| destinationElement.dataset.destinationId = destination.id; | |
| destinationElement.innerHTML = ` | |
| <div class="absolute inset-0 bg-black bg-opacity-50 hover:bg-opacity-30 transition-all duration-300 flex items-end p-3"> | |
| <div> | |
| <h3 class="font-bold">${destination.name}</h3> | |
| <p class="text-xs">${destination.distance.toLocaleString()} km</p> | |
| </div> | |
| </div> | |
| `; | |
| destinationElement.addEventListener('click', () => selectDestination(destination)); | |
| destinationsContainer.appendChild(destinationElement); | |
| }); | |
| } | |
| // Select a rocket part | |
| function selectPart(part, partType) { | |
| // Deselect all parts of this type | |
| document.querySelectorAll(`[data-part-type="${partType}"]`).forEach(el => { | |
| el.classList.remove('selected-part'); | |
| }); | |
| // Select the clicked part | |
| event.currentTarget.classList.add('selected-part'); | |
| // Update current rocket | |
| gameState.currentRocket[partType] = part; | |
| // Update rocket display | |
| updateRocketDisplay(); | |
| updateUI(); | |
| } | |
| // Select a destination | |
| function selectDestination(destination) { | |
| // Deselect all destinations | |
| document.querySelectorAll('.planet-card').forEach(el => { | |
| el.classList.remove('border-2', 'border-yellow-400'); | |
| }); | |
| // Select the clicked destination | |
| event.currentTarget.classList.add('border-2', 'border-yellow-400'); | |
| // Update current destination | |
| gameState.currentDestination = destination; | |
| updateUI(); | |
| } | |
| // Update payload information | |
| function updatePayload() { | |
| gameState.currentPayload.type = document.getElementById('payload-type').value; | |
| gameState.currentPayload.mass = parseInt(document.getElementById('payload-mass').value); | |
| updateUI(); | |
| } | |
| // Update rocket display | |
| function updateRocketDisplay() { | |
| const rocketModel = document.getElementById('rocket-model'); | |
| rocketModel.innerHTML = '<div class="flame" id="rocket-flame"></div><div class="launch-smoke"></div>'; | |
| // Add parts to the rocket model | |
| if (gameState.currentRocket.firstStage) { | |
| const firstStage = document.createElement('div'); | |
| firstStage.className = `first-stage w-16 mx-auto`; | |
| firstStage.style.height = `${gameState.currentRocket.firstStage.height * 10}px`; | |
| firstStage.style.backgroundImage = `url(${gameState.currentRocket.firstStage.image})`; | |
| firstStage.style.backgroundSize = 'contain'; | |
| firstStage.style.backgroundRepeat = 'no-repeat'; | |
| firstStage.style.backgroundPosition = 'center'; | |
| rocketModel.appendChild(firstStage); | |
| } | |
| if (gameState.currentRocket.secondStage) { | |
| const secondStage = document.createElement('div'); | |
| secondStage.className = `second-stage w-12 mx-auto`; | |
| secondStage.style.height = `${gameState.currentRocket.secondStage.height * 10}px`; | |
| secondStage.style.backgroundImage = `url(${gameState.currentRocket.secondStage.image})`; | |
| secondStage.style.backgroundSize = 'contain'; | |
| secondStage.style.backgroundRepeat = 'no-repeat'; | |
| secondStage.style.backgroundPosition = 'center'; | |
| rocketModel.appendChild(secondStage); | |
| } | |
| if (gameState.currentRocket.fairing && gameState.currentRocket.fairing.id !== 'none') { | |
| const fairing = document.createElement('div'); | |
| fairing.className = `fairing w-16 mx-auto rounded-t-lg`; | |
| fairing.style.height = `${gameState.currentRocket.fairing.height * 10}px`; | |
| fairing.style.backgroundImage = `url(${gameState.currentRocket.fairing.image})`; | |
| fairing.style.backgroundSize = 'contain'; | |
| fairing.style.backgroundRepeat = 'no-repeat'; | |
| fairing.style.backgroundPosition = 'center'; | |
| rocketModel.appendChild(fairing); | |
| } | |
| } | |
| // Update UI elements | |
| function updateUI() { | |
| // Update funds and reputation | |
| document.getElementById('funds').textContent = `$${gameState.funds.toLocaleString()}`; | |
| document.getElementById('reputation').textContent = gameState.reputation; | |
| // Calculate rocket stats | |
| let cost = 0; | |
| let payloadCapacity = 0; | |
| let deltaV = 0; | |
| if (gameState.currentRocket.firstStage) { | |
| cost += gameState.currentRocket.firstStage.cost; | |
| payloadCapacity += gameState.currentRocket.firstStage.thrust * 10; | |
| deltaV += gameState.currentRocket.firstStage.thrust * 5; | |
| } | |
| if (gameState.currentRocket.secondStage) { | |
| cost += gameState.currentRocket.secondStage.cost; | |
| payloadCapacity += gameState.currentRocket.secondStage.thrust * 5; | |
| deltaV += gameState.currentRocket.secondStage.thrust * 3; | |
| } | |
| if (gameState.currentRocket.fairing) { | |
| cost += gameState.currentRocket.fairing.cost; | |
| } | |
| if (gameState.currentRocket.engines) { | |
| cost += gameState.currentRocket.engines.cost; | |
| deltaV += gameState.currentRocket.engines.isp * 100; | |
| } | |
| // Update stats display | |
| document.getElementById('delta-v').textContent = `${deltaV.toLocaleString()} m/s`; | |
| document.getElementById('payload').textContent = `${payloadCapacity.toLocaleString()} kg`; | |
| document.getElementById('rocket-cost').textContent = `$${cost.toLocaleString()}`; | |
| // Update launch button state | |
| const launchButton = document.getElementById('launch-button'); | |
| if (isReadyForLaunch()) { | |
| launchButton.disabled = false; | |
| launchButton.classList.remove('bg-gray-600', 'cursor-not-allowed'); | |
| launchButton.classList.add('bg-green-600', 'hover:bg-green-700'); | |
| } else { | |
| launchButton.disabled = true; | |
| launchButton.classList.remove('bg-green-600', 'hover:bg-green-700'); | |
| launchButton.classList.add('bg-gray-600', 'cursor-not-allowed'); | |
| } | |
| } | |
| // Check if rocket is ready for launch | |
| function isReadyForLaunch() { | |
| return gameState.currentRocket.firstStage && | |
| gameState.currentRocket.secondStage && | |
| gameState.currentRocket.engines && | |
| gameState.currentDestination && | |
| gameState.currentPayload.mass > 0; | |
| } | |
| // Launch the rocket | |
| function launchRocket() { | |
| if (!isReadyForLaunch()) return; | |
| const rocketModel = document.getElementById('rocket-model'); | |
| const rocketDisplay = document.getElementById('rocket-display'); | |
| const spaceView = document.getElementById('space-view'); | |
| const planetArrival = document.getElementById('planet-arrival'); | |
| const rocketInSpace = document.getElementById('rocket-in-space'); | |
| const arrivalMessage = document.getElementById('arrival-message'); | |
| const countdownElement = document.getElementById('countdown'); | |
| const stageSeparation = document.getElementById('stage-separation'); | |
| const successMarker = document.getElementById('success-marker'); | |
| const explosion = document.getElementById('explosion'); | |
| const launchButton = document.getElementById('launch-button'); | |
| const missionName = document.getElementById('mission-name').value || `Mission ${Math.floor(Math.random() * 1000)}`; | |
| // Reset space view elements | |
| spaceView.classList.remove('active'); | |
| planetArrival.classList.remove('active'); | |
| rocketInSpace.classList.remove('active'); | |
| arrivalMessage.classList.remove('active'); | |
| planetArrival.style.backgroundImage = ''; | |
| rocketInSpace.style.backgroundImage = ''; | |
| // Disable launch button during launch | |
| launchButton.disabled = true; | |
| launchButton.classList.remove('bg-green-600', 'hover:bg-green-700'); | |
| launchButton.classList.add('bg-gray-600', 'cursor-not-allowed'); | |
| // Clear previous animations | |
| rocketModel.classList.remove('launching', 'launch-shake', 'rocket-ascent'); | |
| explosion.classList.remove('explode'); | |
| successMarker.classList.remove('success-animate'); | |
| // Reset rocket position | |
| rocketModel.style.bottom = '0'; | |
| rocketModel.style.transform = 'translateX(-50%)'; | |
| rocketModel.style.opacity = '1'; | |
| // Play launch sound | |
| playSound('launch'); | |
| // Calculate mission success chance (simplified) | |
| const rocketPower = (gameState.currentRocket.firstStage.thrust + | |
| gameState.currentRocket.secondStage.thrust) / 1000; | |
| const difficulty = gameState.currentDestination.difficulty; | |
| const successChance = Math.min(0.9, 0.5 + (rocketPower - difficulty) * 0.1); | |
| // Calculate mission cost | |
| const missionCost = calculateMissionCost(); | |
| // Update mission control console | |
| updateConsole(`> Starting ${missionName} launch sequence...`); | |
| // Countdown sequence | |
| let count = 5; | |
| const countdownInterval = setInterval(() => { | |
| countdownElement.textContent = count; | |
| countdownElement.classList.add('countdown-animate'); | |
| // Play countdown sound | |
| playSound('countdown'); | |
| setTimeout(() => { | |
| countdownElement.classList.remove('countdown-animate'); | |
| }, 900); | |
| if (count === 0) { | |
| clearInterval(countdownInterval); | |
| countdownElement.textContent = 'LIFTOFF!'; | |
| countdownElement.classList.add('countdown-animate'); | |
| setTimeout(() => { | |
| countdownElement.classList.remove('countdown-animate'); | |
| startLaunchSequence(); | |
| }, 1000); | |
| } | |
| count--; | |
| }, 1000); | |
| function startLaunchSequence() { | |
| // Add launching effects | |
| rocketModel.classList.add('launching', 'launch-shake'); | |
| // Update console | |
| updateConsole("> Engine ignition..."); | |
| updateConsole("> All systems nominal"); | |
| setTimeout(() => { | |
| updateConsole("> Liftoff! We have liftoff!"); | |
| // Start ascent animation | |
| rocketModel.classList.add('rocket-ascent'); | |
| rocketModel.classList.remove('launch-shake'); | |
| // Show space view after rocket leaves the screen | |
| setTimeout(() => { | |
| spaceView.classList.add('active'); | |
| // Show rocket in space | |
| rocketInSpace.style.backgroundImage = `url(${gameState.currentRocket.firstStage.image})`; | |
| rocketInSpace.style.left = '50%'; | |
| rocketInSpace.style.top = '50%'; | |
| rocketInSpace.style.transform = 'translate(-50%, -50%)'; | |
| rocketInSpace.classList.add('active'); | |
| // Random mission outcome | |
| const isSuccess = Math.random() < successChance; | |
| if (isSuccess) { | |
| // Successful mission | |
| setTimeout(() => { | |
| updateConsole("> First stage separation successful"); | |
| // Show stage separation effect | |
| stageSeparation.classList.add('separate'); | |
| playSound('separation'); | |
| setTimeout(() => { | |
| stageSeparation.classList.remove('separate'); | |
| updateConsole("> Second stage ignition"); | |
| setTimeout(() => { | |
| updateConsole("> Payload deployment confirmed"); | |
| // Show destination planet | |
| planetArrival.style.backgroundImage = `url(${gameState.currentDestination.arrivalImage})`; | |
| planetArrival.classList.add('active'); | |
| // Show arrival message | |
| arrivalMessage.innerHTML = ` | |
| <h3 class="orbitron text-xl text-green-400 mb-2">MISSION SUCCESS</h3> | |
| <p>${missionName} has successfully reached ${gameState.currentDestination.name}!</p> | |
| <p class="mt-2 text-sm">Payload delivered: ${gameState.currentPayload.type}</p> | |
| `; | |
| arrivalMessage.classList.add('active'); | |
| setTimeout(() => { | |
| updateConsole(`> Mission to ${gameState.currentDestination.name} successful!`); | |
| // Show success marker | |
| successMarker.classList.add('success-animate'); | |
| playSound('success'); | |
| // Calculate rewards | |
| const reputationGain = gameState.currentDestination.difficulty * 10; | |
| const financialReward = gameState.currentDestination.difficulty * 500000; | |
| setTimeout(() => { | |
| updateConsole(`> Reputation increased by ${reputationGain}`); | |
| updateConsole(`> Financial reward: $${financialReward.toLocaleString()}`); | |
| updateConsole("> Ready for next mission"); | |
| // Update game state | |
| gameState.reputation += reputationGain; | |
| gameState.funds += financialReward - missionCost; | |
| // Reset for next launch | |
| resetAfterLaunch(); | |
| }, 2000); | |
| }, 2000); | |
| }, 2000); | |
| }, 1000); | |
| }, 3000); | |
| } else { | |
| // Failed mission | |
| const failureTime = 2000 + Math.random() * 3000; | |
| const failureReason = getRandomFailureReason(); | |
| setTimeout(() => { | |
| // Show explosion | |
| explosion.style.left = rocketInSpace.style.left; | |
| explosion.style.top = rocketInSpace.style.top; | |
| explosion.classList.add('explode'); | |
| playSound('explosion'); | |
| // Hide rocket | |
| rocketInSpace.style.opacity = '0'; | |
| updateConsole(`> WARNING: ${failureReason}`); | |
| updateConsole("> Attempting to mitigate..."); | |
| setTimeout(() => { | |
| updateConsole("> Mission failure confirmed"); | |
| updateConsole("> Investigating root cause"); | |
| // Calculate penalties | |
| const reputationLoss = gameState.currentDestination.difficulty * 5; | |
| const financialLoss = missionCost * 0.5; | |
| setTimeout(() => { | |
| updateConsole(`> Reputation decreased by ${reputationLoss}`); | |
| updateConsole(`> Financial loss: $${financialLoss.toLocaleString()}`); | |
| updateConsole("> Preparing for next attempt"); | |
| // Update game state | |
| gameState.reputation = Math.max(0, gameState.reputation - reputationLoss); | |
| gameState.funds -= financialLoss; | |
| // Reset for next launch | |
| resetAfterLaunch(); | |
| }, 2000); | |
| }, 2000); | |
| }, failureTime); | |
| } | |
| }, 3000); | |
| }, 1000); | |
| } | |
| } | |
| // Play sound effects | |
| function playSound(type) { | |
| // In a real implementation, you would use the Web Audio API or preloaded audio elements | |
| // This is a placeholder that would be replaced with actual sound effects | |
| console.log(`Playing sound: ${type}`); | |
| // For demonstration purposes, we'll just log the sound that would play | |
| const sounds = { | |
| 'countdown': 'beep.mp3', | |
| 'launch': 'rocket_launch.mp3', | |
| 'separation': 'stage_separation.mp3', | |
| 'success': 'mission_success.mp3', | |
| 'explosion': 'explosion.mp3' | |
| }; | |
| // In a real app, you would play the corresponding sound file | |
| // new Audio(sounds[type]).play(); | |
| } | |
| // Calculate mission cost | |
| function calculateMissionCost() { | |
| let cost = 0; | |
| if (gameState.currentRocket.firstStage) cost += gameState.currentRocket.firstStage.cost; | |
| if (gameState.currentRocket.secondStage) cost += gameState.currentRocket.secondStage.cost; | |
| if (gameState.currentRocket.fairing) cost += gameState.currentRocket.fairing.cost; | |
| if (gameState.currentRocket.engines) cost += gameState.currentRocket.engines.cost; | |
| // Add payload cost | |
| cost += gameState.currentPayload.mass * 1000; | |
| return cost; | |
| } | |
| // Get random failure reason | |
| function getRandomFailureReason() { | |
| const reasons = [ | |
| "First stage engine failure", | |
| "Guidance system malfunction", | |
| "Structural integrity compromised", | |
| "Fuel leak detected", | |
| "Second stage separation failure", | |
| "Communication loss", | |
| "Payload fairing jettison failure", | |
| "Trajectory deviation" | |
| ]; | |
| return reasons[Math.floor(Math.random() * reasons.length)]; | |
| } | |
| // Update mission control console | |
| function updateConsole(message) { | |
| const consoleElement = document.getElementById('mission-console'); | |
| const blinkElement = consoleElement.querySelector('.blink'); | |
| if (blinkElement) { | |
| blinkElement.remove(); | |
| } | |
| const newMessage = document.createElement('p'); | |
| newMessage.className = 'console-text'; | |
| newMessage.textContent = message; | |
| consoleElement.appendChild(newMessage); | |
| consoleElement.scrollTop = consoleElement.scrollHeight; | |
| // Add new blinking cursor | |
| const newBlink = document.createElement('p'); | |
| newBlink.className = 'console-text blink'; | |
| newBlink.textContent = '> _'; | |
| consoleElement.appendChild(newBlink); | |
| consoleElement.scrollTop = consoleElement.scrollHeight; | |
| } | |
| // Reset after launch | |
| function resetAfterLaunch() { | |
| const rocketModel = document.getElementById('rocket-model'); | |
| const spaceView = document.getElementById('space-view'); | |
| const planetArrival = document.getElementById('planet-arrival'); | |
| const rocketInSpace = document.getElementById('rocket-in-space'); | |
| const arrivalMessage = document.getElementById('arrival-message'); | |
| const launchButton = document.getElementById('launch-button'); | |
| const explosion = document.getElementById('explosion'); | |
| const successMarker = document.getElementById('success-marker'); | |
| const stageSeparation = document.getElementById('stage-separation'); | |
| const countdownElement = document.getElementById('countdown'); | |
| // Remove all animation classes | |
| rocketModel.classList.remove('launching', 'launch-shake', 'rocket-ascent'); | |
| explosion.classList.remove('explode'); | |
| successMarker.classList.remove('success-animate'); | |
| stageSeparation.classList.remove('separate'); | |
| countdownElement.classList.remove('countdown-animate'); | |
| // Reset space view elements | |
| spaceView.classList.remove('active'); | |
| planetArrival.classList.remove('active'); | |
| rocketInSpace.classList.remove('active'); | |
| arrivalMessage.classList.remove('active'); | |
| // Reset rocket position and visibility | |
| rocketModel.style.bottom = '0'; | |
| rocketModel.style.transform = 'translateX(-50%)'; | |
| rocketModel.style.opacity = '1'; | |
| // Clear countdown text | |
| countdownElement.textContent = ''; | |
| // Re-enable launch button | |
| launchButton.disabled = false; | |
| launchButton.classList.remove('bg-gray-600', 'cursor-not-allowed'); | |
| launchButton.classList.add('bg-green-600', 'hover:bg-green-700'); | |
| // Update UI | |
| updateUI(); | |
| } | |
| // Initialize the game when DOM is loaded | |
| document.addEventListener('DOMContentLoaded', initGame); | |
| </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=jamesbright/spacex-simulator" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |