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> |