<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Nebula Drift - Planetary Explorer</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"> <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&family=Roboto:wght@300;400;500&display=swap" rel="stylesheet"> <style> :root { --neon-blue: #00f7ff; --neon-purple: #b300ff; --deep-space: #0a0a1a; --star-dust: #e0e0e0; --planet-1: #ff6b6b; --planet-2: #48dbfb; --planet-3: #1dd1a1; --planet-4: #feca57; --planet-5: #5f27cd; } body { font-family: 'Roboto', sans-serif; background-color: var(--deep-space); color: var(--star-dust); overflow: hidden; margin: 0; padding: 0; height: 100vh; } .sci-fi-font { font-family: 'Orbitron', sans-serif; } .game-container { position: relative; width: 100%; height: 100vh; background-image: url('https://images.unsplash.com/photo-1462331940025-496dfbfc7564?q=80&w=800&auto=format&fit=crop'); background-size: cover; background-position: center; } .character-card { transition: all 0.3s ease; background: rgba(10, 10, 26, 0.7); border: 1px solid var(--neon-blue); box-shadow: 0 0 10px var(--neon-blue); } .character-card:hover { transform: translateY(-10px); box-shadow: 0 0 20px var(--neon-purple); } .node { width: 20px; height: 20px; background: var(--neon-blue); border-radius: 50%; position: absolute; box-shadow: 0 0 10px var(--neon-blue); cursor: pointer; } .node-visited { background: var(--neon-purple); } .node-current { width: 25px; height: 25px; box-shadow: 0 0 15px var(--neon-purple); } .node-connection { position: absolute; height: 2px; background: rgba(0, 247, 255, 0.5); transform-origin: left center; } .dialogue-box { background: rgba(10, 10, 26, 0.9); border: 1px solid var(--neon-purple); box-shadow: 0 0 15px var(--neon-purple); } .health-bar { height: 6px; background: linear-gradient(90deg, #ff0000, #ff4500); } .energy-bar { height: 6px; background: linear-gradient(90deg, #00bfff, #00f7ff); } .neon-text { text-shadow: 0 0 5px var(--neon-blue), 0 0 10px var(--neon-blue); } .neon-border { box-shadow: 0 0 10px var(--neon-blue), inset 0 0 10px var(--neon-blue); } .map-overlay { background: rgba(10, 10, 26, 0.95); border: 1px solid var(--neon-purple); } .slide-in { animation: slideIn 0.3s forwards; } .slide-out { animation: slideOut 0.3s forwards; } @keyframes slideIn { from { transform: translateY(100%); } to { transform: translateY(0); } } @keyframes slideOut { from { transform: translateY(0); } to { transform: translateY(100%); } } .blend-in { opacity: 0; transition: opacity 0.3s ease; } .blend-in:hover { opacity: 1; } .avatar-status { background: rgba(10, 10, 26, 0.7); border-top: 1px solid var(--neon-blue); } .planet { position: absolute; border-radius: 50%; cursor: pointer; transition: all 0.5s ease; display: flex; flex-direction: column; align-items: center; } .planet:hover { transform: scale(1.1); } .planet-1 { background: radial-gradient(circle at 30% 30%, var(--planet-1), #cc0000); box-shadow: 0 0 20px var(--planet-1); } .planet-2 { background: radial-gradient(circle at 30% 30%, var(--planet-2), #0099cc); box-shadow: 0 0 20px var(--planet-2); } .planet-3 { background: radial-gradient(circle at 30% 30%, var(--planet-3), #00997a); box-shadow: 0 0 20px var(--planet-3); } .planet-4 { background: radial-gradient(circle at 30% 30%, var(--planet-4), #cc9900); box-shadow: 0 0 20px var(--planet-4); } .planet-5 { background: radial-gradient(circle at 30% 30%, var(--planet-5), #4a0099); box-shadow: 0 0 20px var(--planet-5); } .planet-label { margin-top: 5px; font-size: 0.8rem; background: rgba(0, 0, 0, 0.7); padding: 2px 6px; border-radius: 10px; white-space: nowrap; } .inhabitants-panel { position: absolute; bottom: 80px; left: 50%; transform: translateX(-50%); width: 80%; max-height: 200px; background: rgba(10, 10, 26, 0.95); border: 1px solid var(--neon-purple); border-radius: 8px; padding: 15px; display: none; flex-wrap: wrap; justify-content: center; gap: 10px; overflow-y: auto; box-shadow: 0 0 20px rgba(179, 0, 255, 0.5); } .inhabitant { width: 60px; text-align: center; cursor: pointer; } .inhabitant img { width: 50px; height: 50px; border-radius: 50%; object-fit: cover; border: 2px solid var(--neon-blue); margin-bottom: 5px; } .inhabitant-name { font-size: 0.7rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .planet-details { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 70%; max-width: 500px; background: rgba(10, 10, 26, 0.95); border: 1px solid var(--neon-purple); border-radius: 8px; padding: 20px; display: none; z-index: 100; box-shadow: 0 0 30px rgba(179, 0, 255, 0.7); } .close-panel { position: absolute; top: 10px; right: 10px; color: var(--neon-blue); cursor: pointer; } #planetInteraction { z-index: 1000; } .planet-detail-view { box-shadow: 0 0 30px currentColor; } .planet-action { transition: all 0.3s ease; } .planet-action:hover { transform: translateY(-5px); box-shadow: 0 5px 15px rgba(0, 247, 255, 0.3); } .planet-ring { position: absolute; border-radius: 50%; border: 2px solid rgba(255, 255, 255, 0.5); transform-origin: center; } /* Action Log Styles */ .action-log { position: absolute; bottom: 20px; right: 20px; width: 300px; min-height: 20px; max-height: 200px; background: rgba(10, 10, 26, 0.9); border: 1px solid var(--neon-blue); border-radius: 8px; padding: 10px; overflow-y: auto; z-index: 50; cursor: move; box-shadow: 0 0 15px var(--neon-blue); transition: all 0.3s ease; } .action-log.minimized { height: 20px; overflow: hidden; } .action-log.minimized .action-log-content { display: none; } .action-log-header { font-family: 'Orbitron', sans-serif; color: var(--neon-blue); padding: 0; margin: 0; border-bottom: 1px solid var(--neon-blue); display: flex; justify-content: space-between; align-items: center; line-height: 1; cursor: move; user-select: none; } .action-log-content { font-size: 12px; line-height: 1.4; } .log-entry { margin-bottom: 5px; padding-bottom: 5px; border-bottom: 1px solid rgba(0, 247, 255, 0.2); } .log-time { color: var(--neon-purple); font-size: 10px; margin-right: 5px; } .log-message { color: var(--star-dust); } .action-log-toggle { cursor: pointer; color: var(--neon-blue); } .pulse { animation: pulse 2s infinite; } /* Event animations */ .fadeIn { animation: fadeIn 0.5s ease-in; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } .slideInRight { animation: slideInRight 0.5s ease-out; } @keyframes slideInRight { from { transform: translateX(100%); } to { transform: translateX(0); } } .event-choice:hover { transform: scale(1.05); box-shadow: 0 0 15px var(--neon-purple); } @keyframes pulse { 0% { transform: scale(1); opacity: 0.7; } 50% { transform: scale(1.05); opacity: 1; } 100% { transform: scale(1); opacity: 0.7; } } </style> </head> <body> <div class="game-container" id="gameContainer"> <!-- Game Scene (Planetary View) --> <div class="absolute inset-0 flex items-center justify-center"> <div class="w-full h-full bg-black bg-opacity-30 flex flex-col"> <!-- Space Background with Planets --> <div class="flex-1 relative overflow-hidden" id="spaceView"> <!-- Planets will be dynamically added here --> <div id="planetsContainer"></div> <!-- Inhabitants Panel --> <div class="inhabitants-panel" id="inhabitantsPanel"> <div class="close-panel" id="closeInhabitants"><i class="fas fa-times"></i></div> <h3 class="w-full text-center sci-fi-font text-neon-blue mb-3">Planetary Inhabitants</h3> <!-- Inhabitants will be dynamically added here --> <div id="inhabitantsList"></div> </div> <!-- Planet Details Panel --> <div class="planet-details" id="planetDetails"> <div class="close-panel" id="closePlanetDetails"><i class="fas fa-times"></i></div> <div class="flex items-start"> <div class="w-20 h-20 rounded-full mr-4" id="planetDetailImage"></div> <div> <h2 class="sci-fi-font text-xl text-neon-purple mb-2" id="planetDetailName"></h2> <div class="text-sm mb-3" id="planetDetailDescription"></div> <div class="flex space-x-4 text-xs"> <div><span class="text-neon-blue">Population:</span> <span id="planetDetailPopulation"></span></div> <div><span class="text-neon-blue">Tech Level:</span> <span id="planetDetailTech"></span></div> <div><span class="text-neon-blue">Status:</span> <span id="planetDetailStatus"></span></div> </div> </div> </div> <div class="mt-4 pt-4 border-t border-neon-blue"> <h4 class="sci-fi-font text-neon-blue mb-2">Notable Locations</h4> <div class="flex flex-wrap gap-2" id="planetLocations"></div> </div> <div class="mt-4 pt-4 border-t border-neon-blue"> <h4 class="sci-fi-font text-neon-blue mb-2">Planetary Resources</h4> <div class="flex flex-wrap gap-2" id="planetResources"></div> </div> </div> </div> <!-- Player Status Bar --> <div class="avatar-status h-16 flex items-center px-4"> <div class="flex items-center mr-6"> <div class="w-10 h-10 rounded-full bg-gray-700 overflow-hidden mr-3"> <img src="https://api.dicebear.com/7.x/bottts-neutral/svg?seed=CptAria" alt="Player Avatar" class="w-full h-full object-cover"> </div> <div> <h4 class="sci-fi-font text-neon-blue mb-1">Captain Aria</h4> <div class="flex items-center text-xs space-x-4"> <div> <span>HP</span> <div class="health-bar w-24 rounded-full mt-1"></div> </div> <div> <span>EN</span> <div class="energy-bar w-24 rounded-full mt-1"></div> </div> </div> </div> </div> <div class="ml-auto flex space-x-2"> <button class="w-8 h-8 rounded-full bg-transparent border border-neon-blue text-neon-blue flex items-center justify-center hover:bg-neon-blue hover:text-black transition" id="logToggle"> <i class="fas fa-scroll"></i> </button> <button class="w-8 h-8 rounded-full bg-transparent border border-neon-blue text-neon-blue flex items-center justify-center hover:bg-neon-blue hover:text-black transition" id="mapToggle"> <i class="fas fa-map"></i> </button> <button class="w-8 h-8 rounded-full bg-transparent border border-neon-blue text-neon-blue flex items-center justify-center hover:bg-neon-blue hover:text-black transition" id="menuToggle"> <i class="fas fa-cog"></i> </button> </div> </div> </div> </div> <!-- Player Party Cards --> <div class="absolute bottom-0 left-0 right-0 flex justify-center space-x-4 p-4" id="partyCards"> <!-- Cards will be dynamically added here --> </div> <!-- Map Overlay --> <div class="absolute inset-0 map-overlay hidden flex-col p-8" id="mapOverlay"> <div class="flex justify-between items-center mb-6"> <h2 class="sci-fi-font text-2xl text-neon-purple">Stellar Navigation Map</h2> <button class="text-neon-blue hover:text-neon-purple" id="closeMap"> <i class="fas fa-times text-2xl"></i> </button> </div> <div class="flex-1 relative" id="nodeMap"> <!-- Nodes and connections will be dynamically added here --> </div> <div class="mt-4 text-center"> <p class="text-sm text-neon-blue">Select a node to travel to that location</p> </div> </div> <!-- Character Detail Modal --> <div class="absolute inset-0 bg-black bg-opacity-90 hidden flex-col items-center justify-center z-50" id="characterModal"> <div class="relative w-full max-w-3xl h-3/4 bg-deep-space border-2 border-neon-purple rounded-lg p-6 flex"> <button class="absolute top-4 right-4 text-neon-blue hover:text-neon-purple" id="closeCharacterModal"> <i class="fas fa-times text-2xl"></i> </button> <!-- Character Visual --> <div class="w-1/3 flex flex-col items-center"> <div class="w-64 h-96 bg-gray-900 rounded-lg overflow-hidden mb-4 relative" id="characterFullBody"> <img src="" alt="Character Full Body" class="w-full h-full object-cover" id="characterFullBodyImg"> <!-- Wearable overlays would be added here dynamically --> </div> <div class="text-center"> <h3 class="sci-fi-font text-xl text-neon-blue mb-1" id="characterModalName"></h3> <p class="text-sm text-neon-purple" id="characterModalRole"></p> </div> </div> <!-- Character Details --> <div class="w-2/3 pl-6"> <div class="grid grid-cols-2 gap-4 mb-6"> <div> <h4 class="sci-fi-font text-neon-blue mb-2">Attributes</h4> <div class="space-y-2" id="characterAttributes"> <!-- Attributes will be added here --> </div> </div> <div> <h4 class="sci-fi-font text-neon-blue mb-2">Equipment</h4> <div class="space-y-2" id="characterEquipment"> <!-- Equipment will be added here --> </div> </div> </div> <div class="mb-6"> <h4 class="sci-fi-font text-neon-blue mb-2">Bio</h4> <p class="text-sm" id="characterBio"></p> </div> <div> <h4 class="sci-fi-font text-neon-blue mb-2">Skills</h4> <div class="flex flex-wrap gap-2" id="characterSkills"> <!-- Skills will be added here --> </div> </div> </div> </div> </div> <!-- Quick Menu --> <div class="absolute top-4 right-4 bg-black bg-opacity-70 rounded-lg p-2 hidden" id="quickMenu"> <button class="block w-full text-left px-4 py-2 hover:bg-neon-blue hover:text-black rounded">Inventory</button> <button class="block w-full text-left px-4 py-2 hover:bg-neon-blue hover:text-black rounded">Skills</button> <button class="block w-full text-left px-4 py-2 hover:bg-neon-blue hover:text-black rounded">Journal</button> <button class="block w-full text-left px-4 py-2 hover:bg-neon-blue hover:text-black rounded" id="settingsBtn">Settings</button> <button class="block w-full text-left px-4 py-2 hover:bg-neon-blue hover:text-black rounded">Save Game</button> </div> <!-- Event Scene Container --> <div class="absolute inset-0 bg-black bg-opacity-90 hidden flex-col items-center justify-center z-50" id="eventScene"> <div class="w-full max-w-4xl bg-deep-space border-2 border-neon-purple rounded-lg p-8"> <div class="flex"> <!-- Event Image/Animation Area --> <div class="w-1/3 h-64 bg-gray-900 rounded-lg overflow-hidden mr-6" id="eventImage"> <img src="" alt="Event Image" class="w-full h-full object-cover"> </div> <!-- Event Text Content --> <div class="w-2/3"> <h2 class="sci-fi-font text-2xl text-neon-purple mb-4" id="eventTitle"></h2> <p class="text-neon-blue mb-6" id="eventDescription"></p> <!-- Event Choices --> <div class="flex flex-col space-y-3" id="eventChoices"></div> </div> </div> <!-- Admin Controls (visible only in editor mode) --> <div class="mt-6 pt-6 border-t border-neon-blue hidden" id="eventAdminControls"> <h3 class="sci-fi-font text-neon-blue mb-3">Event Editor</h3> <div class="grid grid-cols-2 gap-4"> <div> <label class="block text-sm mb-1">Event ID</label> <input type="text" class="w-full bg-gray-800 border border-neon-blue rounded px-3 py-2"> </div> <div> <label class="block text-sm mb-1">Image URL</label> <input type="text" class="w-full bg-gray-800 border border-neon-blue rounded px-3 py-2"> </div> <div class="col-span-2"> <label class="block text-sm mb-1">Description</label> <textarea class="w-full bg-gray-800 border border-neon-blue rounded px-3 py-2 h-20"></textarea> </div> </div> <div class="flex justify-end space-x-3 mt-4"> <button class="px-4 py-2 bg-neon-blue text-black rounded">Save</button> <button class="px-4 py-2 bg-gray-700 rounded">Cancel</button> </div> </div> </div> </div> <!-- Settings Panel --> <div class="absolute inset-0 bg-black bg-opacity-90 hidden flex-col p-8" id="settingsPanel"> <div class="flex justify-between items-center mb-6"> <h2 class="sci-fi-font text-2xl text-neon-purple">Game Settings</h2> <button class="text-neon-blue hover:text-neon-purple" id="closeSettings"> <i class="fas fa-times text-2xl"></i> </button> </div> <div class="flex-1 overflow-y-auto"> <div class="max-w-2xl mx-auto"> <div class="mb-8"> <h3 class="sci-fi-font text-xl text-neon-blue mb-4">Audio</h3> <div class="space-y-4"> <div> <label class="flex justify-between items-center"> <span>Master Volume</span> <span id="masterVolumeValue">50%</span> </label> <input type="range" min="0" max="100" value="50" class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer" id="masterVolume"> </div> <div> <label class="flex justify-between items-center"> <span>Music Volume</span> <span id="musicVolumeValue">70%</span> </label> <input type="range" min="0" max="100" value="70" class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer" id="musicVolume"> </div> <div> <label class="flex justify-between items-center"> <span>SFX Volume</span> <span id="sfxVolumeValue">80%</span> </label> <input type="range" min="0" max="100" value="80" class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer" id="sfxVolume"> </div> </div> </div> <div class="mb-8"> <h3 class="sci-fi-font text-xl text-neon-blue mb-4">Graphics</h3> <div class="space-y-4"> <div> <label class="flex items-center space-x-3"> <input type="checkbox" class="form-checkbox h-5 w-5 text-neon-blue" checked id="enableShadows"> <span>Enable Shadows</span> </label> </div> <div> <label class="flex items-center space-x-3"> <input type="checkbox" class="form-checkbox h-5 w-5 text-neon-blue" checked id="enableParticles"> <span>Enable Particles</span> </label> </div> <div> <label class="flex items-center space-x-3"> <input type="checkbox" class="form-checkbox h-5 w-5 text-neon-blue" id="enableBloom"> <span>Enable Bloom Effects</span> </label> </div> <div> <label class="flex justify-between items-center"> <span>Resolution</span> <select class="bg-gray-800 border border-neon-blue text-white rounded px-3 py-1" id="resolution"> <option>1920x1080</option> <option>1600x900</option> <option>1366x768</option> <option selected>1280x720</option> </select> </label> </div> </div> </div> <div class="mb-8"> <h3 class="sci-fi-font text-xl text-neon-blue mb-4">Gameplay</h3> <div class="space-y-4"> <div> <label class="flex items-center space-x-3"> <input type="checkbox" class="form-checkbox h-5 w-5 text-neon-blue" checked id="showTutorials"> <span>Show Tutorials</span> </label> </div> <div> <label class="flex items-center space-x-3"> <input type="checkbox" class="form-checkbox h-5 w-5 text-neon-blue" checked id="showTooltips"> <span>Show Tooltips</span> </label> </div> <div> <label class="flex items-center space-x-3"> <input type="checkbox" class="form-checkbox h-5 w-5 text-neon-blue" id="autoSave"> <span>Enable Auto-Save</span> </label> </div> <div> <label class="flex justify-between items-center"> <span>Text Speed</span> <select class="bg-gray-800 border border-neon-blue text-white rounded px-3 py-1" id="textSpeed"> <option>Slow</option> <option selected>Normal</option> <option>Fast</option> <option>Instant</option> </select> </label> </div> </div> </div> <div class="flex justify-center space-x-4"> <button class="px-6 py-2 bg-neon-blue bg-opacity-20 border border-neon-blue rounded hover:bg-opacity-40" id="saveSettings"> Save Settings </button> <button class="px-6 py-2 bg-gray-800 border border-gray-600 rounded hover:bg-gray-700" id="resetSettings"> Reset Defaults </button> </div> </div> </div> </div> </div> <!-- Action Log --> <div class="action-log" id="actionLog"> <div class="action-log-header"> <span>ACTION LOG</span> <span class="action-log-toggle" id="toggleLog">_</span> </div> <div class="action-log-content" id="logContent"> <!-- Log entries will be added here --> </div> </div> <!-- Planet Interaction Interface --> <div class="absolute inset-0 bg-black bg-opacity-90 hidden flex-col" id="planetInteraction"> <!-- Scene Containers --> <div class="absolute inset-0 hidden" id="exploreScene"> <!-- Explore content will be added here --> </div> <div class="absolute inset-0 hidden" id="settlementScene"> <!-- Settlement content will be added here --> </div> <div class="absolute inset-0 hidden" id="scanScene"> <!-- Scan content will be added here --> </div> <div class="absolute inset-0 hidden" id="landScene"> <!-- Landing content will be added here --> <div class="flex-1 relative p-8"> <button class="absolute top-4 right-4 text-neon-blue hover:text-neon-purple" id="exitPlanet"> <i class="fas fa-arrow-left text-xl mr-2"></i> Return to Orbit </button> <div class="flex h-full"> <!-- Planet View --> <div class="w-1/3 flex flex-col items-center justify-center"> <div class="planet-detail-view w-40 h-40 rounded-full mb-4" id="currentPlanetView"></div> <h2 class="sci-fi-font text-xl text-neon-purple mb-2" id="currentPlanetName"></h2> <div class="text-sm text-center mb-6" id="currentPlanetStatus"></div> <div class="w-full max-w-xs"> <div class="flex justify-between text-xs mb-1"> <span>Fuel Cost:</span> <span id="planetFuelCost">5 units</span> </div> <div class="h-1 bg-gray-800 rounded-full mb-4"> <div class="h-1 bg-neon-blue rounded-full" style="width: 60%"></div> </div> </div> </div> <!-- Interaction Options --> <div class="w-2/3 flex flex-col"> <div class="flex-1 grid grid-cols-2 gap-4 p-4"> <button class="planet-action bg-neon-blue bg-opacity-20 border border-neon-blue rounded-lg p-4 hover:bg-opacity-40 transition" data-action="explore"> <i class="fas fa-binoculars text-2xl mb-2"></i> <h3 class="sci-fi-font text-neon-blue">Explore</h3> <p class="text-xs">Survey the area for resources and points of interest</p> </button> <button class="planet-action bg-neon-blue bg-opacity-20 border border-neon-blue rounded-lg p-4 hover:bg-opacity-40 transition" data-action="settlement"> <i class="fas fa-city text-2xl mb-2"></i> <h3 class="sci-fi-font text-neon-blue">Visit Settlement</h3> <p class="text-xs">Enter the main population center to trade and interact</p> </button> <button class="planet-action bg-neon-blue bg-opacity-20 border border-neon-blue rounded-lg p-4 hover:bg-opacity-40 transition" data-action="scan"> <i class="fas fa-satellite-dish text-2xl mb-2"></i> <h3 class="sci-fi-font text-neon-blue">Scan Planet</h3> <p class="text-xs">Gather scientific data and detect anomalies</p> </button> <button class="planet-action bg-neon-blue bg-opacity-20 border border-neon-blue rounded-lg p-4 hover:bg-opacity-40 transition" data-action="land"> <i class="fas fa-space-shuttle text-2xl mb-2"></i> <h3 class="sci-fi-font text-neon-blue">Land Ship</h3> <p class="text-xs">Establish base camp and deploy exploration team</p> </button> </div> <!-- Crew Status --> <div class="h-16 bg-black bg-opacity-50 border-t border-neon-blue flex items-center px-4"> <div class="flex space-x-4"> <div class="flex items-center"> <div class="w-8 h-8 rounded-full bg-gray-700 overflow-hidden mr-2"> <img src="https://i.pravatar.cc/100" alt="Crew" class="w-full h-full object-cover"> </div> <span class="text-xs">Ready</span> </div> <div class="text-xs"> <div>Fuel: <span class="text-neon-blue fuel-display">85/100</span></div> <div>Supplies: <span class="text-neon-blue supplies-display">72/100</span></div> </div> </div> </div> </div> </div> </div> </div> <script> // Audio elements const audioElements = { backgroundMusic: new Audio(), sfx: new Audio() }; // Game state let currentPlanet = null; let gameTurn = 0; let partyResources = { fuel: 85, supplies: 72, credits: 500 }; let partyStatus = 'exploring'; // exploring, traveling, inCombat, inDialogue // Enhanced event system let worldEvents = []; let activeEventScene = null; const eventScenes = { // Sample event scene 'artifact_discovery': { title: "Ancient Artifact Discovered", description: "Your scanners have detected an unusual energy signature from a nearby ruin.", image: "https://example.com/artifact.jpg", choices: [ { text: "Investigate", action: "investigateArtifact" }, { text: "Leave it", action: "ignoreArtifact" } ], animations: ["fadeIn", "pulse"] } }; let turnTimer = null; function travelToPlanet(planet) { currentPlanet = planet; addLogMessage(`Traveling to ${planet.name}...`); // Hide space view and show planet interaction document.getElementById('gameContainer').style.display = 'none'; document.getElementById('planetInteraction').style.display = 'flex'; // Set planet details document.getElementById('currentPlanetView').className = `planet-detail-view w-40 h-40 rounded-full mb-4 ${planet.type}`; document.getElementById('currentPlanetName').textContent = planet.name; document.getElementById('currentPlanetStatus').textContent = `${planet.status} • ${planet.tech} Tech`; // Add rings if needed const planetView = document.getElementById('currentPlanetView'); if (planet.id === 2 || planet.id === 5) { const ring = document.createElement('div'); ring.className = 'planet-ring'; ring.style.width = '180%'; ring.style.height = '40%'; ring.style.transform = `rotate(${Math.random() * 60}deg)`; planetView.appendChild(ring); } } function exitPlanet() { document.getElementById('planetInteraction').style.display = 'none'; document.getElementById('gameContainer').style.display = 'block'; } function handlePlanetAction(action) { // Hide all scenes first document.querySelectorAll('#planetInteraction > div[id$="Scene"]').forEach(scene => { scene.classList.add('hidden'); }); // Show the selected scene const sceneId = `${action}Scene`; const scene = document.getElementById(sceneId); if (scene) { scene.classList.remove('hidden'); scene.innerHTML = ` <div class="absolute top-4 left-4"> <button class="px-4 py-2 bg-neon-blue bg-opacity-20 border border-neon-blue rounded hover:bg-opacity-40 transition" id="backToPlanet"> <i class="fas fa-arrow-left mr-2"></i> Back to Planet View </button> </div> <div class="absolute inset-0 flex items-center justify-center"> <div class="text-center"> <h2 class="sci-fi-font text-3xl text-neon-purple mb-4">${currentPlanet.name} - ${action.charAt(0).toUpperCase() + action.slice(1)}</h2> <p class="text-neon-blue mb-6">${getSceneDescription(action, currentPlanet)}</p> <div id="${action}Content"></div> </div> </div> `; // Add back button handler document.getElementById('backToPlanet').addEventListener('click', () => { scene.classList.add('hidden'); }); // Load scene-specific content loadSceneContent(action, currentPlanet); } addLogMessage(`Started ${action} action on ${currentPlanet.name}`); consumeResources(getFuelCost(action), getSuppliesCost(action)); processWorldTurn(); } function getSceneDescription(action, planet) { const descriptions = { explore: `Exploring the surface of ${planet.name}. The terrain is ${getTerrainDescription(planet)}.`, settlement: `Entering the main settlement of ${planet.name}. The architecture is ${getArchitectureDescription(planet)}.`, scan: `Scanning ${planet.name} for resources and anomalies. Sensors are detecting ${planet.resources.join(', ')}.`, land: `Establishing base camp on ${planet.name}. The environment is ${getEnvironmentDescription(planet)}.` }; return descriptions[action] || `Performing ${action} on ${planet.name}`; } function loadSceneContent(action, planet) { const contentDiv = document.getElementById(`${action}Content`); if (!contentDiv) return; // Example content for each scene type switch(action) { case 'explore': contentDiv.innerHTML = ` <div class="grid grid-cols-3 gap-4 max-w-2xl mx-auto"> ${[1,2,3].map(i => ` <div class="bg-black bg-opacity-50 border border-neon-blue p-4 rounded-lg cursor-pointer hover:bg-opacity-70 transition"> <div class="w-full h-24 bg-gray-800 mb-2"></div> <h3 class="sci-fi-font text-neon-blue">Location ${i}</h3> <p class="text-xs">Click to explore this area</p> </div> `).join('')} </div> `; break; case 'settlement': contentDiv.innerHTML = ` <div class="flex space-x-4 justify-center"> <button class="px-6 py-3 bg-neon-purple bg-opacity-20 border border-neon-purple rounded-lg hover:bg-opacity-40 transition"> <i class="fas fa-store mr-2"></i> Marketplace </button> <button class="px-6 py-3 bg-neon-purple bg-opacity-20 border border-neon-purple rounded-lg hover:bg-opacity-40 transition"> <i class="fas fa-landmark mr-2"></i> Government </button> <button class="px-6 py-3 bg-neon-purple bg-opacity-20 border border-neon-purple rounded-lg hover:bg-opacity-40 transition"> <i class="fas fa-utensils mr-2"></i> Tavern </button> </div> `; break; case 'scan': contentDiv.innerHTML = ` <div class="max-w-md mx-auto bg-black bg-opacity-50 border border-neon-blue p-6 rounded-lg"> <div class="h-64 bg-gray-900 mb-4 relative overflow-hidden"> <div class="absolute inset-0 bg-gradient-to-b from-transparent to-black opacity-70"></div> <div class="absolute inset-0 flex items-center justify-center"> <div class="text-center"> <div class="text-4xl mb-2">${Math.floor(Math.random() * 100)}%</div> <div class="text-xs">Scan Complete</div> </div> </div> </div> <h3 class="sci-fi-font text-neon-blue mb-2">Detected Resources</h3> <ul class="text-sm space-y-1"> ${planet.resources.map(res => `<li>• ${res}</li>`).join('')} </ul> </div> `; break; case 'land': contentDiv.innerHTML = ` <div class="max-w-md mx-auto"> <div class="bg-black bg-opacity-50 border border-neon-blue p-6 rounded-lg mb-4"> <h3 class="sci-fi-font text-neon-blue mb-2">Base Camp Established</h3> <p class="text-sm">Your crew has set up a temporary base on ${planet.name}.</p> </div> <div class="grid grid-cols-2 gap-4"> <button class="px-4 py-2 bg-neon-blue bg-opacity-20 border border-neon-blue rounded hover:bg-opacity-40 transition"> Deploy Drones </button> <button class="px-4 py-2 bg-neon-blue bg-opacity-20 border border-neon-blue rounded hover:bg-opacity-40 transition"> Send Scouts </button> <button class="px-4 py-2 bg-neon-blue bg-opacity-20 border border-neon-blue rounded hover:bg-opacity-40 transition"> Extract Resources </button> <button class="px-4 py-2 bg-neon-blue bg-opacity-20 border border-neon-blue rounded hover:bg-opacity-40 transition"> Research Samples </button> </div> </div> `; break; } } function getFuelCost(action) { const costs = { explore: 5, settlement: 2, scan: 3, land: 8 }; return costs[action] || 0; } function getSuppliesCost(action) { const costs = { explore: 10, settlement: 5, scan: 0, land: 15 }; return costs[action] || 0; } function getTerrainDescription(planet) { const terrains = { 1: "volcanic and extremely hot", 2: "aquatic with floating islands", 3: "lush with dense vegetation", 4: "arid with shifting sands", 5: "dark and mysterious" }; return terrains[planet.id] || "unusual"; } function getArchitectureDescription(planet) { const styles = { 1: "built from black basalt with glowing magma channels", 2: "fluid and organic, made from living coral", 3: "grown from living trees and vines", 4: "ancient stonework covered in golden hieroglyphs", 5: "impossibly smooth black surfaces with no visible seams" }; return styles[planet.id] || "unique"; } function getEnvironmentDescription(planet) { const environments = { 1: "scorching hot with ash-filled winds", 2: "humid with frequent rain showers", 3: "teeming with life and vibrant colors", 4: "dry with occasional sandstorms", 5: "eerily silent with strange energy fluctuations" }; return environments[planet.id] || "challenging"; } function processWorldTurn() { gameTurn++; addLogMessage(`Turn ${gameTurn} completed`); // Regenerate party resources partyResources.fuel = Math.min(100, partyResources.fuel + 2); partyResources.supplies = Math.min(100, partyResources.supplies + 1); // Update UI updateResourceDisplay(); // Process pending world events processEvents(); } function consumeResources(fuel, supplies) { partyResources.fuel -= fuel; partyResources.supplies -= supplies; if (partyResources.fuel <= 0 || partyResources.supplies <= 0) { addLogMessage("CRITICAL: Resources depleted!"); // Trigger emergency state } } function playSound(src, isMusic = false) { try { const audio = isMusic ? audioElements.backgroundMusic : audioElements.sfx; if (audio.src !== src) { audio.src = src; } return audio.play().catch(e => { console.warn('Audio playback prevented:', e); // Implement any fallback behavior here }); } catch (e) { console.error('Error playing sound:', e); } } function triggerRandomEvent(chance) { if (Math.random() < chance) { const events = [ "Solar flare detected! Systems temporarily impaired.", "Alien artifact discovered!", "Meteor shower approaching!", "Distress signal detected nearby.", "Hostile ships detected in orbit." ]; const randomEvent = events[Math.floor(Math.random() * events.length)]; worldEvents.push(randomEvent); addLogMessage(`EVENT: ${randomEvent}`); } } function processEvents() { if (worldEvents.length > 0) { const eventId = worldEvents.shift(); showEventScene(eventId); } } function showEventScene(eventId) { const event = eventScenes[eventId]; if (!event) return; activeEventScene = eventId; const eventScene = document.getElementById('eventScene'); // Populate event data document.getElementById('eventTitle').textContent = event.title; document.getElementById('eventDescription').textContent = event.description; document.getElementById('eventImage').querySelector('img').src = event.image; // Add choices const choicesContainer = document.getElementById('eventChoices'); choicesContainer.innerHTML = ''; event.choices.forEach(choice => { const button = document.createElement('button'); button.className = 'px-4 py-2 bg-neon-blue bg-opacity-20 border border-neon-blue rounded hover:bg-opacity-40 transition text-left'; button.textContent = choice.text; button.addEventListener('click', () => handleEventChoice(choice.action)); choicesContainer.appendChild(button); }); // Apply animations event.animations.forEach(anim => { eventScene.classList.add(anim); }); eventScene.style.display = 'flex'; } function handleEventChoice(action) { addLogMessage(`Chose action: ${action}`); // Process the choice switch(action) { case 'investigateArtifact': // Add rewards or consequences partyResources.credits += 150; addLogMessage("Gained 150 credits from artifact!"); break; case 'ignoreArtifact': // Other consequences addLogMessage("The artifact remains undiscovered..."); break; } // Close the event scene document.getElementById('eventScene').style.display = 'none'; activeEventScene = null; // Process next event if any processEvents(); } // Admin mode toggle function toggleEditorMode(enable) { document.getElementById('eventAdminControls').style.display = enable ? 'block' : 'none'; if (enable) { // Populate editor with current event data if one is active if (activeEventScene) { const event = eventScenes[activeEventScene]; // Fill editor fields... } } } function updateResourceDisplay() { document.querySelectorAll('.fuel-display').forEach(el => { el.textContent = `${partyResources.fuel}/100`; el.style.width = `${partyResources.fuel}%`; }); document.querySelectorAll('.supplies-display').forEach(el => { el.textContent = `${partyResources.supplies}/100`; el.style.width = `${partyResources.supplies}%`; }); } function discoverResources(planet) { const discoveryChance = 0.6; // 60% chance per resource planet.resources.forEach(resource => { if (Math.random() < discoveryChance) { addLogMessage(`Discovered: ${resource}`); // Add to inventory } }); } // Action Log Functions function addLogMessage(message) { const logContent = document.getElementById('logContent'); const now = new Date(); const timeString = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`; const logEntry = document.createElement('div'); logEntry.className = 'log-entry'; logEntry.innerHTML = `<span class="log-time">[${timeString}]</span><span class="log-message">${message}</span>`; logContent.appendChild(logEntry); logContent.scrollTop = logContent.scrollHeight; } function makeDraggable(element) { let isDragging = false; let offsetX, offsetY; const header = element.querySelector('.action-log-header'); header.addEventListener('mousedown', (e) => { isDragging = true; const rect = element.getBoundingClientRect(); offsetX = e.clientX - rect.left; offsetY = e.clientY - rect.top; element.style.cursor = 'grabbing'; }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; const x = e.clientX - offsetX; const y = e.clientY - offsetY; element.style.left = `${x}px`; element.style.top = `${y}px`; element.style.right = 'auto'; element.style.bottom = 'auto'; }); document.addEventListener('mouseup', () => { isDragging = false; element.style.cursor = ''; }); } function showCharacterModal(character) { const modal = document.getElementById('characterModal'); document.getElementById('characterFullBodyImg').src = character.fullBody; document.getElementById('characterModalName').textContent = character.name; document.getElementById('characterModalRole').textContent = character.role; document.getElementById('characterBio').textContent = character.bio; // Set attributes const attributesDiv = document.getElementById('characterAttributes'); attributesDiv.innerHTML = ''; for (const [attr, value] of Object.entries(character.attributes)) { const attrEl = document.createElement('div'); attrEl.className = 'flex justify-between text-sm'; attrEl.innerHTML = ` <span class="capitalize">${attr}:</span> <span>${value}/20</span> <div class="w-3/4 h-2 bg-gray-800 rounded-full"> <div class="h-2 bg-neon-blue rounded-full" style="width: ${value * 5}%"></div> </div> `; attributesDiv.appendChild(attrEl); } // Set equipment const equipmentDiv = document.getElementById('characterEquipment'); equipmentDiv.innerHTML = ''; for (const [slot, item] of Object.entries(character.equipment)) { const equipEl = document.createElement('div'); equipEl.className = 'flex justify-between text-sm'; equipEl.innerHTML = ` <span class="capitalize">${slot}:</span> <span class="text-neon-purple">${item}</span> `; equipmentDiv.appendChild(equipEl); } // Set skills const skillsDiv = document.getElementById('characterSkills'); skillsDiv.innerHTML = ''; character.skills.forEach(skill => { const skillEl = document.createElement('div'); skillEl.className = 'px-3 py-1 bg-neon-blue bg-opacity-20 rounded-full text-xs'; skillEl.textContent = skill; skillsDiv.appendChild(skillEl); }); // Add wearables overlays const fullBodyContainer = document.getElementById('characterFullBody'); // Clear existing wearables const existingWearables = fullBodyContainer.querySelectorAll('.wearable-overlay'); existingWearables.forEach(el => el.remove()); // Add new wearables if (character.wearables) { character.wearables.forEach(wearable => { const overlay = document.createElement('img'); overlay.className = 'wearable-overlay absolute inset-0 w-full h-full object-cover'; overlay.src = wearable.img; overlay.style.pointerEvents = 'none'; fullBodyContainer.appendChild(overlay); }); } modal.style.display = 'flex'; } function initAudio() { // Configure audio elements audioElements.backgroundMusic.loop = true; audioElements.backgroundMusic.volume = 0.7; audioElements.sfx.volume = 0.8; // Preload audio (optional) audioElements.backgroundMusic.src = 'path/to/background-music.mp3'; } function cleanupAudio() { // Pause and clean up audio elements audioElements.backgroundMusic.pause(); audioElements.backgroundMusic.removeAttribute('src'); audioElements.sfx.pause(); audioElements.sfx.removeAttribute('src'); } document.addEventListener('DOMContentLoaded', function() { // Initialize audio initAudio(); // Add character modal close handler document.getElementById('closeCharacterModal').addEventListener('click', function() { document.getElementById('characterModal').style.display = 'none'; }); // Initialize game elements makeDraggable(document.getElementById('actionLog')); // Toggle log minimized state document.getElementById('toggleLog').addEventListener('click', function() { const log = document.getElementById('actionLog'); const isMinimized = log.classList.toggle('minimized'); this.textContent = isMinimized ? '+' : '_'; // Ensure header remains clickable for dragging if (isMinimized) { log.style.height = '20px'; } else { log.style.height = ''; } }); // Add initial log messages addLogMessage('Game initialized'); addLogMessage('Systems check complete'); addLogMessage('Navigation systems online'); initPartyCards(); initNodeMap(); initPlanets(); // UI Toggles const logToggle = document.getElementById('logToggle'); const actionLog = document.getElementById('actionLog'); const mapToggle = document.getElementById('mapToggle'); const mapOverlay = document.getElementById('mapOverlay'); const closeMap = document.getElementById('closeMap'); const menuToggle = document.getElementById('menuToggle'); const quickMenu = document.getElementById('quickMenu'); // Initialize log state let isLogVisible = true; logToggle.addEventListener('click', function() { isLogVisible = !isLogVisible; actionLog.style.display = isLogVisible ? 'block' : 'none'; this.classList.toggle('bg-neon-blue', isLogVisible); this.classList.toggle('text-black', isLogVisible); addLogMessage(isLogVisible ? 'Action log opened' : 'Action log closed'); }); const closeInhabitants = document.getElementById('closeInhabitants'); const closePlanetDetails = document.getElementById('closePlanetDetails'); const inhabitantsPanel = document.getElementById('inhabitantsPanel'); const planetDetails = document.getElementById('planetDetails'); // Initialize map state let isMapVisible = false; mapToggle.addEventListener('click', function() { isMapVisible = !isMapVisible; if (isMapVisible) { mapOverlay.classList.remove('hidden'); mapOverlay.classList.add('flex'); this.classList.add('bg-neon-blue', 'text-black'); addLogMessage('Navigation map opened'); // Center on current node document.querySelectorAll('.node').forEach(node => { if (node.classList.contains('node-current')) { node.scrollIntoView({ behavior: 'smooth', block: 'center' }); } }); } else { mapOverlay.classList.add('hidden'); mapOverlay.classList.remove('flex'); this.classList.remove('bg-neon-blue', 'text-black'); addLogMessage('Navigation map closed'); } }); closeMap.addEventListener('click', function() { mapOverlay.classList.add('hidden'); mapOverlay.classList.remove('flex'); }); // Initialize menu state let isMenuVisible = false; menuToggle.addEventListener('click', function() { isMenuVisible = !isMenuVisible; if (isMenuVisible) { quickMenu.classList.remove('hidden'); this.classList.add('bg-neon-blue', 'text-black'); addLogMessage('Quick menu opened'); // Close other overlays if open if (isMapVisible) { mapOverlay.classList.add('hidden'); mapOverlay.classList.remove('flex'); mapToggle.classList.remove('bg-neon-blue', 'text-black'); isMapVisible = false; } } else { quickMenu.classList.add('hidden'); this.classList.remove('bg-neon-blue', 'text-black'); addLogMessage('Quick menu closed'); } }); // Add functionality to quick menu buttons document.querySelectorAll('#quickMenu button').forEach(button => { button.addEventListener('click', function() { const action = this.textContent.trim(); switch(action) { case 'Inventory': addLogMessage('Opening inventory...'); alert('Inventory system would open here'); break; case 'Skills': addLogMessage('Viewing crew skills...'); alert('Skills panel would open here'); break; case 'Journal': addLogMessage('Accessing captain\'s log...'); alert('Journal/quest log would open here'); break; case 'Settings': // Already handled by existing code break; case 'Save Game': addLogMessage('Saving game progress...'); alert('Game saved successfully!'); break; } quickMenu.classList.add('hidden'); menuToggle.classList.remove('bg-neon-blue', 'text-black'); }); }); // Settings panel toggle document.getElementById('settingsBtn').addEventListener('click', function() { quickMenu.classList.add('hidden'); document.getElementById('settingsPanel').classList.remove('hidden'); }); document.getElementById('closeSettings').addEventListener('click', function() { document.getElementById('settingsPanel').classList.add('hidden'); }); // Volume slider updates document.getElementById('masterVolume').addEventListener('input', function() { const volume = this.value / 100; document.getElementById('masterVolumeValue').textContent = this.value + '%'; audioElements.backgroundMusic.volume = volume * 0.7; // Adjusted for music audioElements.sfx.volume = volume * 0.8; // Adjusted for SFX }); document.getElementById('musicVolume').addEventListener('input', function() { const volume = this.value / 100; document.getElementById('musicVolumeValue').textContent = this.value + '%'; audioElements.backgroundMusic.volume = volume; }); document.getElementById('sfxVolume').addEventListener('input', function() { const volume = this.value / 100; document.getElementById('sfxVolumeValue').textContent = this.value + '%'; audioElements.sfx.volume = volume; }); // Save settings document.getElementById('saveSettings').addEventListener('click', function() { alert('Settings saved!'); document.getElementById('settingsPanel').classList.add('hidden'); // In a real game, you would save these values to localStorage or send to server }); // Reset settings document.getElementById('resetSettings').addEventListener('click', function() { if (confirm('Reset all settings to default values?')) { // Reset sliders document.getElementById('masterVolume').value = 50; document.getElementById('musicVolume').value = 70; document.getElementById('sfxVolume').value = 80; document.getElementById('masterVolumeValue').textContent = '50%'; document.getElementById('musicVolumeValue').textContent = '70%'; document.getElementById('sfxVolumeValue').textContent = '80%'; // Reset checkboxes document.getElementById('enableShadows').checked = true; document.getElementById('enableParticles').checked = true; document.getElementById('enableBloom').checked = false; document.getElementById('showTutorials').checked = true; document.getElementById('showTooltips').checked = true; document.getElementById('autoSave').checked = false; // Reset selects document.getElementById('resolution').value = '1280x720'; document.getElementById('textSpeed').value = 'Normal'; alert('Settings reset to defaults!'); } }); closeInhabitants.addEventListener('click', function() { inhabitantsPanel.style.display = 'none'; }); closePlanetDetails.addEventListener('click', function() { planetDetails.style.display = 'none'; }); // Planet interaction events document.getElementById('exitPlanet').addEventListener('click', exitPlanet); document.querySelectorAll('.planet-action').forEach(button => { button.addEventListener('click', function() { handlePlanetAction(this.dataset.action); }); }); // Clean up audio when page unloads window.addEventListener('beforeunload', cleanupAudio); // Close panels when clicking outside document.addEventListener('click', function(e) { if (!inhabitantsPanel.contains(e.target) && !e.target.closest('.planet')) { inhabitantsPanel.style.display = 'none'; } if (!planetDetails.contains(e.target) && !e.target.closest('.planet') && !e.target.closest('.inhabitant')) { planetDetails.style.display = 'none'; } }); }); function initPartyCards() { const partyCards = document.getElementById('partyCards'); const characters = [ { id: 1, name: 'Aria', role: 'Captain', hp: 95, energy: 80, img: 'https://api.dicebear.com/7.x/bottts-neutral/svg?seed=Aria', fullBody: 'https://api.dicebear.com/7.x/bottts-neutral/svg?seed=Aria&size=500', attributes: { strength: 8, intelligence: 12, charisma: 14, agility: 10 }, equipment: { weapon: 'Plasma Pistol', armor: 'Nano-Fiber Vest', accessory: 'Comm Link' }, bio: 'Former star fleet officer with a reputation for getting the job done no matter the cost.', skills: ['Leadership', 'Piloting', 'Tactics', 'Diplomacy'], wearables: [ { type: 'helmet', img: 'https://example.com/helmet1.png' }, { type: 'armor', img: 'https://example.com/armor1.png' } ] }, // Similar expanded data for other characters... { id: 2, name: 'Vex', role: 'Engineer', hp: 80, energy: 65, img: 'https://api.dicebear.com/7.x/bottts-neutral/svg?seed=Vex', fullBody: 'https://api.dicebear.com/7.x/bottts-neutral/svg?seed=Vex&size=500', // ... other expanded fields } ]; characters.forEach(char => { const card = document.createElement('div'); card.className = 'character-card w-20 h-28 rounded-lg overflow-hidden relative cursor-pointer'; card.innerHTML = ` <img src="${char.img}" alt="${char.name}" class="w-full h-full object-cover"> <div class="absolute bottom-0 left-0 right-0 bg-black bg-opacity-70 p-1 text-center"> <p class="text-xs font-bold truncate">${char.name}</p> <div class="flex justify-between text-xxs px-1"> <span>HP:${char.hp}%</span> <span>EN:${char.energy}%</span> </div> </div> `; card.addEventListener('click', () => showCharacterModal(char)); partyCards.appendChild(card); }); } function initNodeMap() { const nodeMap = document.getElementById('nodeMap'); // Sample node data const nodes = [ { id: 1, x: 10, y: 50, visited: true, current: false }, { id: 2, x: 25, y: 30, visited: true, current: false }, { id: 3, x: 40, y: 50, visited: true, current: true }, { id: 4, x: 55, y: 30, visited: false, current: false }, { id: 5, x: 70, y: 50, visited: false, current: false }, { id: 6, x: 85, y: 30, visited: false, current: false } ]; // Connections between nodes const connections = [ { from: 1, to: 2 }, { from: 2, to: 3 }, { from: 3, to: 4 }, { from: 4, to: 5 }, { from: 5, to: 6 } ]; // Draw connections first (so they appear behind nodes) connections.forEach(conn => { const fromNode = nodes.find(n => n.id === conn.from); const toNode = nodes.find(n => n.id === conn.to); if (fromNode && toNode) { const x1 = `${fromNode.x}%`; const y1 = `${fromNode.y}%`; const x2 = `${toNode.x}%`; const y2 = `${toNode.y}%`; // Calculate distance and angle const dx = toNode.x - fromNode.x; const dy = toNode.y - fromNode.y; const distance = Math.sqrt(dx * dx + dy * dy); const angle = Math.atan2(dy, dx) * 180 / Math.PI; const connection = document.createElement('div'); connection.className = 'node-connection'; connection.style.left = x1; connection.style.top = y1; connection.style.width = `${distance}%`; connection.style.transform = `rotate(${angle}deg)`; nodeMap.appendChild(connection); } }); // Draw nodes nodes.forEach(node => { const nodeEl = document.createElement('div'); nodeEl.className = 'node'; if (node.visited) nodeEl.classList.add('node-visited'); if (node.current) nodeEl.classList.add('node-current'); nodeEl.style.left = `${node.x}%`; nodeEl.style.top = `${node.y}%`; nodeEl.dataset.nodeId = node.id; nodeEl.addEventListener('click', function() { if (node.visited) { alert(`Traveling to node ${node.id}`); // In a real game, this would trigger scene transition } else { alert('This location is unexplored'); } }); nodeMap.appendChild(nodeEl); }); } function initPlanets() { const planetsContainer = document.getElementById('planetsContainer'); const inhabitantsPanel = document.getElementById('inhabitantsPanel'); const inhabitantsList = document.getElementById('inhabitantsList'); const planetDetails = document.getElementById('planetDetails'); const planetResources = document.getElementById('planetResources'); // Sample planetary data const planets = [ { id: 1, name: "Pyronis", type: "planet-1", x: 20, y: 30, size: 80, description: "A molten world with rivers of lava and towering obsidian spires. Home to the heat-resistant Pyronians.", population: "12.7 billion", tech: "Industrial", status: "Allied", resources: ["Magma Crystals", "Obsidian", "Thermal Energy"], inhabitants: [ { id: 1, name: "Ignar", img: "https://api.dicebear.com/7.x/bottts-neutral/svg?seed=Ignar", role: "High Priest" }, { id: 2, name: "Vulthra", img: "https://api.dicebear.com/7.x/bottts-neutral/svg?seed=Vulthra", role: "Miner Guildmaster" }, { id: 3, name: "Flarxis", img: "https://api.dicebear.com/7.x/bottts-neutral/svg?seed=Flarxis", role: "Diplomat" } ], locations: ["Obsidian Citadel", "Magma Forges", "The Eternal Caldera"] }, { id: 2, name: "Aquarion", type: "planet-2", x: 50, y: 60, size: 100, description: "A water world with floating cities and deep ocean trenches hiding ancient secrets.", population: "8.3 billion", tech: "Advanced", status: "Neutral", resources: ["Aqua Crystals", "Deep Sea Minerals", "Hydro Energy"], inhabitants: [ { id: 4, name: "Marei", img: "https://i.pravatar.cc/150?img=14", role: "Oceanographer" }, { id: 5, name: "Tidalis", img: "https://i.pravatar.cc/150?img=15", role: "Fleet Admiral" }, { id: 6, name: "Nerissa", img: "https://i.pravatar.cc/150?img=16", role: "Historian" } ], locations: ["Neptune Spire", "Abyssal Station", "The Coral Bazaar"] }, { id: 3, name: "Verdantis", type: "planet-3", x: 70, y: 40, size: 90, description: "A lush jungle planet with towering flora and diverse ecosystems. The Verdans live in harmony with nature.", population: "5.2 billion", tech: "Pre-Warp", status: "Friendly", resources: ["Bio-Organic Compounds", "Rare Herbs", "Solar Energy"], inhabitants: [ { id: 7, name: "Silva", img: "https://i.pravatar.cc/150?img=17", role: "Elder" }, { id: 8, name: "Terrax", img: "https://i.pravatar.cc/150?img=18", role: "Hunter" }, { id: 9, name: "Flora", img: "https://i.pravatar.cc/150?img=19", role: "Herbalist" } ], locations: ["Canopy City", "Root Nexus", "The Shimmering Falls"] }, { id: 4, name: "Aurelia", type: "planet-4", x: 30, y: 70, size: 70, description: "A desert planet with golden sands and ancient ruins. The Aurelians are nomadic traders and scholars.", population: "3.9 billion", tech: "Medieval", status: "Cautious", resources: ["Golden Sand", "Ancient Artifacts", "Solar Power"], inhabitants: [ { id: 10, name: "Solon", img: "https://i.pravatar.cc/150?img=20", role: "Archaeologist" }, { id: 11, name: "Dunea", img: "https://i.pravatar.cc/150?img=21", role: "Merchant Queen" }, { id: 12, name: "Ozymar", img: "https://i.pravatar.cc/150?img=22", role: "Star Gazer" } ], locations: ["The Golden Pyramid", "Oasis of Mirrors", "The Singing Dunes"] }, { id: 5, name: "Umbrax", type: "planet-5", x: 80, y: 20, size: 60, description: "A dark planet shrouded in perpetual twilight. The Umbrians are secretive and technologically advanced.", population: "1.1 billion", tech: "Cutting Edge", status: "Hostile", resources: ["Dark Matter", "Void Crystals", "Zero-Point Energy"], inhabitants: [ { id: 13, name: "Noctis", img: "https://i.pravatar.cc/150?img=23", role: "Shadow Minister" }, { id: 14, name: "Eclipsa", img: "https://i.pravatar.cc/150?img=24", role: "Scientist" }, { id: 15, name: "Voidar", img: "https://i.pravatar.cc/150?img=25", role: "Security Chief" } ], locations: ["The Dark Spire", "Twilight Archives", "The Veil Gate"] } ]; // Create planets planets.forEach(planet => { const planetEl = document.createElement('div'); planetEl.className = `planet ${planet.type} pulse`; planetEl.style.width = `${planet.size}px`; planetEl.style.height = `${planet.size}px`; planetEl.style.left = `${planet.x}%`; planetEl.style.top = `${planet.y}%`; planetEl.dataset.planetId = planet.id; // Add rings for some planets if (planet.id === 2 || planet.id === 5) { const ring = document.createElement('div'); ring.className = 'planet-ring'; ring.style.width = `${planet.size * 1.8}px`; ring.style.height = `${planet.size * 0.4}px`; ring.style.transform = `rotate(${Math.random() * 60}deg)`; planetEl.appendChild(ring); } // Add planet label const label = document.createElement('div'); label.className = 'planet-label sci-fi-font'; label.textContent = planet.name; planetEl.appendChild(label); // Add click event to travel to planet planetEl.addEventListener('click', function(e) { e.stopPropagation(); // Show travel confirmation if (confirm(`Travel to ${planet.name}?`)) { travelToPlanet(planet); } // Populate inhabitants inhabitantsList.innerHTML = ''; planet.inhabitants.forEach(inhabitant => { const inhabitantEl = document.createElement('div'); inhabitantEl.className = 'inhabitant'; inhabitantEl.innerHTML = ` <img src="${inhabitant.img}" alt="${inhabitant.name}"> <div class="inhabitant-name">${inhabitant.name}</div> <div class="text-xxs text-neon-blue">${inhabitant.role}</div> `; inhabitantEl.addEventListener('click', function() { showPlanetDetails(planet); }); inhabitantsList.appendChild(inhabitantEl); }); }); // Add double click for planet details planetEl.addEventListener('dblclick', function() { showPlanetDetails(planet); }); planetsContainer.appendChild(planetEl); }); function showPlanetDetails(planet) { const planetDetailImage = document.getElementById('planetDetailImage'); const planetDetailName = document.getElementById('planetDetailName'); const planetDetailDescription = document.getElementById('planetDetailDescription'); const planetDetailPopulation = document.getElementById('planetDetailPopulation'); const planetDetailTech = document.getElementById('planetDetailTech'); const planetDetailStatus = document.getElementById('planetDetailStatus'); const planetLocations = document.getElementById('planetLocations'); const planetResources = document.getElementById('planetResources'); // Set planet details planetDetailImage.className = `w-20 h-20 rounded-full mr-4 ${planet.type}`; planetDetailName.textContent = planet.name; planetDetailDescription.textContent = planet.description; planetDetailPopulation.textContent = planet.population; planetDetailTech.textContent = planet.tech; planetDetailStatus.textContent = planet.status; // Add locations planetLocations.innerHTML = ''; planet.locations.forEach(location => { const locationEl = document.createElement('div'); locationEl.className = 'px-3 py-1 bg-neon-blue bg-opacity-20 rounded-full text-xs'; locationEl.textContent = location; planetLocations.appendChild(locationEl); }); // Add resources planetResources.innerHTML = ''; planet.resources.forEach(resource => { const resourceEl = document.createElement('div'); resourceEl.className = 'px-3 py-1 bg-neon-purple bg-opacity-20 rounded-full text-xs'; resourceEl.textContent = resource; planetResources.appendChild(resourceEl); }); // Show panel planetDetails.style.display = 'block'; inhabitantsPanel.style.display = 'none'; } } </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=weltenschmid/nebuladrift" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> </html>