spacex-simulator / index.html
jamesbright's picture
Add 1 files
5db2c16 verified
<!DOCTYPE html>
<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>