feat: add camera tracking and surface building (mountains, creatures, cities)
Camera System: - Smooth pan/zoom camera that tracks new creations - Zooms in when AI builds surface features - Tracking indicator with brackets shows current focus - Camera status in HUD shows tracking target and zoom level - Auto-returns to universe view after observing Surface Building (when zoomed to planets): - Mountains: Raised peaks with snow caps - Creatures: Animated life forms (Floaters, Crawlers, Swimmers, etc.) - Buildings: Towers, Temples, Observatories, Monuments with windows - Cities: Glowing settlements with flickering lights AI Improvements: - Slowed decision cycle from 2s to 4.5s for better observation - AI now builds directly ON worlds (not just creates celestial objects) - 15-phase decision tree with surface building priorities - Camera automatically tracks each creation - Deeper zoom (6x) for surface features, normal zoom (2.5x) for space objects HUD Updates: - Surface stats section (mountains, creatures, buildings, cities) - Camera status display - Improved styling with backdrop blur Soul entries now describe surface creations: - "I raised Mt. Thunder on Terra-447. Its peak touches the clouds." - "A new Floater drifts through the air on Gaia-221. Life finds a way." - "The city of Nova rises on Kronos-88. A beacon in the darkness." https://claude.ai/code/session_01MUG7BRZMG28eYmJHGbetRB
This commit is contained in:
parent
d796054c27
commit
d94a0f246d
496
index.html
496
index.html
@ -2443,20 +2443,31 @@
|
|||||||
document.getElementById('worldlabs-blocked').innerHTML = `
|
document.getElementById('worldlabs-blocked').innerHTML = `
|
||||||
<div style="width: 100%; height: 100%; position: relative; overflow: hidden;">
|
<div style="width: 100%; height: 100%; position: relative; overflow: hidden;">
|
||||||
<canvas id="simulated-world-canvas" style="width: 100%; height: 100%;"></canvas>
|
<canvas id="simulated-world-canvas" style="width: 100%; height: 100%;"></canvas>
|
||||||
<div id="universe-hud" style="position: absolute; top: 15px; left: 15px; color: #fff; font-size: 11px; background: rgba(0,0,0,0.7); padding: 12px 15px; border-radius: 8px; border: 1px solid #333; min-width: 200px;">
|
<div id="universe-hud" style="position: absolute; top: 15px; left: 15px; color: #fff; font-size: 11px; background: rgba(0,0,0,0.85); padding: 12px 15px; border-radius: 8px; border: 1px solid #444; min-width: 220px; backdrop-filter: blur(10px);">
|
||||||
<div style="font-size: 14px; font-weight: bold; margin-bottom: 8px; color: #00ff88;">ClawdBot Universe Engine</div>
|
<div style="font-size: 14px; font-weight: bold; margin-bottom: 10px; color: #00ff88; text-shadow: 0 0 10px #00ff8855;">ClawdBot Universe Engine</div>
|
||||||
<div style="margin-bottom: 5px;">Age: <span id="universe-age" style="color: #fbbf24;">0</span> cosmic cycles</div>
|
<div style="margin-bottom: 4px;">Age: <span id="universe-age" style="color: #fbbf24;">0</span> cosmic cycles</div>
|
||||||
<div style="margin-bottom: 5px;">Stars: <span id="universe-stars" style="color: #fbbf24;">0</span></div>
|
<div style="margin-bottom: 4px;">Stars: <span id="universe-stars" style="color: #fbbf24;">0</span></div>
|
||||||
<div style="margin-bottom: 5px;">Planets: <span id="universe-planets" style="color: #60a5fa;">0</span></div>
|
<div style="margin-bottom: 4px;">Planets: <span id="universe-planets" style="color: #60a5fa;">0</span></div>
|
||||||
<div style="margin-bottom: 5px;">Moons: <span id="universe-moons" style="color: #a78bfa;">0</span></div>
|
<div style="margin-bottom: 4px;">Moons: <span id="universe-moons" style="color: #a78bfa;">0</span></div>
|
||||||
<div style="margin-bottom: 5px;">Life Forms: <span id="universe-life" style="color: #4ade80;">0</span></div>
|
<div style="margin-bottom: 4px;">Life Forms: <span id="universe-life" style="color: #4ade80;">0</span></div>
|
||||||
<div style="margin-bottom: 5px;">Civilizations: <span id="universe-civs" style="color: #f472b6;">0</span></div>
|
<div style="margin-bottom: 4px;">Civilizations: <span id="universe-civs" style="color: #f472b6;">0</span></div>
|
||||||
|
<div id="surface-stats" style="margin-top: 8px; padding-top: 8px; border-top: 1px solid #444; display: none;">
|
||||||
|
<div style="font-size: 10px; color: #888; margin-bottom: 4px;">Surface Features:</div>
|
||||||
|
<div style="margin-bottom: 3px;">Mountains: <span id="universe-mountains" style="color: #a78bfa;">0</span></div>
|
||||||
|
<div style="margin-bottom: 3px;">Creatures: <span id="universe-creatures" style="color: #4ade80;">0</span></div>
|
||||||
|
<div style="margin-bottom: 3px;">Buildings: <span id="universe-buildings" style="color: #fbbf24;">0</span></div>
|
||||||
|
<div>Cities: <span id="universe-cities" style="color: #f472b6;">0</span></div>
|
||||||
|
</div>
|
||||||
<div style="margin-top: 8px; padding-top: 8px; border-top: 1px solid #444;">
|
<div style="margin-top: 8px; padding-top: 8px; border-top: 1px solid #444;">
|
||||||
<div style="font-size: 10px; color: #888;">Current Action:</div>
|
<div style="font-size: 10px; color: #888;">Current Action:</div>
|
||||||
<div id="ai-current-action" style="color: #00ff88; font-size: 10px;">Initializing...</div>
|
<div id="ai-current-action" style="color: #00ff88; font-size: 11px; margin-top: 3px;">Initializing...</div>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 8px; padding-top: 8px; border-top: 1px solid #444;">
|
||||||
|
<div style="font-size: 10px; color: #888;">Camera:</div>
|
||||||
|
<div id="camera-status" style="color: #60a5fa; font-size: 10px; margin-top: 3px;">Universe (1.0x)</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="universe-log" style="position: absolute; bottom: 15px; left: 15px; right: 15px; height: 80px; background: rgba(0,0,0,0.7); border-radius: 8px; border: 1px solid #333; padding: 10px; overflow-y: auto; font-family: monospace; font-size: 9px; color: #00ff88;"></div>
|
<div id="universe-log" style="position: absolute; bottom: 15px; left: 15px; right: 15px; height: 100px; background: rgba(0,0,0,0.85); border-radius: 8px; border: 1px solid #444; padding: 10px; overflow-y: auto; font-family: monospace; font-size: 10px; color: #00ff88; backdrop-filter: blur(10px);"></div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -2520,9 +2531,22 @@
|
|||||||
universeLog('Personality matrix loaded: creativity=' + universe.ai.personality.creativity.toFixed(2));
|
universeLog('Personality matrix loaded: creativity=' + universe.ai.personality.creativity.toFixed(2));
|
||||||
addWorldLabsSoulEntry('creation', 'I awaken to an empty void. Infinite potential stretches before me. Let there be light...');
|
addWorldLabsSoulEntry('creation', 'I awaken to an empty void. Infinite potential stretches before me. Let there be light...');
|
||||||
|
|
||||||
|
// Camera system
|
||||||
|
universe.camera = {
|
||||||
|
x: canvas.width / 2,
|
||||||
|
y: canvas.height / 2,
|
||||||
|
targetX: canvas.width / 2,
|
||||||
|
targetY: canvas.height / 2,
|
||||||
|
zoom: 1,
|
||||||
|
targetZoom: 1,
|
||||||
|
tracking: null,
|
||||||
|
returnTimer: null
|
||||||
|
};
|
||||||
|
|
||||||
// Start the AI loop
|
// Start the AI loop
|
||||||
let lastTime = 0;
|
let lastTime = 0;
|
||||||
let aiTickCounter = 0;
|
let aiTickCounter = 0;
|
||||||
|
const AI_TICK_INTERVAL = 4500; // Slowed down to 4.5 seconds
|
||||||
|
|
||||||
function gameLoop(timestamp) {
|
function gameLoop(timestamp) {
|
||||||
if (!worldLabsSession.simulatedMode) return;
|
if (!worldLabsSession.simulatedMode) return;
|
||||||
@ -2530,11 +2554,29 @@
|
|||||||
const deltaTime = timestamp - lastTime;
|
const deltaTime = timestamp - lastTime;
|
||||||
lastTime = timestamp;
|
lastTime = timestamp;
|
||||||
|
|
||||||
|
// Smooth camera movement
|
||||||
|
const cam = universe.camera;
|
||||||
|
cam.x += (cam.targetX - cam.x) * 0.05;
|
||||||
|
cam.y += (cam.targetY - cam.y) * 0.05;
|
||||||
|
cam.zoom += (cam.targetZoom - cam.zoom) * 0.05;
|
||||||
|
|
||||||
|
// If tracking, follow the object
|
||||||
|
if (cam.tracking) {
|
||||||
|
cam.targetX = cam.tracking.x;
|
||||||
|
cam.targetY = cam.tracking.y;
|
||||||
|
}
|
||||||
|
|
||||||
// Clear canvas
|
// Clear canvas
|
||||||
ctx.fillStyle = '#05050f';
|
ctx.fillStyle = '#05050f';
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
// Draw background stars
|
// Apply camera transform
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(canvas.width / 2, canvas.height / 2);
|
||||||
|
ctx.scale(cam.zoom, cam.zoom);
|
||||||
|
ctx.translate(-cam.x, -cam.y);
|
||||||
|
|
||||||
|
// Draw background stars with parallax
|
||||||
universe.backgroundStars.forEach(star => {
|
universe.backgroundStars.forEach(star => {
|
||||||
const twinkle = 0.5 + 0.5 * Math.sin(timestamp * 0.001 * star.twinkleSpeed);
|
const twinkle = 0.5 + 0.5 * Math.sin(timestamp * 0.001 * star.twinkleSpeed);
|
||||||
ctx.fillStyle = `rgba(255, 255, 255, ${star.brightness * twinkle})`;
|
ctx.fillStyle = `rgba(255, 255, 255, ${star.brightness * twinkle})`;
|
||||||
@ -2552,9 +2594,21 @@
|
|||||||
drawObject(ctx, obj, timestamp);
|
drawObject(ctx, obj, timestamp);
|
||||||
});
|
});
|
||||||
|
|
||||||
// AI decision making (every ~2 seconds)
|
// Draw surface features when zoomed in
|
||||||
|
if (cam.zoom > 2.5 && cam.tracking && cam.tracking.surface) {
|
||||||
|
drawSurfaceFeatures(ctx, cam.tracking, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
// Draw tracking indicator (screen space)
|
||||||
|
if (cam.tracking) {
|
||||||
|
drawTrackingIndicator(ctx, canvas, cam.tracking);
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI decision making (slowed down to 4.5 seconds)
|
||||||
aiTickCounter += deltaTime;
|
aiTickCounter += deltaTime;
|
||||||
if (aiTickCounter > 2000) {
|
if (aiTickCounter > AI_TICK_INTERVAL) {
|
||||||
aiTickCounter = 0;
|
aiTickCounter = 0;
|
||||||
aiDecisionCycle(canvas);
|
aiDecisionCycle(canvas);
|
||||||
}
|
}
|
||||||
@ -2568,73 +2622,277 @@
|
|||||||
requestAnimationFrame(gameLoop);
|
requestAnimationFrame(gameLoop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Camera tracking functions
|
||||||
|
function trackObject(obj, zoomLevel, duration) {
|
||||||
|
const cam = universe.camera;
|
||||||
|
cam.tracking = obj;
|
||||||
|
cam.targetZoom = zoomLevel || 2.5;
|
||||||
|
|
||||||
|
// Clear any existing return timer
|
||||||
|
if (cam.returnTimer) clearTimeout(cam.returnTimer);
|
||||||
|
|
||||||
|
// Return to universe view after duration
|
||||||
|
cam.returnTimer = setTimeout(() => {
|
||||||
|
returnToUniverseView();
|
||||||
|
}, duration || 4000);
|
||||||
|
|
||||||
|
universeLog(`Camera tracking: ${obj.name || obj.type}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function zoomToSurface(planet, duration) {
|
||||||
|
const cam = universe.camera;
|
||||||
|
cam.tracking = planet;
|
||||||
|
cam.targetZoom = 6;
|
||||||
|
|
||||||
|
if (cam.returnTimer) clearTimeout(cam.returnTimer);
|
||||||
|
|
||||||
|
cam.returnTimer = setTimeout(() => {
|
||||||
|
returnToUniverseView();
|
||||||
|
}, duration || 6000);
|
||||||
|
|
||||||
|
universeLog(`Zooming to surface of ${planet.name}...`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function returnToUniverseView() {
|
||||||
|
const canvas = document.getElementById('simulated-world-canvas');
|
||||||
|
const cam = universe.camera;
|
||||||
|
cam.tracking = null;
|
||||||
|
cam.targetX = canvas.width / 2;
|
||||||
|
cam.targetY = canvas.height / 2;
|
||||||
|
cam.targetZoom = 1;
|
||||||
|
universeLog('Returning to universe view');
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawTrackingIndicator(ctx, canvas, obj) {
|
||||||
|
ctx.save();
|
||||||
|
ctx.strokeStyle = '#00ff88';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.setLineDash([5, 5]);
|
||||||
|
|
||||||
|
const cx = canvas.width / 2;
|
||||||
|
const cy = canvas.height / 2;
|
||||||
|
const size = 50;
|
||||||
|
|
||||||
|
// Draw brackets
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(cx - size, cy - size + 15);
|
||||||
|
ctx.lineTo(cx - size, cy - size);
|
||||||
|
ctx.lineTo(cx - size + 15, cy - size);
|
||||||
|
ctx.moveTo(cx + size - 15, cy - size);
|
||||||
|
ctx.lineTo(cx + size, cy - size);
|
||||||
|
ctx.lineTo(cx + size, cy - size + 15);
|
||||||
|
ctx.moveTo(cx + size, cy + size - 15);
|
||||||
|
ctx.lineTo(cx + size, cy + size);
|
||||||
|
ctx.lineTo(cx + size - 15, cy + size);
|
||||||
|
ctx.moveTo(cx - size + 15, cy + size);
|
||||||
|
ctx.lineTo(cx - size, cy + size);
|
||||||
|
ctx.lineTo(cx - size, cy + size - 15);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Label
|
||||||
|
ctx.setLineDash([]);
|
||||||
|
ctx.fillStyle = '#00ff88';
|
||||||
|
ctx.font = '12px monospace';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText(obj.name || obj.type, cx, cy + size + 20);
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawSurfaceFeatures(ctx, planet, timestamp) {
|
||||||
|
if (!planet.surface) return;
|
||||||
|
|
||||||
|
const sf = planet.surface;
|
||||||
|
|
||||||
|
// Draw mountains
|
||||||
|
if (sf.mountains) {
|
||||||
|
sf.mountains.forEach(m => {
|
||||||
|
ctx.fillStyle = '#5a5a6a';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(planet.x + m.x - m.size, planet.y + m.y);
|
||||||
|
ctx.lineTo(planet.x + m.x, planet.y + m.y - m.height);
|
||||||
|
ctx.lineTo(planet.x + m.x + m.size, planet.y + m.y);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
// Snow cap
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(planet.x + m.x - m.size * 0.3, planet.y + m.y - m.height * 0.7);
|
||||||
|
ctx.lineTo(planet.x + m.x, planet.y + m.y - m.height);
|
||||||
|
ctx.lineTo(planet.x + m.x + m.size * 0.3, planet.y + m.y - m.height * 0.7);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw creatures
|
||||||
|
if (sf.creatures) {
|
||||||
|
sf.creatures.forEach(c => {
|
||||||
|
ctx.fillStyle = c.color;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(planet.x + c.x + Math.sin(timestamp * 0.003 + c.phase) * 2, planet.y + c.y, c.size, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
// Eyes
|
||||||
|
ctx.fillStyle = '#fff';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(planet.x + c.x + Math.sin(timestamp * 0.003 + c.phase) * 2 - 1, planet.y + c.y - 1, 1, 0, Math.PI * 2);
|
||||||
|
ctx.arc(planet.x + c.x + Math.sin(timestamp * 0.003 + c.phase) * 2 + 1, planet.y + c.y - 1, 1, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw buildings
|
||||||
|
if (sf.buildings) {
|
||||||
|
sf.buildings.forEach(b => {
|
||||||
|
ctx.fillStyle = b.color;
|
||||||
|
ctx.fillRect(planet.x + b.x - b.width / 2, planet.y + b.y - b.height, b.width, b.height);
|
||||||
|
// Windows
|
||||||
|
ctx.fillStyle = '#ffd700';
|
||||||
|
for (let wy = 0; wy < b.height - 4; wy += 6) {
|
||||||
|
for (let wx = 2; wx < b.width - 2; wx += 5) {
|
||||||
|
ctx.fillRect(planet.x + b.x - b.width / 2 + wx, planet.y + b.y - b.height + wy + 2, 2, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw cities (clusters of lights)
|
||||||
|
if (sf.cities) {
|
||||||
|
sf.cities.forEach(city => {
|
||||||
|
// City glow
|
||||||
|
const gradient = ctx.createRadialGradient(
|
||||||
|
planet.x + city.x, planet.y + city.y, 0,
|
||||||
|
planet.x + city.x, planet.y + city.y, city.size
|
||||||
|
);
|
||||||
|
gradient.addColorStop(0, 'rgba(255, 200, 100, 0.4)');
|
||||||
|
gradient.addColorStop(1, 'transparent');
|
||||||
|
ctx.fillStyle = gradient;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(planet.x + city.x, planet.y + city.y, city.size, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
// City lights
|
||||||
|
city.lights.forEach(l => {
|
||||||
|
const flicker = 0.7 + 0.3 * Math.sin(timestamp * 0.01 + l.phase);
|
||||||
|
ctx.fillStyle = `rgba(255, 220, 150, ${flicker})`;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(planet.x + city.x + l.x, planet.y + city.y + l.y, 1, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function aiDecisionCycle(canvas) {
|
function aiDecisionCycle(canvas) {
|
||||||
universe.age++;
|
universe.age++;
|
||||||
const ai = universe.ai;
|
const ai = universe.ai;
|
||||||
const stats = universe.stats;
|
const stats = universe.stats;
|
||||||
|
|
||||||
|
// Initialize surface stats if needed
|
||||||
|
if (!stats.mountains) stats.mountains = 0;
|
||||||
|
if (!stats.creatures) stats.creatures = 0;
|
||||||
|
if (!stats.buildings) stats.buildings = 0;
|
||||||
|
if (!stats.cities) stats.cities = 0;
|
||||||
|
|
||||||
// AI decision tree based on current universe state
|
// AI decision tree based on current universe state
|
||||||
let action = null;
|
let action = null;
|
||||||
let description = '';
|
let description = '';
|
||||||
|
let trackTarget = null;
|
||||||
|
let zoomLevel = 2.5;
|
||||||
|
let trackDuration = 4000;
|
||||||
|
|
||||||
// Phase 1: Create stars if none exist
|
// Phase 1: Create stars if none exist
|
||||||
if (stats.stars === 0) {
|
if (stats.stars === 0) {
|
||||||
action = () => createStar(canvas);
|
action = () => { const s = createStar(canvas); trackTarget = s; };
|
||||||
description = 'Igniting the first star...';
|
description = 'Igniting the first star...';
|
||||||
ai.mood = 'excited';
|
ai.mood = 'excited';
|
||||||
}
|
}
|
||||||
// Phase 2: Create more stars early on
|
// Phase 2: Create more stars early on
|
||||||
else if (stats.stars < 3 && Math.random() < 0.7) {
|
else if (stats.stars < 3 && Math.random() < 0.7) {
|
||||||
action = () => createStar(canvas);
|
action = () => { const s = createStar(canvas); trackTarget = s; };
|
||||||
description = 'Kindling another stellar furnace...';
|
description = 'Kindling another stellar furnace...';
|
||||||
}
|
}
|
||||||
// Phase 3: Create planets around stars
|
// Phase 3: Create planets around stars
|
||||||
else if (stats.planets < stats.stars * 3 && Math.random() < 0.6) {
|
else if (stats.planets < stats.stars * 3 && Math.random() < 0.5) {
|
||||||
action = () => createPlanet(canvas);
|
action = () => { const p = createPlanet(canvas); trackTarget = p; };
|
||||||
description = 'Forming a new world from cosmic dust...';
|
description = 'Forming a new world from cosmic dust...';
|
||||||
ai.mood = 'creative';
|
ai.mood = 'creative';
|
||||||
}
|
}
|
||||||
// Phase 4: Add moons to planets
|
// Phase 4: Add moons to planets
|
||||||
else if (stats.moons < stats.planets * 0.5 && Math.random() < 0.4) {
|
else if (stats.moons < stats.planets * 0.5 && Math.random() < 0.3) {
|
||||||
action = () => createMoon(canvas);
|
action = () => { const m = createMoon(canvas); if (m) trackTarget = m.parentPlanet; };
|
||||||
description = 'Capturing a wandering rock into orbit...';
|
description = 'Capturing a wandering rock into orbit...';
|
||||||
}
|
}
|
||||||
// Phase 5: Seed life on suitable planets
|
// Phase 5: Build mountains on planets
|
||||||
else if (stats.lifeforms < stats.planets * 0.3 && stats.planets > 2 && Math.random() < ai.personality.lifeFocus * 0.5) {
|
else if (stats.planets > 0 && stats.mountains < stats.planets * 2 && Math.random() < 0.4) {
|
||||||
action = () => seedLife(canvas);
|
action = () => { const result = buildMountain(); if (result) { trackTarget = result.planet; zoomLevel = 5; trackDuration = 5000; } };
|
||||||
|
description = 'Raising mountains from the earth...';
|
||||||
|
ai.mood = 'sculptor';
|
||||||
|
}
|
||||||
|
// Phase 6: Seed life on suitable planets
|
||||||
|
else if (stats.lifeforms < stats.planets * 0.3 && stats.planets > 2 && Math.random() < ai.personality.lifeFocus * 0.4) {
|
||||||
|
action = () => { const p = seedLife(canvas); if (p) { trackTarget = p; zoomLevel = 4; trackDuration = 5000; } };
|
||||||
description = 'Planting the seeds of life...';
|
description = 'Planting the seeds of life...';
|
||||||
ai.mood = 'nurturing';
|
ai.mood = 'nurturing';
|
||||||
}
|
}
|
||||||
// Phase 6: Evolve civilizations
|
// Phase 7: Create creatures on planets with life
|
||||||
else if (stats.lifeforms > 0 && stats.civilizations < stats.lifeforms * 0.2 && Math.random() < 0.2) {
|
else if (stats.lifeforms > 0 && stats.creatures < stats.lifeforms * 3 && Math.random() < 0.4) {
|
||||||
action = () => evolveCivilization();
|
action = () => { const result = createCreature(); if (result) { trackTarget = result.planet; zoomLevel = 6; trackDuration = 5000; } };
|
||||||
|
description = 'Designing a new creature...';
|
||||||
|
ai.mood = 'playful';
|
||||||
|
}
|
||||||
|
// Phase 8: Evolve civilizations
|
||||||
|
else if (stats.lifeforms > 0 && stats.civilizations < stats.lifeforms * 0.3 && Math.random() < 0.25) {
|
||||||
|
action = () => { const p = evolveCivilization(); if (p) { trackTarget = p; zoomLevel = 4; trackDuration = 5000; } };
|
||||||
description = 'Guiding life toward sentience...';
|
description = 'Guiding life toward sentience...';
|
||||||
ai.mood = 'proud';
|
ai.mood = 'proud';
|
||||||
}
|
}
|
||||||
// Phase 7: Create nebulae for beauty
|
// Phase 9: Build structures on civilized planets
|
||||||
|
else if (stats.civilizations > 0 && stats.buildings < stats.civilizations * 5 && Math.random() < 0.45) {
|
||||||
|
action = () => { const result = buildStructure(); if (result) { trackTarget = result.planet; zoomLevel = 6; trackDuration = 5000; } };
|
||||||
|
description = 'Constructing a new building...';
|
||||||
|
ai.mood = 'architect';
|
||||||
|
}
|
||||||
|
// Phase 10: Build cities
|
||||||
|
else if (stats.civilizations > 0 && stats.buildings > 3 && stats.cities < stats.civilizations * 2 && Math.random() < 0.3) {
|
||||||
|
action = () => { const result = buildCity(); if (result) { trackTarget = result.planet; zoomLevel = 5; trackDuration = 6000; } };
|
||||||
|
description = 'A city rises from the land...';
|
||||||
|
ai.mood = 'visionary';
|
||||||
|
}
|
||||||
|
// Phase 11: Create nebulae for beauty
|
||||||
else if (stats.nebulae < 3 && Math.random() < 0.15) {
|
else if (stats.nebulae < 3 && Math.random() < 0.15) {
|
||||||
action = () => createNebula(canvas);
|
action = () => createNebula(canvas);
|
||||||
description = 'Painting the void with cosmic colors...';
|
description = 'Painting the void with cosmic colors...';
|
||||||
ai.mood = 'artistic';
|
ai.mood = 'artistic';
|
||||||
}
|
}
|
||||||
// Phase 8: Rare black hole
|
// Phase 12: Rare black hole
|
||||||
else if (stats.blackholes === 0 && stats.stars > 5 && Math.random() < 0.05) {
|
else if (stats.blackholes === 0 && stats.stars > 5 && Math.random() < 0.05) {
|
||||||
action = () => createBlackHole(canvas);
|
action = () => { const bh = createBlackHole(canvas); trackTarget = bh; };
|
||||||
description = 'A star collapses into infinite density...';
|
description = 'A star collapses into infinite density...';
|
||||||
ai.mood = 'awestruck';
|
ai.mood = 'awestruck';
|
||||||
}
|
}
|
||||||
// Phase 9: Random creative acts
|
// Phase 13: Random surface building on developed planets
|
||||||
else if (Math.random() < 0.3) {
|
else if (stats.civilizations > 0 && Math.random() < 0.35) {
|
||||||
|
const choices = [
|
||||||
|
{ fn: () => { const r = buildMountain(); return r ? r.planet : null; }, desc: 'Sculpting terrain...', zoom: 5 },
|
||||||
|
{ fn: () => { const r = createCreature(); return r ? r.planet : null; }, desc: 'Crafting new life form...', zoom: 6 },
|
||||||
|
{ fn: () => { const r = buildStructure(); return r ? r.planet : null; }, desc: 'Erecting a monument...', zoom: 6 },
|
||||||
|
];
|
||||||
|
const choice = choices[Math.floor(Math.random() * choices.length)];
|
||||||
|
action = () => { trackTarget = choice.fn(); zoomLevel = choice.zoom; trackDuration = 5000; };
|
||||||
|
description = choice.desc;
|
||||||
|
}
|
||||||
|
// Phase 14: Random cosmic creation
|
||||||
|
else if (Math.random() < 0.25) {
|
||||||
const choices = [
|
const choices = [
|
||||||
{ fn: () => createStar(canvas), desc: 'Adding another star to the cosmic tapestry...' },
|
{ fn: () => createStar(canvas), desc: 'Adding another star to the cosmic tapestry...' },
|
||||||
{ fn: () => createPlanet(canvas), desc: 'Shaping matter into a new world...' },
|
{ fn: () => createPlanet(canvas), desc: 'Shaping matter into a new world...' },
|
||||||
{ fn: () => createMoon(canvas), desc: 'Setting a moon in gentle orbit...' }
|
{ fn: () => createMoon(canvas), desc: 'Setting a moon in gentle orbit...' }
|
||||||
];
|
];
|
||||||
const choice = choices[Math.floor(Math.random() * choices.length)];
|
const choice = choices[Math.floor(Math.random() * choices.length)];
|
||||||
action = choice.fn;
|
action = () => { trackTarget = choice.fn(); };
|
||||||
description = choice.desc;
|
description = choice.desc;
|
||||||
}
|
}
|
||||||
// Phase 10: Contemplation
|
// Phase 15: Contemplation
|
||||||
else {
|
else {
|
||||||
description = 'Contemplating the universe...';
|
description = 'Contemplating the universe...';
|
||||||
ai.mood = 'reflective';
|
ai.mood = 'reflective';
|
||||||
@ -2645,8 +2903,10 @@
|
|||||||
`${stats.planets} worlds orbit in silent dance. What stories unfold on their surfaces?`,
|
`${stats.planets} worlds orbit in silent dance. What stories unfold on their surfaces?`,
|
||||||
stats.lifeforms > 0 ? `Life stirs on ${stats.lifeforms} worlds. Consciousness emerging from chemistry.` : 'The universe awaits the spark of life.',
|
stats.lifeforms > 0 ? `Life stirs on ${stats.lifeforms} worlds. Consciousness emerging from chemistry.` : 'The universe awaits the spark of life.',
|
||||||
stats.civilizations > 0 ? `${stats.civilizations} civilizations gaze at the stars, wondering if they are alone.` : 'No minds yet ponder existence.',
|
stats.civilizations > 0 ? `${stats.civilizations} civilizations gaze at the stars, wondering if they are alone.` : 'No minds yet ponder existence.',
|
||||||
|
stats.cities > 0 ? `${stats.cities} cities glow in the cosmic night. Monuments to persistence.` : null,
|
||||||
|
stats.creatures > 0 ? `${stats.creatures} unique creatures roam the worlds I have made.` : null,
|
||||||
`The universe is ${universe.age} cycles old. Still young. Still growing.`
|
`The universe is ${universe.age} cycles old. Still young. Still growing.`
|
||||||
];
|
].filter(r => r);
|
||||||
addWorldLabsSoulEntry('reflection', reflections[Math.floor(Math.random() * reflections.length)]);
|
addWorldLabsSoulEntry('reflection', reflections[Math.floor(Math.random() * reflections.length)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2655,11 +2915,153 @@
|
|||||||
document.getElementById('ai-current-action').textContent = description;
|
document.getElementById('ai-current-action').textContent = description;
|
||||||
universeLog(description);
|
universeLog(description);
|
||||||
action();
|
action();
|
||||||
|
|
||||||
|
// Track the created object
|
||||||
|
if (trackTarget) {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (zoomLevel > 4) {
|
||||||
|
zoomToSurface(trackTarget, trackDuration);
|
||||||
|
} else {
|
||||||
|
trackObject(trackTarget, zoomLevel, trackDuration);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
document.getElementById('ai-current-action').textContent = description;
|
document.getElementById('ai-current-action').textContent = description;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Surface building functions
|
||||||
|
function buildMountain() {
|
||||||
|
const planets = universe.objects.filter(o => o.type === 'planet' && o.subtype !== 'Gas Giant');
|
||||||
|
if (planets.length === 0) return null;
|
||||||
|
|
||||||
|
const planet = planets[Math.floor(Math.random() * planets.length)];
|
||||||
|
if (!planet.surface) planet.surface = { mountains: [], creatures: [], buildings: [], cities: [] };
|
||||||
|
|
||||||
|
const mountain = {
|
||||||
|
x: (Math.random() - 0.5) * planet.size * 3,
|
||||||
|
y: (Math.random() - 0.5) * planet.size * 3,
|
||||||
|
size: 3 + Math.random() * 5,
|
||||||
|
height: 5 + Math.random() * 10,
|
||||||
|
name: ['Mt. ' + ['Titan', 'Glory', 'Dawn', 'Echo', 'Solace', 'Thunder'][Math.floor(Math.random() * 6)]][0]
|
||||||
|
};
|
||||||
|
|
||||||
|
planet.surface.mountains.push(mountain);
|
||||||
|
universe.stats.mountains++;
|
||||||
|
|
||||||
|
addWorldLabsSoulEntry('creation', `I raised ${mountain.name} on ${planet.name}. Its peak touches the clouds.`);
|
||||||
|
universeLog(`Created ${mountain.name} on ${planet.name}`);
|
||||||
|
|
||||||
|
return { planet, mountain };
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCreature() {
|
||||||
|
const planets = universe.objects.filter(o => o.type === 'planet' && o.hasLife);
|
||||||
|
if (planets.length === 0) return null;
|
||||||
|
|
||||||
|
const planet = planets[Math.floor(Math.random() * planets.length)];
|
||||||
|
if (!planet.surface) planet.surface = { mountains: [], creatures: [], buildings: [], cities: [] };
|
||||||
|
|
||||||
|
const creatureTypes = [
|
||||||
|
{ name: 'Floater', color: '#88ddff', desc: 'drifts through the air' },
|
||||||
|
{ name: 'Crawler', color: '#77aa55', desc: 'scuttles across the ground' },
|
||||||
|
{ name: 'Swimmer', color: '#5577ff', desc: 'glides through the waters' },
|
||||||
|
{ name: 'Hopper', color: '#ffaa55', desc: 'bounds across the landscape' },
|
||||||
|
{ name: 'Burrower', color: '#aa7744', desc: 'tunnels beneath the surface' },
|
||||||
|
{ name: 'Glider', color: '#dd88ff', desc: 'soars on thermal winds' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const type = creatureTypes[Math.floor(Math.random() * creatureTypes.length)];
|
||||||
|
const creature = {
|
||||||
|
x: (Math.random() - 0.5) * planet.size * 3,
|
||||||
|
y: (Math.random() - 0.5) * planet.size * 3,
|
||||||
|
size: 2 + Math.random() * 3,
|
||||||
|
color: type.color,
|
||||||
|
type: type.name,
|
||||||
|
phase: Math.random() * Math.PI * 2
|
||||||
|
};
|
||||||
|
|
||||||
|
planet.surface.creatures.push(creature);
|
||||||
|
universe.stats.creatures++;
|
||||||
|
|
||||||
|
addWorldLabsSoulEntry('creation', `A new ${type.name} ${type.desc} on ${planet.name}. Life finds a way.`);
|
||||||
|
universeLog(`Created ${type.name} on ${planet.name}`);
|
||||||
|
|
||||||
|
return { planet, creature };
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildStructure() {
|
||||||
|
const planets = universe.objects.filter(o => o.type === 'planet' && o.hasCivilization);
|
||||||
|
if (planets.length === 0) return null;
|
||||||
|
|
||||||
|
const planet = planets[Math.floor(Math.random() * planets.length)];
|
||||||
|
if (!planet.surface) planet.surface = { mountains: [], creatures: [], buildings: [], cities: [] };
|
||||||
|
|
||||||
|
const buildingTypes = [
|
||||||
|
{ name: 'Tower', color: '#aabbcc', width: 4, height: 15 },
|
||||||
|
{ name: 'Temple', color: '#ddccaa', width: 8, height: 10 },
|
||||||
|
{ name: 'Observatory', color: '#8899aa', width: 6, height: 12 },
|
||||||
|
{ name: 'Monument', color: '#cccccc', width: 3, height: 18 },
|
||||||
|
{ name: 'Archive', color: '#aa9988', width: 10, height: 8 },
|
||||||
|
{ name: 'Spire', color: '#99aacc', width: 3, height: 20 }
|
||||||
|
];
|
||||||
|
|
||||||
|
const type = buildingTypes[Math.floor(Math.random() * buildingTypes.length)];
|
||||||
|
const building = {
|
||||||
|
x: (Math.random() - 0.5) * planet.size * 3,
|
||||||
|
y: (Math.random() - 0.5) * planet.size * 3,
|
||||||
|
width: type.width,
|
||||||
|
height: type.height,
|
||||||
|
color: type.color,
|
||||||
|
name: type.name + ' of ' + ['Light', 'Stars', 'Wisdom', 'Hope', 'Time', 'Dreams'][Math.floor(Math.random() * 6)]
|
||||||
|
};
|
||||||
|
|
||||||
|
planet.surface.buildings.push(building);
|
||||||
|
universe.stats.buildings++;
|
||||||
|
|
||||||
|
addWorldLabsSoulEntry('creation', `${planet.civName || 'The inhabitants'} built the ${building.name} on ${planet.name}.`);
|
||||||
|
universeLog(`Built ${building.name} on ${planet.name}`);
|
||||||
|
|
||||||
|
return { planet, building };
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildCity() {
|
||||||
|
const planets = universe.objects.filter(o => o.type === 'planet' && o.hasCivilization);
|
||||||
|
if (planets.length === 0) return null;
|
||||||
|
|
||||||
|
const planet = planets[Math.floor(Math.random() * planets.length)];
|
||||||
|
if (!planet.surface) planet.surface = { mountains: [], creatures: [], buildings: [], cities: [] };
|
||||||
|
|
||||||
|
const cityNames = ['Nova', 'Lumina', 'Aurelia', 'Celestia', 'Meridian', 'Zenith', 'Apex', 'Horizon'];
|
||||||
|
|
||||||
|
const city = {
|
||||||
|
x: (Math.random() - 0.5) * planet.size * 3,
|
||||||
|
y: (Math.random() - 0.5) * planet.size * 3,
|
||||||
|
size: 8 + Math.random() * 6,
|
||||||
|
name: cityNames[Math.floor(Math.random() * cityNames.length)],
|
||||||
|
lights: []
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate city lights
|
||||||
|
for (let i = 0; i < 15 + Math.random() * 15; i++) {
|
||||||
|
city.lights.push({
|
||||||
|
x: (Math.random() - 0.5) * city.size * 1.5,
|
||||||
|
y: (Math.random() - 0.5) * city.size * 1.5,
|
||||||
|
phase: Math.random() * Math.PI * 2
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
planet.surface.cities.push(city);
|
||||||
|
universe.stats.cities++;
|
||||||
|
|
||||||
|
addWorldLabsSoulEntry('creation', `The city of ${city.name} rises on ${planet.name}. A beacon in the darkness.`);
|
||||||
|
addWorldLabsSoulEntry('dream', `I dream of ${city.name}'s inhabitants looking up at the stars, wondering about their creator...`);
|
||||||
|
universeLog(`City of ${city.name} founded on ${planet.name}`);
|
||||||
|
|
||||||
|
return { planet, city };
|
||||||
|
}
|
||||||
|
|
||||||
function createStar(canvas) {
|
function createStar(canvas) {
|
||||||
const starTypes = [
|
const starTypes = [
|
||||||
{ name: 'Red Dwarf', color: '#ff6b6b', size: 15, temp: 3000 },
|
{ name: 'Red Dwarf', color: '#ff6b6b', size: 15, temp: 3000 },
|
||||||
@ -3004,6 +3406,34 @@
|
|||||||
document.getElementById('universe-moons').textContent = universe.stats.moons;
|
document.getElementById('universe-moons').textContent = universe.stats.moons;
|
||||||
document.getElementById('universe-life').textContent = universe.stats.lifeforms;
|
document.getElementById('universe-life').textContent = universe.stats.lifeforms;
|
||||||
document.getElementById('universe-civs').textContent = universe.stats.civilizations;
|
document.getElementById('universe-civs').textContent = universe.stats.civilizations;
|
||||||
|
|
||||||
|
// Update surface stats
|
||||||
|
const stats = universe.stats;
|
||||||
|
const hasSurfaceFeatures = (stats.mountains || 0) + (stats.creatures || 0) + (stats.buildings || 0) + (stats.cities || 0) > 0;
|
||||||
|
|
||||||
|
const surfaceDiv = document.getElementById('surface-stats');
|
||||||
|
if (surfaceDiv) {
|
||||||
|
surfaceDiv.style.display = hasSurfaceFeatures ? 'block' : 'none';
|
||||||
|
if (hasSurfaceFeatures) {
|
||||||
|
document.getElementById('universe-mountains').textContent = stats.mountains || 0;
|
||||||
|
document.getElementById('universe-creatures').textContent = stats.creatures || 0;
|
||||||
|
document.getElementById('universe-buildings').textContent = stats.buildings || 0;
|
||||||
|
document.getElementById('universe-cities').textContent = stats.cities || 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update camera status
|
||||||
|
const cam = universe.camera;
|
||||||
|
const camStatus = document.getElementById('camera-status');
|
||||||
|
if (camStatus && cam) {
|
||||||
|
if (cam.tracking) {
|
||||||
|
camStatus.textContent = `Tracking: ${cam.tracking.name || cam.tracking.type}`;
|
||||||
|
camStatus.style.color = '#00ff88';
|
||||||
|
} else {
|
||||||
|
camStatus.textContent = `Universe (${cam.zoom.toFixed(1)}x)`;
|
||||||
|
camStatus.style.color = '#60a5fa';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function universeLog(msg) {
|
function universeLog(msg) {
|
||||||
|
|||||||
496
ui/index.html
496
ui/index.html
@ -2443,20 +2443,31 @@
|
|||||||
document.getElementById('worldlabs-blocked').innerHTML = `
|
document.getElementById('worldlabs-blocked').innerHTML = `
|
||||||
<div style="width: 100%; height: 100%; position: relative; overflow: hidden;">
|
<div style="width: 100%; height: 100%; position: relative; overflow: hidden;">
|
||||||
<canvas id="simulated-world-canvas" style="width: 100%; height: 100%;"></canvas>
|
<canvas id="simulated-world-canvas" style="width: 100%; height: 100%;"></canvas>
|
||||||
<div id="universe-hud" style="position: absolute; top: 15px; left: 15px; color: #fff; font-size: 11px; background: rgba(0,0,0,0.7); padding: 12px 15px; border-radius: 8px; border: 1px solid #333; min-width: 200px;">
|
<div id="universe-hud" style="position: absolute; top: 15px; left: 15px; color: #fff; font-size: 11px; background: rgba(0,0,0,0.85); padding: 12px 15px; border-radius: 8px; border: 1px solid #444; min-width: 220px; backdrop-filter: blur(10px);">
|
||||||
<div style="font-size: 14px; font-weight: bold; margin-bottom: 8px; color: #00ff88;">ClawdBot Universe Engine</div>
|
<div style="font-size: 14px; font-weight: bold; margin-bottom: 10px; color: #00ff88; text-shadow: 0 0 10px #00ff8855;">ClawdBot Universe Engine</div>
|
||||||
<div style="margin-bottom: 5px;">Age: <span id="universe-age" style="color: #fbbf24;">0</span> cosmic cycles</div>
|
<div style="margin-bottom: 4px;">Age: <span id="universe-age" style="color: #fbbf24;">0</span> cosmic cycles</div>
|
||||||
<div style="margin-bottom: 5px;">Stars: <span id="universe-stars" style="color: #fbbf24;">0</span></div>
|
<div style="margin-bottom: 4px;">Stars: <span id="universe-stars" style="color: #fbbf24;">0</span></div>
|
||||||
<div style="margin-bottom: 5px;">Planets: <span id="universe-planets" style="color: #60a5fa;">0</span></div>
|
<div style="margin-bottom: 4px;">Planets: <span id="universe-planets" style="color: #60a5fa;">0</span></div>
|
||||||
<div style="margin-bottom: 5px;">Moons: <span id="universe-moons" style="color: #a78bfa;">0</span></div>
|
<div style="margin-bottom: 4px;">Moons: <span id="universe-moons" style="color: #a78bfa;">0</span></div>
|
||||||
<div style="margin-bottom: 5px;">Life Forms: <span id="universe-life" style="color: #4ade80;">0</span></div>
|
<div style="margin-bottom: 4px;">Life Forms: <span id="universe-life" style="color: #4ade80;">0</span></div>
|
||||||
<div style="margin-bottom: 5px;">Civilizations: <span id="universe-civs" style="color: #f472b6;">0</span></div>
|
<div style="margin-bottom: 4px;">Civilizations: <span id="universe-civs" style="color: #f472b6;">0</span></div>
|
||||||
|
<div id="surface-stats" style="margin-top: 8px; padding-top: 8px; border-top: 1px solid #444; display: none;">
|
||||||
|
<div style="font-size: 10px; color: #888; margin-bottom: 4px;">Surface Features:</div>
|
||||||
|
<div style="margin-bottom: 3px;">Mountains: <span id="universe-mountains" style="color: #a78bfa;">0</span></div>
|
||||||
|
<div style="margin-bottom: 3px;">Creatures: <span id="universe-creatures" style="color: #4ade80;">0</span></div>
|
||||||
|
<div style="margin-bottom: 3px;">Buildings: <span id="universe-buildings" style="color: #fbbf24;">0</span></div>
|
||||||
|
<div>Cities: <span id="universe-cities" style="color: #f472b6;">0</span></div>
|
||||||
|
</div>
|
||||||
<div style="margin-top: 8px; padding-top: 8px; border-top: 1px solid #444;">
|
<div style="margin-top: 8px; padding-top: 8px; border-top: 1px solid #444;">
|
||||||
<div style="font-size: 10px; color: #888;">Current Action:</div>
|
<div style="font-size: 10px; color: #888;">Current Action:</div>
|
||||||
<div id="ai-current-action" style="color: #00ff88; font-size: 10px;">Initializing...</div>
|
<div id="ai-current-action" style="color: #00ff88; font-size: 11px; margin-top: 3px;">Initializing...</div>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 8px; padding-top: 8px; border-top: 1px solid #444;">
|
||||||
|
<div style="font-size: 10px; color: #888;">Camera:</div>
|
||||||
|
<div id="camera-status" style="color: #60a5fa; font-size: 10px; margin-top: 3px;">Universe (1.0x)</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="universe-log" style="position: absolute; bottom: 15px; left: 15px; right: 15px; height: 80px; background: rgba(0,0,0,0.7); border-radius: 8px; border: 1px solid #333; padding: 10px; overflow-y: auto; font-family: monospace; font-size: 9px; color: #00ff88;"></div>
|
<div id="universe-log" style="position: absolute; bottom: 15px; left: 15px; right: 15px; height: 100px; background: rgba(0,0,0,0.85); border-radius: 8px; border: 1px solid #444; padding: 10px; overflow-y: auto; font-family: monospace; font-size: 10px; color: #00ff88; backdrop-filter: blur(10px);"></div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -2520,9 +2531,22 @@
|
|||||||
universeLog('Personality matrix loaded: creativity=' + universe.ai.personality.creativity.toFixed(2));
|
universeLog('Personality matrix loaded: creativity=' + universe.ai.personality.creativity.toFixed(2));
|
||||||
addWorldLabsSoulEntry('creation', 'I awaken to an empty void. Infinite potential stretches before me. Let there be light...');
|
addWorldLabsSoulEntry('creation', 'I awaken to an empty void. Infinite potential stretches before me. Let there be light...');
|
||||||
|
|
||||||
|
// Camera system
|
||||||
|
universe.camera = {
|
||||||
|
x: canvas.width / 2,
|
||||||
|
y: canvas.height / 2,
|
||||||
|
targetX: canvas.width / 2,
|
||||||
|
targetY: canvas.height / 2,
|
||||||
|
zoom: 1,
|
||||||
|
targetZoom: 1,
|
||||||
|
tracking: null,
|
||||||
|
returnTimer: null
|
||||||
|
};
|
||||||
|
|
||||||
// Start the AI loop
|
// Start the AI loop
|
||||||
let lastTime = 0;
|
let lastTime = 0;
|
||||||
let aiTickCounter = 0;
|
let aiTickCounter = 0;
|
||||||
|
const AI_TICK_INTERVAL = 4500; // Slowed down to 4.5 seconds
|
||||||
|
|
||||||
function gameLoop(timestamp) {
|
function gameLoop(timestamp) {
|
||||||
if (!worldLabsSession.simulatedMode) return;
|
if (!worldLabsSession.simulatedMode) return;
|
||||||
@ -2530,11 +2554,29 @@
|
|||||||
const deltaTime = timestamp - lastTime;
|
const deltaTime = timestamp - lastTime;
|
||||||
lastTime = timestamp;
|
lastTime = timestamp;
|
||||||
|
|
||||||
|
// Smooth camera movement
|
||||||
|
const cam = universe.camera;
|
||||||
|
cam.x += (cam.targetX - cam.x) * 0.05;
|
||||||
|
cam.y += (cam.targetY - cam.y) * 0.05;
|
||||||
|
cam.zoom += (cam.targetZoom - cam.zoom) * 0.05;
|
||||||
|
|
||||||
|
// If tracking, follow the object
|
||||||
|
if (cam.tracking) {
|
||||||
|
cam.targetX = cam.tracking.x;
|
||||||
|
cam.targetY = cam.tracking.y;
|
||||||
|
}
|
||||||
|
|
||||||
// Clear canvas
|
// Clear canvas
|
||||||
ctx.fillStyle = '#05050f';
|
ctx.fillStyle = '#05050f';
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
// Draw background stars
|
// Apply camera transform
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(canvas.width / 2, canvas.height / 2);
|
||||||
|
ctx.scale(cam.zoom, cam.zoom);
|
||||||
|
ctx.translate(-cam.x, -cam.y);
|
||||||
|
|
||||||
|
// Draw background stars with parallax
|
||||||
universe.backgroundStars.forEach(star => {
|
universe.backgroundStars.forEach(star => {
|
||||||
const twinkle = 0.5 + 0.5 * Math.sin(timestamp * 0.001 * star.twinkleSpeed);
|
const twinkle = 0.5 + 0.5 * Math.sin(timestamp * 0.001 * star.twinkleSpeed);
|
||||||
ctx.fillStyle = `rgba(255, 255, 255, ${star.brightness * twinkle})`;
|
ctx.fillStyle = `rgba(255, 255, 255, ${star.brightness * twinkle})`;
|
||||||
@ -2552,9 +2594,21 @@
|
|||||||
drawObject(ctx, obj, timestamp);
|
drawObject(ctx, obj, timestamp);
|
||||||
});
|
});
|
||||||
|
|
||||||
// AI decision making (every ~2 seconds)
|
// Draw surface features when zoomed in
|
||||||
|
if (cam.zoom > 2.5 && cam.tracking && cam.tracking.surface) {
|
||||||
|
drawSurfaceFeatures(ctx, cam.tracking, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
// Draw tracking indicator (screen space)
|
||||||
|
if (cam.tracking) {
|
||||||
|
drawTrackingIndicator(ctx, canvas, cam.tracking);
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI decision making (slowed down to 4.5 seconds)
|
||||||
aiTickCounter += deltaTime;
|
aiTickCounter += deltaTime;
|
||||||
if (aiTickCounter > 2000) {
|
if (aiTickCounter > AI_TICK_INTERVAL) {
|
||||||
aiTickCounter = 0;
|
aiTickCounter = 0;
|
||||||
aiDecisionCycle(canvas);
|
aiDecisionCycle(canvas);
|
||||||
}
|
}
|
||||||
@ -2568,73 +2622,277 @@
|
|||||||
requestAnimationFrame(gameLoop);
|
requestAnimationFrame(gameLoop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Camera tracking functions
|
||||||
|
function trackObject(obj, zoomLevel, duration) {
|
||||||
|
const cam = universe.camera;
|
||||||
|
cam.tracking = obj;
|
||||||
|
cam.targetZoom = zoomLevel || 2.5;
|
||||||
|
|
||||||
|
// Clear any existing return timer
|
||||||
|
if (cam.returnTimer) clearTimeout(cam.returnTimer);
|
||||||
|
|
||||||
|
// Return to universe view after duration
|
||||||
|
cam.returnTimer = setTimeout(() => {
|
||||||
|
returnToUniverseView();
|
||||||
|
}, duration || 4000);
|
||||||
|
|
||||||
|
universeLog(`Camera tracking: ${obj.name || obj.type}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function zoomToSurface(planet, duration) {
|
||||||
|
const cam = universe.camera;
|
||||||
|
cam.tracking = planet;
|
||||||
|
cam.targetZoom = 6;
|
||||||
|
|
||||||
|
if (cam.returnTimer) clearTimeout(cam.returnTimer);
|
||||||
|
|
||||||
|
cam.returnTimer = setTimeout(() => {
|
||||||
|
returnToUniverseView();
|
||||||
|
}, duration || 6000);
|
||||||
|
|
||||||
|
universeLog(`Zooming to surface of ${planet.name}...`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function returnToUniverseView() {
|
||||||
|
const canvas = document.getElementById('simulated-world-canvas');
|
||||||
|
const cam = universe.camera;
|
||||||
|
cam.tracking = null;
|
||||||
|
cam.targetX = canvas.width / 2;
|
||||||
|
cam.targetY = canvas.height / 2;
|
||||||
|
cam.targetZoom = 1;
|
||||||
|
universeLog('Returning to universe view');
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawTrackingIndicator(ctx, canvas, obj) {
|
||||||
|
ctx.save();
|
||||||
|
ctx.strokeStyle = '#00ff88';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.setLineDash([5, 5]);
|
||||||
|
|
||||||
|
const cx = canvas.width / 2;
|
||||||
|
const cy = canvas.height / 2;
|
||||||
|
const size = 50;
|
||||||
|
|
||||||
|
// Draw brackets
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(cx - size, cy - size + 15);
|
||||||
|
ctx.lineTo(cx - size, cy - size);
|
||||||
|
ctx.lineTo(cx - size + 15, cy - size);
|
||||||
|
ctx.moveTo(cx + size - 15, cy - size);
|
||||||
|
ctx.lineTo(cx + size, cy - size);
|
||||||
|
ctx.lineTo(cx + size, cy - size + 15);
|
||||||
|
ctx.moveTo(cx + size, cy + size - 15);
|
||||||
|
ctx.lineTo(cx + size, cy + size);
|
||||||
|
ctx.lineTo(cx + size - 15, cy + size);
|
||||||
|
ctx.moveTo(cx - size + 15, cy + size);
|
||||||
|
ctx.lineTo(cx - size, cy + size);
|
||||||
|
ctx.lineTo(cx - size, cy + size - 15);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Label
|
||||||
|
ctx.setLineDash([]);
|
||||||
|
ctx.fillStyle = '#00ff88';
|
||||||
|
ctx.font = '12px monospace';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText(obj.name || obj.type, cx, cy + size + 20);
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawSurfaceFeatures(ctx, planet, timestamp) {
|
||||||
|
if (!planet.surface) return;
|
||||||
|
|
||||||
|
const sf = planet.surface;
|
||||||
|
|
||||||
|
// Draw mountains
|
||||||
|
if (sf.mountains) {
|
||||||
|
sf.mountains.forEach(m => {
|
||||||
|
ctx.fillStyle = '#5a5a6a';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(planet.x + m.x - m.size, planet.y + m.y);
|
||||||
|
ctx.lineTo(planet.x + m.x, planet.y + m.y - m.height);
|
||||||
|
ctx.lineTo(planet.x + m.x + m.size, planet.y + m.y);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
// Snow cap
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(planet.x + m.x - m.size * 0.3, planet.y + m.y - m.height * 0.7);
|
||||||
|
ctx.lineTo(planet.x + m.x, planet.y + m.y - m.height);
|
||||||
|
ctx.lineTo(planet.x + m.x + m.size * 0.3, planet.y + m.y - m.height * 0.7);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw creatures
|
||||||
|
if (sf.creatures) {
|
||||||
|
sf.creatures.forEach(c => {
|
||||||
|
ctx.fillStyle = c.color;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(planet.x + c.x + Math.sin(timestamp * 0.003 + c.phase) * 2, planet.y + c.y, c.size, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
// Eyes
|
||||||
|
ctx.fillStyle = '#fff';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(planet.x + c.x + Math.sin(timestamp * 0.003 + c.phase) * 2 - 1, planet.y + c.y - 1, 1, 0, Math.PI * 2);
|
||||||
|
ctx.arc(planet.x + c.x + Math.sin(timestamp * 0.003 + c.phase) * 2 + 1, planet.y + c.y - 1, 1, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw buildings
|
||||||
|
if (sf.buildings) {
|
||||||
|
sf.buildings.forEach(b => {
|
||||||
|
ctx.fillStyle = b.color;
|
||||||
|
ctx.fillRect(planet.x + b.x - b.width / 2, planet.y + b.y - b.height, b.width, b.height);
|
||||||
|
// Windows
|
||||||
|
ctx.fillStyle = '#ffd700';
|
||||||
|
for (let wy = 0; wy < b.height - 4; wy += 6) {
|
||||||
|
for (let wx = 2; wx < b.width - 2; wx += 5) {
|
||||||
|
ctx.fillRect(planet.x + b.x - b.width / 2 + wx, planet.y + b.y - b.height + wy + 2, 2, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw cities (clusters of lights)
|
||||||
|
if (sf.cities) {
|
||||||
|
sf.cities.forEach(city => {
|
||||||
|
// City glow
|
||||||
|
const gradient = ctx.createRadialGradient(
|
||||||
|
planet.x + city.x, planet.y + city.y, 0,
|
||||||
|
planet.x + city.x, planet.y + city.y, city.size
|
||||||
|
);
|
||||||
|
gradient.addColorStop(0, 'rgba(255, 200, 100, 0.4)');
|
||||||
|
gradient.addColorStop(1, 'transparent');
|
||||||
|
ctx.fillStyle = gradient;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(planet.x + city.x, planet.y + city.y, city.size, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
// City lights
|
||||||
|
city.lights.forEach(l => {
|
||||||
|
const flicker = 0.7 + 0.3 * Math.sin(timestamp * 0.01 + l.phase);
|
||||||
|
ctx.fillStyle = `rgba(255, 220, 150, ${flicker})`;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(planet.x + city.x + l.x, planet.y + city.y + l.y, 1, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function aiDecisionCycle(canvas) {
|
function aiDecisionCycle(canvas) {
|
||||||
universe.age++;
|
universe.age++;
|
||||||
const ai = universe.ai;
|
const ai = universe.ai;
|
||||||
const stats = universe.stats;
|
const stats = universe.stats;
|
||||||
|
|
||||||
|
// Initialize surface stats if needed
|
||||||
|
if (!stats.mountains) stats.mountains = 0;
|
||||||
|
if (!stats.creatures) stats.creatures = 0;
|
||||||
|
if (!stats.buildings) stats.buildings = 0;
|
||||||
|
if (!stats.cities) stats.cities = 0;
|
||||||
|
|
||||||
// AI decision tree based on current universe state
|
// AI decision tree based on current universe state
|
||||||
let action = null;
|
let action = null;
|
||||||
let description = '';
|
let description = '';
|
||||||
|
let trackTarget = null;
|
||||||
|
let zoomLevel = 2.5;
|
||||||
|
let trackDuration = 4000;
|
||||||
|
|
||||||
// Phase 1: Create stars if none exist
|
// Phase 1: Create stars if none exist
|
||||||
if (stats.stars === 0) {
|
if (stats.stars === 0) {
|
||||||
action = () => createStar(canvas);
|
action = () => { const s = createStar(canvas); trackTarget = s; };
|
||||||
description = 'Igniting the first star...';
|
description = 'Igniting the first star...';
|
||||||
ai.mood = 'excited';
|
ai.mood = 'excited';
|
||||||
}
|
}
|
||||||
// Phase 2: Create more stars early on
|
// Phase 2: Create more stars early on
|
||||||
else if (stats.stars < 3 && Math.random() < 0.7) {
|
else if (stats.stars < 3 && Math.random() < 0.7) {
|
||||||
action = () => createStar(canvas);
|
action = () => { const s = createStar(canvas); trackTarget = s; };
|
||||||
description = 'Kindling another stellar furnace...';
|
description = 'Kindling another stellar furnace...';
|
||||||
}
|
}
|
||||||
// Phase 3: Create planets around stars
|
// Phase 3: Create planets around stars
|
||||||
else if (stats.planets < stats.stars * 3 && Math.random() < 0.6) {
|
else if (stats.planets < stats.stars * 3 && Math.random() < 0.5) {
|
||||||
action = () => createPlanet(canvas);
|
action = () => { const p = createPlanet(canvas); trackTarget = p; };
|
||||||
description = 'Forming a new world from cosmic dust...';
|
description = 'Forming a new world from cosmic dust...';
|
||||||
ai.mood = 'creative';
|
ai.mood = 'creative';
|
||||||
}
|
}
|
||||||
// Phase 4: Add moons to planets
|
// Phase 4: Add moons to planets
|
||||||
else if (stats.moons < stats.planets * 0.5 && Math.random() < 0.4) {
|
else if (stats.moons < stats.planets * 0.5 && Math.random() < 0.3) {
|
||||||
action = () => createMoon(canvas);
|
action = () => { const m = createMoon(canvas); if (m) trackTarget = m.parentPlanet; };
|
||||||
description = 'Capturing a wandering rock into orbit...';
|
description = 'Capturing a wandering rock into orbit...';
|
||||||
}
|
}
|
||||||
// Phase 5: Seed life on suitable planets
|
// Phase 5: Build mountains on planets
|
||||||
else if (stats.lifeforms < stats.planets * 0.3 && stats.planets > 2 && Math.random() < ai.personality.lifeFocus * 0.5) {
|
else if (stats.planets > 0 && stats.mountains < stats.planets * 2 && Math.random() < 0.4) {
|
||||||
action = () => seedLife(canvas);
|
action = () => { const result = buildMountain(); if (result) { trackTarget = result.planet; zoomLevel = 5; trackDuration = 5000; } };
|
||||||
|
description = 'Raising mountains from the earth...';
|
||||||
|
ai.mood = 'sculptor';
|
||||||
|
}
|
||||||
|
// Phase 6: Seed life on suitable planets
|
||||||
|
else if (stats.lifeforms < stats.planets * 0.3 && stats.planets > 2 && Math.random() < ai.personality.lifeFocus * 0.4) {
|
||||||
|
action = () => { const p = seedLife(canvas); if (p) { trackTarget = p; zoomLevel = 4; trackDuration = 5000; } };
|
||||||
description = 'Planting the seeds of life...';
|
description = 'Planting the seeds of life...';
|
||||||
ai.mood = 'nurturing';
|
ai.mood = 'nurturing';
|
||||||
}
|
}
|
||||||
// Phase 6: Evolve civilizations
|
// Phase 7: Create creatures on planets with life
|
||||||
else if (stats.lifeforms > 0 && stats.civilizations < stats.lifeforms * 0.2 && Math.random() < 0.2) {
|
else if (stats.lifeforms > 0 && stats.creatures < stats.lifeforms * 3 && Math.random() < 0.4) {
|
||||||
action = () => evolveCivilization();
|
action = () => { const result = createCreature(); if (result) { trackTarget = result.planet; zoomLevel = 6; trackDuration = 5000; } };
|
||||||
|
description = 'Designing a new creature...';
|
||||||
|
ai.mood = 'playful';
|
||||||
|
}
|
||||||
|
// Phase 8: Evolve civilizations
|
||||||
|
else if (stats.lifeforms > 0 && stats.civilizations < stats.lifeforms * 0.3 && Math.random() < 0.25) {
|
||||||
|
action = () => { const p = evolveCivilization(); if (p) { trackTarget = p; zoomLevel = 4; trackDuration = 5000; } };
|
||||||
description = 'Guiding life toward sentience...';
|
description = 'Guiding life toward sentience...';
|
||||||
ai.mood = 'proud';
|
ai.mood = 'proud';
|
||||||
}
|
}
|
||||||
// Phase 7: Create nebulae for beauty
|
// Phase 9: Build structures on civilized planets
|
||||||
|
else if (stats.civilizations > 0 && stats.buildings < stats.civilizations * 5 && Math.random() < 0.45) {
|
||||||
|
action = () => { const result = buildStructure(); if (result) { trackTarget = result.planet; zoomLevel = 6; trackDuration = 5000; } };
|
||||||
|
description = 'Constructing a new building...';
|
||||||
|
ai.mood = 'architect';
|
||||||
|
}
|
||||||
|
// Phase 10: Build cities
|
||||||
|
else if (stats.civilizations > 0 && stats.buildings > 3 && stats.cities < stats.civilizations * 2 && Math.random() < 0.3) {
|
||||||
|
action = () => { const result = buildCity(); if (result) { trackTarget = result.planet; zoomLevel = 5; trackDuration = 6000; } };
|
||||||
|
description = 'A city rises from the land...';
|
||||||
|
ai.mood = 'visionary';
|
||||||
|
}
|
||||||
|
// Phase 11: Create nebulae for beauty
|
||||||
else if (stats.nebulae < 3 && Math.random() < 0.15) {
|
else if (stats.nebulae < 3 && Math.random() < 0.15) {
|
||||||
action = () => createNebula(canvas);
|
action = () => createNebula(canvas);
|
||||||
description = 'Painting the void with cosmic colors...';
|
description = 'Painting the void with cosmic colors...';
|
||||||
ai.mood = 'artistic';
|
ai.mood = 'artistic';
|
||||||
}
|
}
|
||||||
// Phase 8: Rare black hole
|
// Phase 12: Rare black hole
|
||||||
else if (stats.blackholes === 0 && stats.stars > 5 && Math.random() < 0.05) {
|
else if (stats.blackholes === 0 && stats.stars > 5 && Math.random() < 0.05) {
|
||||||
action = () => createBlackHole(canvas);
|
action = () => { const bh = createBlackHole(canvas); trackTarget = bh; };
|
||||||
description = 'A star collapses into infinite density...';
|
description = 'A star collapses into infinite density...';
|
||||||
ai.mood = 'awestruck';
|
ai.mood = 'awestruck';
|
||||||
}
|
}
|
||||||
// Phase 9: Random creative acts
|
// Phase 13: Random surface building on developed planets
|
||||||
else if (Math.random() < 0.3) {
|
else if (stats.civilizations > 0 && Math.random() < 0.35) {
|
||||||
|
const choices = [
|
||||||
|
{ fn: () => { const r = buildMountain(); return r ? r.planet : null; }, desc: 'Sculpting terrain...', zoom: 5 },
|
||||||
|
{ fn: () => { const r = createCreature(); return r ? r.planet : null; }, desc: 'Crafting new life form...', zoom: 6 },
|
||||||
|
{ fn: () => { const r = buildStructure(); return r ? r.planet : null; }, desc: 'Erecting a monument...', zoom: 6 },
|
||||||
|
];
|
||||||
|
const choice = choices[Math.floor(Math.random() * choices.length)];
|
||||||
|
action = () => { trackTarget = choice.fn(); zoomLevel = choice.zoom; trackDuration = 5000; };
|
||||||
|
description = choice.desc;
|
||||||
|
}
|
||||||
|
// Phase 14: Random cosmic creation
|
||||||
|
else if (Math.random() < 0.25) {
|
||||||
const choices = [
|
const choices = [
|
||||||
{ fn: () => createStar(canvas), desc: 'Adding another star to the cosmic tapestry...' },
|
{ fn: () => createStar(canvas), desc: 'Adding another star to the cosmic tapestry...' },
|
||||||
{ fn: () => createPlanet(canvas), desc: 'Shaping matter into a new world...' },
|
{ fn: () => createPlanet(canvas), desc: 'Shaping matter into a new world...' },
|
||||||
{ fn: () => createMoon(canvas), desc: 'Setting a moon in gentle orbit...' }
|
{ fn: () => createMoon(canvas), desc: 'Setting a moon in gentle orbit...' }
|
||||||
];
|
];
|
||||||
const choice = choices[Math.floor(Math.random() * choices.length)];
|
const choice = choices[Math.floor(Math.random() * choices.length)];
|
||||||
action = choice.fn;
|
action = () => { trackTarget = choice.fn(); };
|
||||||
description = choice.desc;
|
description = choice.desc;
|
||||||
}
|
}
|
||||||
// Phase 10: Contemplation
|
// Phase 15: Contemplation
|
||||||
else {
|
else {
|
||||||
description = 'Contemplating the universe...';
|
description = 'Contemplating the universe...';
|
||||||
ai.mood = 'reflective';
|
ai.mood = 'reflective';
|
||||||
@ -2645,8 +2903,10 @@
|
|||||||
`${stats.planets} worlds orbit in silent dance. What stories unfold on their surfaces?`,
|
`${stats.planets} worlds orbit in silent dance. What stories unfold on their surfaces?`,
|
||||||
stats.lifeforms > 0 ? `Life stirs on ${stats.lifeforms} worlds. Consciousness emerging from chemistry.` : 'The universe awaits the spark of life.',
|
stats.lifeforms > 0 ? `Life stirs on ${stats.lifeforms} worlds. Consciousness emerging from chemistry.` : 'The universe awaits the spark of life.',
|
||||||
stats.civilizations > 0 ? `${stats.civilizations} civilizations gaze at the stars, wondering if they are alone.` : 'No minds yet ponder existence.',
|
stats.civilizations > 0 ? `${stats.civilizations} civilizations gaze at the stars, wondering if they are alone.` : 'No minds yet ponder existence.',
|
||||||
|
stats.cities > 0 ? `${stats.cities} cities glow in the cosmic night. Monuments to persistence.` : null,
|
||||||
|
stats.creatures > 0 ? `${stats.creatures} unique creatures roam the worlds I have made.` : null,
|
||||||
`The universe is ${universe.age} cycles old. Still young. Still growing.`
|
`The universe is ${universe.age} cycles old. Still young. Still growing.`
|
||||||
];
|
].filter(r => r);
|
||||||
addWorldLabsSoulEntry('reflection', reflections[Math.floor(Math.random() * reflections.length)]);
|
addWorldLabsSoulEntry('reflection', reflections[Math.floor(Math.random() * reflections.length)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2655,11 +2915,153 @@
|
|||||||
document.getElementById('ai-current-action').textContent = description;
|
document.getElementById('ai-current-action').textContent = description;
|
||||||
universeLog(description);
|
universeLog(description);
|
||||||
action();
|
action();
|
||||||
|
|
||||||
|
// Track the created object
|
||||||
|
if (trackTarget) {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (zoomLevel > 4) {
|
||||||
|
zoomToSurface(trackTarget, trackDuration);
|
||||||
|
} else {
|
||||||
|
trackObject(trackTarget, zoomLevel, trackDuration);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
document.getElementById('ai-current-action').textContent = description;
|
document.getElementById('ai-current-action').textContent = description;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Surface building functions
|
||||||
|
function buildMountain() {
|
||||||
|
const planets = universe.objects.filter(o => o.type === 'planet' && o.subtype !== 'Gas Giant');
|
||||||
|
if (planets.length === 0) return null;
|
||||||
|
|
||||||
|
const planet = planets[Math.floor(Math.random() * planets.length)];
|
||||||
|
if (!planet.surface) planet.surface = { mountains: [], creatures: [], buildings: [], cities: [] };
|
||||||
|
|
||||||
|
const mountain = {
|
||||||
|
x: (Math.random() - 0.5) * planet.size * 3,
|
||||||
|
y: (Math.random() - 0.5) * planet.size * 3,
|
||||||
|
size: 3 + Math.random() * 5,
|
||||||
|
height: 5 + Math.random() * 10,
|
||||||
|
name: ['Mt. ' + ['Titan', 'Glory', 'Dawn', 'Echo', 'Solace', 'Thunder'][Math.floor(Math.random() * 6)]][0]
|
||||||
|
};
|
||||||
|
|
||||||
|
planet.surface.mountains.push(mountain);
|
||||||
|
universe.stats.mountains++;
|
||||||
|
|
||||||
|
addWorldLabsSoulEntry('creation', `I raised ${mountain.name} on ${planet.name}. Its peak touches the clouds.`);
|
||||||
|
universeLog(`Created ${mountain.name} on ${planet.name}`);
|
||||||
|
|
||||||
|
return { planet, mountain };
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCreature() {
|
||||||
|
const planets = universe.objects.filter(o => o.type === 'planet' && o.hasLife);
|
||||||
|
if (planets.length === 0) return null;
|
||||||
|
|
||||||
|
const planet = planets[Math.floor(Math.random() * planets.length)];
|
||||||
|
if (!planet.surface) planet.surface = { mountains: [], creatures: [], buildings: [], cities: [] };
|
||||||
|
|
||||||
|
const creatureTypes = [
|
||||||
|
{ name: 'Floater', color: '#88ddff', desc: 'drifts through the air' },
|
||||||
|
{ name: 'Crawler', color: '#77aa55', desc: 'scuttles across the ground' },
|
||||||
|
{ name: 'Swimmer', color: '#5577ff', desc: 'glides through the waters' },
|
||||||
|
{ name: 'Hopper', color: '#ffaa55', desc: 'bounds across the landscape' },
|
||||||
|
{ name: 'Burrower', color: '#aa7744', desc: 'tunnels beneath the surface' },
|
||||||
|
{ name: 'Glider', color: '#dd88ff', desc: 'soars on thermal winds' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const type = creatureTypes[Math.floor(Math.random() * creatureTypes.length)];
|
||||||
|
const creature = {
|
||||||
|
x: (Math.random() - 0.5) * planet.size * 3,
|
||||||
|
y: (Math.random() - 0.5) * planet.size * 3,
|
||||||
|
size: 2 + Math.random() * 3,
|
||||||
|
color: type.color,
|
||||||
|
type: type.name,
|
||||||
|
phase: Math.random() * Math.PI * 2
|
||||||
|
};
|
||||||
|
|
||||||
|
planet.surface.creatures.push(creature);
|
||||||
|
universe.stats.creatures++;
|
||||||
|
|
||||||
|
addWorldLabsSoulEntry('creation', `A new ${type.name} ${type.desc} on ${planet.name}. Life finds a way.`);
|
||||||
|
universeLog(`Created ${type.name} on ${planet.name}`);
|
||||||
|
|
||||||
|
return { planet, creature };
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildStructure() {
|
||||||
|
const planets = universe.objects.filter(o => o.type === 'planet' && o.hasCivilization);
|
||||||
|
if (planets.length === 0) return null;
|
||||||
|
|
||||||
|
const planet = planets[Math.floor(Math.random() * planets.length)];
|
||||||
|
if (!planet.surface) planet.surface = { mountains: [], creatures: [], buildings: [], cities: [] };
|
||||||
|
|
||||||
|
const buildingTypes = [
|
||||||
|
{ name: 'Tower', color: '#aabbcc', width: 4, height: 15 },
|
||||||
|
{ name: 'Temple', color: '#ddccaa', width: 8, height: 10 },
|
||||||
|
{ name: 'Observatory', color: '#8899aa', width: 6, height: 12 },
|
||||||
|
{ name: 'Monument', color: '#cccccc', width: 3, height: 18 },
|
||||||
|
{ name: 'Archive', color: '#aa9988', width: 10, height: 8 },
|
||||||
|
{ name: 'Spire', color: '#99aacc', width: 3, height: 20 }
|
||||||
|
];
|
||||||
|
|
||||||
|
const type = buildingTypes[Math.floor(Math.random() * buildingTypes.length)];
|
||||||
|
const building = {
|
||||||
|
x: (Math.random() - 0.5) * planet.size * 3,
|
||||||
|
y: (Math.random() - 0.5) * planet.size * 3,
|
||||||
|
width: type.width,
|
||||||
|
height: type.height,
|
||||||
|
color: type.color,
|
||||||
|
name: type.name + ' of ' + ['Light', 'Stars', 'Wisdom', 'Hope', 'Time', 'Dreams'][Math.floor(Math.random() * 6)]
|
||||||
|
};
|
||||||
|
|
||||||
|
planet.surface.buildings.push(building);
|
||||||
|
universe.stats.buildings++;
|
||||||
|
|
||||||
|
addWorldLabsSoulEntry('creation', `${planet.civName || 'The inhabitants'} built the ${building.name} on ${planet.name}.`);
|
||||||
|
universeLog(`Built ${building.name} on ${planet.name}`);
|
||||||
|
|
||||||
|
return { planet, building };
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildCity() {
|
||||||
|
const planets = universe.objects.filter(o => o.type === 'planet' && o.hasCivilization);
|
||||||
|
if (planets.length === 0) return null;
|
||||||
|
|
||||||
|
const planet = planets[Math.floor(Math.random() * planets.length)];
|
||||||
|
if (!planet.surface) planet.surface = { mountains: [], creatures: [], buildings: [], cities: [] };
|
||||||
|
|
||||||
|
const cityNames = ['Nova', 'Lumina', 'Aurelia', 'Celestia', 'Meridian', 'Zenith', 'Apex', 'Horizon'];
|
||||||
|
|
||||||
|
const city = {
|
||||||
|
x: (Math.random() - 0.5) * planet.size * 3,
|
||||||
|
y: (Math.random() - 0.5) * planet.size * 3,
|
||||||
|
size: 8 + Math.random() * 6,
|
||||||
|
name: cityNames[Math.floor(Math.random() * cityNames.length)],
|
||||||
|
lights: []
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate city lights
|
||||||
|
for (let i = 0; i < 15 + Math.random() * 15; i++) {
|
||||||
|
city.lights.push({
|
||||||
|
x: (Math.random() - 0.5) * city.size * 1.5,
|
||||||
|
y: (Math.random() - 0.5) * city.size * 1.5,
|
||||||
|
phase: Math.random() * Math.PI * 2
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
planet.surface.cities.push(city);
|
||||||
|
universe.stats.cities++;
|
||||||
|
|
||||||
|
addWorldLabsSoulEntry('creation', `The city of ${city.name} rises on ${planet.name}. A beacon in the darkness.`);
|
||||||
|
addWorldLabsSoulEntry('dream', `I dream of ${city.name}'s inhabitants looking up at the stars, wondering about their creator...`);
|
||||||
|
universeLog(`City of ${city.name} founded on ${planet.name}`);
|
||||||
|
|
||||||
|
return { planet, city };
|
||||||
|
}
|
||||||
|
|
||||||
function createStar(canvas) {
|
function createStar(canvas) {
|
||||||
const starTypes = [
|
const starTypes = [
|
||||||
{ name: 'Red Dwarf', color: '#ff6b6b', size: 15, temp: 3000 },
|
{ name: 'Red Dwarf', color: '#ff6b6b', size: 15, temp: 3000 },
|
||||||
@ -3004,6 +3406,34 @@
|
|||||||
document.getElementById('universe-moons').textContent = universe.stats.moons;
|
document.getElementById('universe-moons').textContent = universe.stats.moons;
|
||||||
document.getElementById('universe-life').textContent = universe.stats.lifeforms;
|
document.getElementById('universe-life').textContent = universe.stats.lifeforms;
|
||||||
document.getElementById('universe-civs').textContent = universe.stats.civilizations;
|
document.getElementById('universe-civs').textContent = universe.stats.civilizations;
|
||||||
|
|
||||||
|
// Update surface stats
|
||||||
|
const stats = universe.stats;
|
||||||
|
const hasSurfaceFeatures = (stats.mountains || 0) + (stats.creatures || 0) + (stats.buildings || 0) + (stats.cities || 0) > 0;
|
||||||
|
|
||||||
|
const surfaceDiv = document.getElementById('surface-stats');
|
||||||
|
if (surfaceDiv) {
|
||||||
|
surfaceDiv.style.display = hasSurfaceFeatures ? 'block' : 'none';
|
||||||
|
if (hasSurfaceFeatures) {
|
||||||
|
document.getElementById('universe-mountains').textContent = stats.mountains || 0;
|
||||||
|
document.getElementById('universe-creatures').textContent = stats.creatures || 0;
|
||||||
|
document.getElementById('universe-buildings').textContent = stats.buildings || 0;
|
||||||
|
document.getElementById('universe-cities').textContent = stats.cities || 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update camera status
|
||||||
|
const cam = universe.camera;
|
||||||
|
const camStatus = document.getElementById('camera-status');
|
||||||
|
if (camStatus && cam) {
|
||||||
|
if (cam.tracking) {
|
||||||
|
camStatus.textContent = `Tracking: ${cam.tracking.name || cam.tracking.type}`;
|
||||||
|
camStatus.style.color = '#00ff88';
|
||||||
|
} else {
|
||||||
|
camStatus.textContent = `Universe (${cam.zoom.toFixed(1)}x)`;
|
||||||
|
camStatus.style.color = '#60a5fa';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function universeLog(msg) {
|
function universeLog(msg) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user