JIT
create a fun web game with galactic tycoon theme
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Galactic Tycoon</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js"></script> <style> body { margin: 0; font-family: 'Space Mono', monospace; background: #000; color: #0f0; overflow: hidden; } #game-panel { position: absolute; top: 10px; left: 10px; width: 250px; background: rgba(0, 0, 0, 0.8); padding: 15px; border-radius: 8px; border: 2px solid #0f0; box-shadow: 0 0 15px #0f0; } #game-panel h3 { color: #0f0; text-align: center; margin-top: 0; border-bottom: 1px solid #0f0; padding-bottom: 8px; } #game-panel button { display: block; width: calc(100% - 10px); padding: 10px; margin: 8px 5px; background: #004d00; color: #0f0; border: 1px solid #0f0; border-radius: 5px; cursor: pointer; font-family: 'Space Mono', monospace; font-size: 1em; transition: background 0.2s, color 0.2s; } #game-panel button:hover { background: #008000; color: #fff; } #game-panel button:disabled { background: #333; color: #666; border-color: #666; cursor: not-allowed; } .resource-display { display: flex; justify-content: space-between; margin-bottom: 5px; font-size: 1.1em; } canvas { display: block; } </style> <link href="https://fonts.googleapis.com/css2?family=Space+Mono&display=swap" rel="stylesheet"> </head> <body> <div id="game-panel"> <h3>Galactic Tycoon</h3> <p class="resource-display">Credits: <span id="credits">0</span></p> <p class="resource-display">Metal: <span id="metal">0</span></p> <p class="resource-display">Water: <span id="water">0</span></p> <hr style="border-color: #0f0;"> <button id="collect-button" onclick="window.collectResources()">Collect Resources</button> <button id="buy-asteroid-button" onclick="window.buyBody('Asteroid')" disabled>Buy Asteroid (100 C)</button> <button id="buy-water-asteroid-button" onclick="window.buyBody('Water Asteroid')" disabled>Buy Water Asteroid (150 C)</button> <button id="buy-planet-button" onclick="window.buyBody('Planet')" disabled>Buy Planet (500 M, 200 W)</button> <button onclick="window.resetGame()">Reset Game</button> </div> <script> let bodies = []; const G = 0.5; // Gravitational constant const AU = 100; // Astronomical Unit (scaling factor for distances) let credits = 100; // Starting credits let metal = 0; let water = 0; let cameraZoom = 1; let cameraOffset = { x: 0, y: 0 }; let draggedBody = null; let dragStartPos = null; // Simplified star type for the game's initial setup const initialStarType = { type: 'Yellow Dwarf', mass: 5000, radius: 40, color: '#FFFF00' }; function setup() { createCanvas(windowWidth, windowHeight); resetGame(); } function draw() { background(0); push(); translate(width / 2, height / 2); scale(cameraZoom); translate(-width / 2 + cameraOffset.x, -height / 2 + cameraOffset.y); // Update and draw bodies for (let i = 0; i < bodies.length; i++) { for (let j = 0; j < bodies.length; j++) { if (i !== j && bodies[i] && bodies[j]) { bodies[i].applyGravity(bodies[j]); } } } for (let body of bodies) { if (body) { body.update(); body.draw(); } } pop(); updateGamePanel(); } class CelestialBody { constructor(x, y, mass, radius, color, type, velocity = null, starType = null, name = null, parent = null) { this.pos = createVector(x, y); this.vel = velocity || createVector(0, 0); this.mass = mass; this.radius = radius; this.color = color; this.type = type; this.starType = starType; this.name = name || this.generateName(); this.star = type === 'star' ? null : parent || bodies.find(b => b && b.type === 'star') || null; this.trail = []; this.trailLength = type === 'comet' ? 50 : 20; } generateName() { const prefixes = ['Astro', 'Cosmo', 'Nova', 'Star', 'Zenith', 'Orion']; const suffixes = ['rock', 'field', 'belt', 'cluster', 'prime', 'ia']; return prefixes[Math.floor(Math.random() * prefixes.length)] + suffixes[Math.floor(Math.random() * suffixes.length)]; } applyGravity(other) { if (this === other || !other || this.type === 'star' && other.type === 'star') return; // Stars don't apply gravity to each other in this simplified version let force = p5.Vector.sub(other.pos, this.pos); let distance = force.mag(); // Prevent division by zero and handle very close objects if (distance < this.radius + other.radius) { this.handleCollision(other); return; } let strength = (G * this.mass * other.mass) / (distance * distance); force.setMag(strength); let acc = force.copy().div(this.mass); this.vel.add(acc); } handleCollision(other) { if (this.mass <= other.mass) return; // Only the larger body absorbs the smaller // Simple merge: larger body grows this.mass += other.mass; this.radius = Math.pow(this.radius ** 3 + other.radius ** 3, 1 / 3); // Remove the absorbed body const index = bodies.indexOf(other); if (index !== -1) { bodies.splice(index, 1); } // If a player-placed body collides, give some resources if (other.type === 'asteroid') { metal += 10; } else if (other.type === 'water_asteroid') { water += 10; } } update() { if (this !== draggedBody) { // Only update position if not being dragged this.pos.add(this.vel); } this.trail.push(this.pos.copy()); if (this.trail.length > this.trailLength) { this.trail.shift(); } } draw() { push(); // Draw trail if (this.trail.length > 1) { noFill(); stroke(color(this.color).setAlpha(50)); strokeWeight(1); beginShape(); for (let pos of this.trail) { vertex(pos.x, pos.y); } endShape(); } // Draw body fill(this.color); noStroke(); ellipse(this.pos.x, this.pos.y, this.radius * 2); // Add a small pulsating effect for player-collectible asteroids if (this.type === 'asteroid' || this.type === 'water_asteroid') { stroke(0, 255, 0, map(sin(frameCount * 0.1), -1, 1, 50, 150)); strokeWeight(2); noFill(); ellipse(this.pos.x, this.pos.y, this.radius * 2 + 5 + sin(frameCount * 0.1) * 2); } pop(); } } // --- Game Functions --- window.collectResources = function() { let collectedMetal = 0; let collectedWater = 0; let bodiesToRemove = []; for (let i = bodies.length - 1; i >= 0; i--) { const body = bodies[i]; if (body.type === 'asteroid') { collectedMetal += Math.floor(body.mass); bodiesToRemove.push(i); } else if (body.type === 'water_asteroid') { collectedWater += Math.floor(body.mass); bodiesToRemove.push(i); } } // Remove collected bodies from the array for (let i of bodiesToRemove.sort((a, b) => b - a)) { bodies.splice(i, 1); } metal += collectedMetal; water += collectedWater; credits += (collectedMetal * 0.5) + (collectedWater * 0.7); // Credits for collected resources updateGamePanel(); }; window.buyBody = function(type) { let costCredits = 0; let costMetal = 0; let costWater = 0; let newBody = null; const star = bodies[0]; // Assuming the first body is always the star // Place new bodies at a random offset from the center let x = width / 2 + (Math.random() * 300 - 150) / cameraZoom + cameraOffset.x; let y = height / 2 + (Math.random() * 300 - 150) / cameraZoom + cameraOffset.y; let vel = p5.Vector.random2D().mult(Math.random() * 2 + 1); switch (type) { case 'Asteroid': costCredits = 100; if (credits >= costCredits) { newBody = new CelestialBody(x, y, 5 + Math.random() * 5, 5, '#964B00', 'asteroid', vel, null, null, star); credits -= costCredits; } break; case 'Water Asteroid': costCredits = 150; if (credits >= costCredits) { newBody = new CelestialBody(x, y, 5 + Math.random() * 5, 6, '#00BFFF', 'water_asteroid', vel, null, null, star); credits -= costCredits; } break; case 'Planet': costMetal = 500; costWater = 200; if (metal >= costMetal && water >= costWater) { newBody = new CelestialBody(x, y, 200 + Math.random() * 100, 20 + Math.random() * 5, '#1E90FF', 'planet', vel, null, null, star); // A simple planet type metal -= costMetal; water -= costWater; } break; } if (newBody) { bodies.push(newBody); // Give the new body an initial velocity to orbit the star let distToStar = p5.Vector.dist(newBody.pos, star.pos); if (distToStar > 0) { let orbitSpeed = Math.sqrt(G * star.mass / distToStar); let direction = p5.Vector.sub(star.pos, newBody.pos).normalize(); newBody.vel = createVector(-direction.y, direction.x).mult(orbitSpeed); } } updateGamePanel(); }; window.resetGame = function() { bodies = []; credits = 100; metal = 0; water = 0; cameraZoom = 1; cameraOffset = { x: 0, y: 0 }; // Add the initial star const sun = new CelestialBody(width / 2, height / 2, initialStarType.mass, initialStarType.radius, initialStarType.color, 'star', null, initialStarType.type); bodies.push(sun); // Add some initial asteroids for the player to collect for (let i = 0; i < 5; i++) { const distFromStar = 200 + Math.random() * 100; const angle = Math.random() * TWO_PI; const x = width / 2 + distFromStar * Math.cos(angle); const y = height / 2 + distFromStar * Math.sin(angle); const mass = 5 + Math.random() * 5; const radius = 5; const type = Math.random() < 0.5 ? 'asteroid' : 'water_asteroid'; const color = type === 'asteroid' ? '#964B00' : '#00BFFF'; const asteroidVel = createVector(-Math.sin(angle), Math.cos(angle)).mult(Math.sqrt(G * sun.mass / distFromStar)); bodies.push(new CelestialBody(x, y, mass, radius, color, type, asteroidVel, null, null, sun)); } updateGamePanel(); }; function updateGamePanel() { document.getElementById('credits').textContent = credits.toFixed(0); document.getElementById('metal').textContent = metal.toFixed(0); document.getElementById('water').textContent = water.toFixed(0); // Enable/disable buttons based on resources document.getElementById('buy-asteroid-button').disabled = credits < 100; document.getElementById('buy-water-asteroid-button').disabled = credits < 150; document.getElementById('buy-planet-button').disabled = metal < 500 || water < 200; document.getElementById('buy-asteroid-button').textContent = `Buy Asteroid (${100} C)`; document.getElementById('buy-water-asteroid-button').textContent = `Buy Water Asteroid (${150} C)`; document.getElementById('buy-planet-button').textContent = `Buy Planet (${500} M, ${200} W)`; } // --- Camera Controls (simplified from original) --- function mouseWheel(event) { let scaleFactor = 1.05; if (event.delta > 0) { cameraZoom /= scaleFactor; } else { cameraZoom *= scaleFactor; } cameraZoom = constrain(cameraZoom, 0.1, 5); // Prevent extreme zoom } function mousePressed() { draggedBody = null; // Reset dragged body on new click let worldMouse = screenToWorld(mouseX, mouseY); for (let body of bodies) { if (dist(worldMouse.x, worldMouse.y, body.pos.x, body.pos.y) < body.radius) { draggedBody = body; dragStartPos = worldMouse.copy(); break; } } } function mouseDragged() { if (draggedBody && draggedBody.type !== 'star') { // Prevent dragging the star let worldMouse = screenToWorld(mouseX, mouseY); draggedBody.pos.set(worldMouse); draggedBody.trail = []; // Clear trail when dragging } else if (!draggedBody) { // If no body is dragged, drag the camera cameraOffset.x += (mouseX - pmouseX) / cameraZoom; cameraOffset.y += (mouseY - pmouseY) / cameraZoom; } } function mouseReleased() { if (draggedBody && dragStartPos) { let worldMouse = screenToWorld(mouseX, mouseY); let dragDistance = p5.Vector.sub(worldMouse, dragStartPos); // Apply a velocity based on drag draggedBody.vel = dragDistance.mult(0.1); } draggedBody = null; dragStartPos = null; } // Helper to convert screen coordinates to world coordinates function screenToWorld(sx, sy) { let wx = (sx - width / 2) / cameraZoom + width / 2 - cameraOffset.x; let wy = (sy - height / 2) / cameraZoom + height / 2 - cameraOffset.y; return createVector(wx, wy); } </script> </body> </html>

I'll help organize this into a more modular structure by splitting the code into separate files. Here's the improved version:

Updated files
v1
An error occurred in the code: ``` Script error. ``` Update the code to resolve this issue.