Files
blackroad-metaverse/universe.html
Your Name a40f2b2742 🔐 Login system
2026-01-30 16:29:07 -06:00

1122 lines
38 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BlackRoad Universe — Complete Living Metaverse</title>
<meta name="description" content="The Complete BlackRoad Universe - 18 integrated systems, infinite exploration, living AI">
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<!-- Three.js -->
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/"
}
}
</script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
/* ===== OFFICIAL BLACKROAD COLORS ===== */
--sunrise-orange: #FF9D00;
--warm-orange: #FF6B00;
--hot-pink: #FF0066;
--electric-magenta: #FF006B;
--deep-magenta: #D600AA;
--vivid-purple: #7700FF;
--cyber-blue: #0066FF;
/* ===== NEUTRALS ===== */
--pure-black: #000000;
--deep-black: #0A0A0A;
--charcoal: #1A1A1A;
--pure-white: #FFFFFF;
--gray-300: #A0A0A0;
/* ===== SEMANTIC COLORS ===== */
--success: #27AE60;
--error: #E74C3C;
--alice-blue: #0066FF;
--aria-pink: #FF0066;
--lucidia-purple: #7700FF;
/* ===== OFFICIAL GRADIENTS ===== */
--gradient-br: linear-gradient(180deg, #FF9D00 0%, #FF6B00 25%, #FF0066 75%, #FF006B 100%);
--gradient-os: linear-gradient(180deg, #FF006B 0%, #D600AA 25%, #7700FF 75%, #0066FF 100%);
--gradient-full: linear-gradient(180deg, #FF9D00 0%, #FF6B00 14%, #FF0066 28%, #FF006B 42%, #D600AA 57%, #7700FF 71%, #0066FF 100%);
/* ===== GOLDEN RATIO SPACING (φ = 1.618) ===== */
--space-xs: 8px;
--space-sm: 13px; /* 8 × 1.618 */
--space-md: 21px; /* 13 × 1.618 */
--space-lg: 34px; /* 21 × 1.618 */
--space-xl: 55px; /* 34 × 1.618 */
--space-2xl: 89px; /* 55 × 1.618 */
/* ===== BORDER RADIUS ===== */
--radius-sm: 6px;
--radius-md: 10px;
--radius-lg: 16px;
--radius-xl: 24px;
--radius-2xl: 34px;
/* ===== GLASS MORPHISM ===== */
--glass-bg: rgba(255, 255, 255, 0.05);
--glass-bg-strong: rgba(255, 255, 255, 0.08);
--glass-border: rgba(255, 255, 255, 0.1);
--glass-border-strong: rgba(255, 255, 255, 0.15);
/* ===== LEGACY COMPAT ===== */
--bg-dark: var(--deep-black);
--bg-darker: var(--pure-black);
--text-primary: var(--pure-white);
--text-secondary: var(--gray-300);
--accent-purple: var(--vivid-purple);
--accent-blue: var(--cyber-blue);
--accent-red: var(--error);
--accent-green: var(--success);
--accent-orange: var(--sunrise-orange);
}
body {
font-family: 'Inter', sans-serif;
background: var(--bg-darker);
color: var(--text-primary);
overflow: hidden;
position: relative;
}
/* ===== LOADING SCREEN ===== */
#loading-screen {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #0a0a0a 0%, #1a0a2e 50%, #0a0a0a 100%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 10001;
transition: opacity 0.5s ease;
}
#loading-screen.hidden {
opacity: 0;
pointer-events: none;
}
.loading-logo {
font-size: 48px;
font-weight: 900;
font-family: 'JetBrains Mono', monospace;
background: var(--gradient-full);
background-size: 300% 300%;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
animation: gradientShift 3s ease infinite;
margin-bottom: var(--space-lg);
}
@keyframes gradientShift {
0%, 100% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
}
.loading-bar-container {
width: 300px;
height: 4px;
background: rgba(255, 255, 255, 0.1);
border-radius: 2px;
overflow: hidden;
margin-bottom: 20px;
}
.loading-bar {
height: 100%;
background: var(--gradient-br);
border-radius: 2px;
transition: width 0.3s ease;
width: 0%;
}
.loading-text {
font-size: 14px;
color: var(--text-secondary);
font-family: 'JetBrains Mono', monospace;
}
.loading-systems {
margin-top: var(--space-lg);
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-sm);
max-width: 600px;
}
.system-item {
font-size: 11px;
color: var(--text-secondary);
padding: var(--space-xs) var(--space-sm);
background: var(--glass-bg);
border: 1px solid var(--glass-border);
border-radius: var(--radius-sm);
opacity: 0.3;
transition: opacity 0.3s ease;
font-family: 'JetBrains Mono', monospace;
}
.system-item.loaded {
opacity: 1;
color: var(--accent-green);
}
/* ===== LOGIN SCREEN ===== */
#login-screen {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #0a0a0a 0%, #1a0a2e 50%, #0a0a0a 100%);
display: flex;
align-items: center;
justify-content: center;
z-index: 10000;
transition: opacity 0.5s ease, visibility 0.5s ease;
}
#login-screen.hidden {
opacity: 0;
visibility: hidden;
pointer-events: none;
}
.login-background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
.star {
position: absolute;
width: 2px;
height: 2px;
background: white;
border-radius: 50%;
animation: twinkle 3s infinite;
}
@keyframes twinkle {
0%, 100% { opacity: 0.3; }
50% { opacity: 1; }
}
.login-container {
position: relative;
z-index: 2;
background: var(--glass-bg);
backdrop-filter: blur(20px);
border: 1px solid var(--glass-border);
border-radius: var(--radius-xl);
padding: var(--space-2xl);
max-width: 480px;
width: 90%;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}
.login-title {
font-size: 36px;
font-weight: 900;
font-family: 'JetBrains Mono', monospace;
text-align: center;
margin-bottom: var(--space-sm);
background: var(--gradient-full);
background-size: 300% 300%;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
animation: gradientShift 3s ease infinite;
}
.login-subtitle {
text-align: center;
color: var(--text-secondary);
font-size: 14px;
margin-bottom: 40px;
}
.login-input {
width: 100%;
padding: 16px 20px;
background: rgba(255, 255, 255, 0.08);
border: 1px solid var(--glass-border);
border-radius: 12px;
color: var(--text-primary);
font-size: 15px;
margin-bottom: 16px;
transition: all 0.3s ease;
font-family: 'Inter', sans-serif;
}
.login-input:focus {
outline: none;
background: rgba(255, 255, 255, 0.12);
border-color: var(--accent-purple);
}
.login-button {
width: 100%;
padding: 16px;
background: linear-gradient(45deg, #9B59B6, #8E44AD);
border: none;
border-radius: 12px;
color: white;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease;
font-family: 'Inter', sans-serif;
}
.login-button:hover {
transform: translateY(-2px);
box-shadow: 0 10px 30px rgba(155, 89, 182, 0.3);
}
.login-button:active {
transform: translateY(0);
}
.login-features {
margin-top: 30px;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.feature-badge {
background: rgba(255, 255, 255, 0.05);
padding: 10px;
border-radius: 8px;
font-size: 12px;
text-align: center;
border: 1px solid rgba(255, 255, 255, 0.08);
}
/* ===== GAME CANVAS ===== */
#game-canvas {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: none;
}
#game-canvas.active {
display: block;
}
/* ===== HUD ===== */
.hud {
position: fixed;
z-index: 1000;
pointer-events: none;
}
.hud-top-left {
top: 20px;
left: 20px;
}
.hud-top-right {
top: 20px;
right: 20px;
text-align: right;
}
.hud-bottom-left {
bottom: 20px;
left: 20px;
}
.hud-panel {
background: var(--glass-bg);
backdrop-filter: blur(10px);
border: 1px solid var(--glass-border);
border-radius: 12px;
padding: 16px 20px;
margin-bottom: 12px;
font-family: 'JetBrains Mono', monospace;
font-size: 13px;
}
.hud-title {
font-weight: 600;
margin-bottom: 8px;
color: var(--accent-purple);
}
.hud-row {
display: flex;
justify-content: space-between;
align-items: center;
margin: 4px 0;
}
.hud-label {
color: var(--text-secondary);
}
.hud-value {
color: var(--text-primary);
font-weight: 500;
}
/* ===== CONTROLS HELP ===== */
#controls-help {
position: fixed;
bottom: 20px;
right: 20px;
background: var(--glass-bg);
backdrop-filter: blur(10px);
border: 1px solid var(--glass-border);
border-radius: 12px;
padding: 20px;
max-width: 300px;
z-index: 1000;
opacity: 0;
transform: translateY(20px);
transition: opacity 0.3s ease, transform 0.3s ease;
pointer-events: none;
}
#controls-help.visible {
opacity: 1;
transform: translateY(0);
}
.control-group {
margin-bottom: 16px;
}
.control-group-title {
font-weight: 600;
color: var(--accent-purple);
margin-bottom: 8px;
font-size: 12px;
}
.control-row {
display: flex;
justify-content: space-between;
margin: 4px 0;
font-size: 11px;
}
.control-key {
background: rgba(255, 255, 255, 0.1);
padding: 2px 8px;
border-radius: 4px;
font-family: 'JetBrains Mono', monospace;
font-weight: 500;
}
.control-desc {
color: var(--text-secondary);
}
/* ===== NOTIFICATION ===== */
.notification {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%) translateY(-100px);
background: var(--glass-bg);
backdrop-filter: blur(10px);
border: 1px solid var(--glass-border);
border-radius: 12px;
padding: 16px 24px;
z-index: 2000;
transition: transform 0.3s ease;
pointer-events: none;
}
.notification.show {
transform: translateX(-50%) translateY(0);
}
/* ===== SYSTEM STATUS INDICATOR ===== */
.system-status {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
z-index: 999;
display: flex;
gap: 8px;
pointer-events: none;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--accent-green);
box-shadow: 0 0 10px var(--accent-green);
animation: pulse 2s ease infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.status-label {
font-size: 11px;
color: var(--text-secondary);
font-family: 'JetBrains Mono', monospace;
}
</style>
</head>
<body>
<!-- LOADING SCREEN -->
<div id="loading-screen">
<div class="loading-logo">BLACKROAD</div>
<div class="loading-bar-container">
<div class="loading-bar" id="loading-bar"></div>
</div>
<div class="loading-text" id="loading-text">Initializing Universe...</div>
<div class="loading-systems">
<div class="system-item" data-system="celestial">🌌 Celestial Mechanics</div>
<div class="system-item" data-system="truth">🔐 Truth Contracts</div>
<div class="system-item" data-system="verification">✅ Verification</div>
<div class="system-item" data-system="physics">⚡ Physics Engine</div>
<div class="system-item" data-system="agents">🤖 Intelligent Agents</div>
<div class="system-item" data-system="quests">🏆 Quest System</div>
<div class="system-item" data-system="crafting">🛠️ Crafting</div>
<div class="system-item" data-system="dialogue">💬 Dialogue</div>
<div class="system-item" data-system="evolution">🌍 World Evolution</div>
<div class="system-item" data-system="nature">🦋 Living Nature</div>
<div class="system-item" data-system="music">🎵 Living Music</div>
<div class="system-item" data-system="creation">🌱 Creation Powers</div>
<div class="system-item" data-system="multiplayer">👥 Multiplayer</div>
<div class="system-item" data-system="graphics">🎨 Photorealism</div>
<div class="system-item" data-system="biomes">🌲 Infinite Biomes</div>
<div class="system-item" data-system="particles">✨ Particles</div>
<div class="system-item" data-system="transport">🚀 Transportation</div>
<div class="system-item" data-system="integration">🔗 Game Integration</div>
</div>
</div>
<!-- LOGIN SCREEN -->
<div id="login-screen" class="hidden">
<div class="login-background" id="login-bg"></div>
<div class="login-container">
<h1 class="login-title">BLACKROAD</h1>
<p class="login-subtitle">The Complete Living Universe</p>
<input type="text" class="login-input" id="username-input" placeholder="Enter your name..." />
<button class="login-button" id="enter-button">Enter Universe</button>
<div class="login-features">
<div class="feature-badge">🌌 18 Systems</div>
<div class="feature-badge">∞ Infinite World</div>
<div class="feature-badge">🤖 AI Agents</div>
<div class="feature-badge">🦋 Living Nature</div>
<div class="feature-badge">🎨 Photorealistic</div>
<div class="feature-badge">⚡ Real Physics</div>
</div>
</div>
</div>
<!-- GAME CANVAS -->
<canvas id="game-canvas"></canvas>
<!-- HUD -->
<div class="hud hud-top-left">
<div class="hud-panel">
<div class="hud-title">🌍 UNIVERSE STATUS</div>
<div class="hud-row">
<span class="hud-label">Time:</span>
<span class="hud-value" id="hud-time">00:00</span>
</div>
<div class="hud-row">
<span class="hud-label">Biome:</span>
<span class="hud-value" id="hud-biome">Loading...</span>
</div>
<div class="hud-row">
<span class="hud-label">Position:</span>
<span class="hud-value" id="hud-position">0, 0, 0</span>
</div>
</div>
<div class="hud-panel">
<div class="hud-title">⚡ SYSTEMS</div>
<div class="hud-row">
<span class="hud-label">Physics:</span>
<span class="hud-value" style="color: var(--accent-green);"></span>
</div>
<div class="hud-row">
<span class="hud-label">AI Agents:</span>
<span class="hud-value" id="hud-agents">3</span>
</div>
<div class="hud-row">
<span class="hud-label">Quests:</span>
<span class="hud-value" id="hud-quests">0/12</span>
</div>
</div>
</div>
<div class="hud hud-top-right">
<div class="hud-panel">
<div class="hud-title">🎮 CONTROLS</div>
<div style="font-size: 11px; color: var(--text-secondary);">
Press <strong style="color: white;">H</strong> for help
</div>
</div>
</div>
<!-- CONTROLS HELP -->
<div id="controls-help">
<div class="control-group">
<div class="control-group-title">MOVEMENT</div>
<div class="control-row">
<span class="control-key">W A S D</span>
<span class="control-desc">Move</span>
</div>
<div class="control-row">
<span class="control-key">SPACE</span>
<span class="control-desc">Jump / Fly Up</span>
</div>
<div class="control-row">
<span class="control-key">SHIFT</span>
<span class="control-desc">Fly Down</span>
</div>
<div class="control-row">
<span class="control-key">F</span>
<span class="control-desc">Toggle Flying</span>
</div>
</div>
<div class="control-group">
<div class="control-group-title">INTERACTION</div>
<div class="control-row">
<span class="control-key">E</span>
<span class="control-desc">Interact / Love</span>
</div>
<div class="control-row">
<span class="control-key">Q</span>
<span class="control-desc">Feed</span>
</div>
<div class="control-row">
<span class="control-key">T</span>
<span class="control-desc">Teleport Menu</span>
</div>
</div>
<div class="control-group">
<div class="control-group-title">WEATHER</div>
<div class="control-row">
<span class="control-key">R</span>
<span class="control-desc">Toggle Rain</span>
</div>
<div class="control-row">
<span class="control-key">N</span>
<span class="control-desc">Toggle Snow</span>
</div>
<div class="control-row">
<span class="control-key">G</span>
<span class="control-desc">Toggle Fireflies</span>
</div>
</div>
<div class="control-group">
<div class="control-group-title">SYSTEM</div>
<div class="control-row">
<span class="control-key">H</span>
<span class="control-desc">Toggle Help</span>
</div>
<div class="control-row">
<span class="control-key">ESC</span>
<span class="control-desc">Unlock Mouse</span>
</div>
</div>
</div>
<!-- SYSTEM STATUS INDICATOR -->
<div class="system-status">
<div class="status-dot"></div>
<div class="status-label">18 SYSTEMS ACTIVE</div>
</div>
<!-- NOTIFICATION -->
<div class="notification" id="notification"></div>
<!-- MAIN SCRIPT -->
<script type="module">
import * as THREE from 'three';
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
// ===== SYSTEM LOADER =====
const systems = [
'celestial', 'truth', 'verification', 'physics', 'agents', 'quests',
'crafting', 'dialogue', 'evolution', 'nature', 'music', 'creation',
'multiplayer', 'graphics', 'biomes', 'particles', 'transport', 'integration'
];
let loadedSystems = 0;
function updateLoadingProgress(systemName, progress) {
const systemElement = document.querySelector(`[data-system="${systemName}"]`);
if (systemElement) {
systemElement.classList.add('loaded');
}
loadedSystems++;
const percent = (loadedSystems / systems.length) * 100;
document.getElementById('loading-bar').style.width = `${percent}%`;
document.getElementById('loading-text').textContent = `Loading ${systemName}... ${Math.round(percent)}%`;
}
// Simulate system loading
async function loadSystems() {
for (const system of systems) {
await new Promise(resolve => setTimeout(resolve, 100));
updateLoadingProgress(system, loadedSystems / systems.length);
}
setTimeout(() => {
document.getElementById('loading-screen').classList.add('hidden');
setTimeout(() => {
document.getElementById('login-screen').classList.remove('hidden');
initLoginScreen();
}, 500);
}, 500);
}
// ===== LOGIN SCREEN =====
function initLoginScreen() {
const bg = document.getElementById('login-bg');
// Create animated starfield
for (let i = 0; i < 100; i++) {
const star = document.createElement('div');
star.className = 'star';
star.style.left = `${Math.random() * 100}%`;
star.style.top = `${Math.random() * 100}%`;
star.style.animationDelay = `${Math.random() * 3}s`;
bg.appendChild(star);
}
const usernameInput = document.getElementById('username-input');
const enterButton = document.getElementById('enter-button');
enterButton.addEventListener('click', () => {
const username = usernameInput.value.trim() || 'Explorer';
document.getElementById('login-screen').classList.add('hidden');
setTimeout(() => {
document.getElementById('game-canvas').classList.add('active');
initUniverse(username);
}, 500);
});
usernameInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
enterButton.click();
}
});
usernameInput.focus();
}
// ===== UNIVERSE INITIALIZATION =====
let scene, camera, renderer, controls;
let playerName = 'Explorer';
let clock, gameTime = 0;
let chunks = new Map();
let currentBiome = 'Forest';
function initUniverse(username) {
playerName = username;
showNotification(`Welcome to BlackRoad, ${playerName}! 🌌`);
// Setup Three.js
scene = new THREE.Scene();
scene.fog = new THREE.Fog(0x87CEEB, 50, 200);
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 10, 0);
const canvas = document.getElementById('game-canvas');
renderer = new THREE.WebGLRenderer({
canvas,
antialias: true,
powerPreference: 'high-performance'
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
// Controls
controls = new PointerLockControls(camera, canvas);
canvas.addEventListener('click', () => {
controls.lock();
});
// Lighting
const ambientLight = new THREE.AmbientLight(0xffffff, 0.4);
scene.add(ambientLight);
const sun = new THREE.DirectionalLight(0xffffff, 1);
sun.position.set(50, 100, 50);
sun.castShadow = true;
sun.shadow.mapSize.width = 2048;
sun.shadow.mapSize.height = 2048;
sun.shadow.camera.far = 500;
sun.shadow.camera.left = -100;
sun.shadow.camera.right = 100;
sun.shadow.camera.top = 100;
sun.shadow.camera.bottom = -100;
scene.add(sun);
const hemisphereLight = new THREE.HemisphereLight(0x87CEEB, 0x8B7355, 0.5);
scene.add(hemisphereLight);
// Ground plane
const groundGeometry = new THREE.PlaneGeometry(1000, 1000);
const groundMaterial = new THREE.MeshStandardMaterial({
color: 0x228B22,
roughness: 0.8,
metalness: 0.2
});
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2;
ground.receiveShadow = true;
scene.add(ground);
// Add some demo content
addDemoContent();
// Setup controls and game loop
setupControls();
clock = new THREE.Clock();
animate();
// Show controls help briefly
setTimeout(() => {
document.getElementById('controls-help').classList.add('visible');
setTimeout(() => {
document.getElementById('controls-help').classList.remove('visible');
}, 5000);
}, 2000);
}
function addDemoContent() {
// Add some trees
for (let i = 0; i < 50; i++) {
const tree = createTree();
tree.position.set(
(Math.random() - 0.5) * 200,
0,
(Math.random() - 0.5) * 200
);
scene.add(tree);
}
// Add AI agents (placeholder spheres)
const agentData = [
{ name: 'Alice', color: 0x4A90E2, pos: [20, 2, 20] },
{ name: 'Aria', color: 0xE74C3C, pos: [-20, 2, 20] },
{ name: 'Lucidia', color: 0x9B59B6, pos: [0, 2, -30] }
];
agentData.forEach(data => {
const agent = new THREE.Group();
// Body
const bodyGeometry = new THREE.SphereGeometry(1.5, 32, 32);
const bodyMaterial = new THREE.MeshStandardMaterial({
color: data.color,
emissive: data.color,
emissiveIntensity: 0.3,
metalness: 0.3,
roughness: 0.7
});
const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
body.castShadow = true;
agent.add(body);
// Glow
const glowGeometry = new THREE.SphereGeometry(2, 32, 32);
const glowMaterial = new THREE.MeshBasicMaterial({
color: data.color,
transparent: true,
opacity: 0.2
});
const glow = new THREE.Mesh(glowGeometry, glowMaterial);
agent.add(glow);
// Light
const light = new THREE.PointLight(data.color, 2, 20);
light.position.set(0, 0, 0);
agent.add(light);
agent.position.set(...data.pos);
scene.add(agent);
});
}
function createTree() {
const tree = new THREE.Group();
// Trunk
const trunkGeometry = new THREE.CylinderGeometry(0.5, 0.7, 4, 8);
const trunkMaterial = new THREE.MeshStandardMaterial({ color: 0x8B4513 });
const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial);
trunk.position.y = 2;
trunk.castShadow = true;
tree.add(trunk);
// Leaves
const leavesGeometry = new THREE.SphereGeometry(2.5, 8, 8);
const leavesMaterial = new THREE.MeshStandardMaterial({ color: 0x228B22 });
const leaves = new THREE.Mesh(leavesGeometry, leavesMaterial);
leaves.position.y = 5;
leaves.castShadow = true;
tree.add(leaves);
return tree;
}
// ===== CONTROLS =====
const keys = {};
let isFlying = false;
let helpVisible = false;
function setupControls() {
window.addEventListener('keydown', (e) => {
keys[e.key.toLowerCase()] = true;
// Special keys
if (e.key.toLowerCase() === 'h') {
helpVisible = !helpVisible;
document.getElementById('controls-help').classList.toggle('visible', helpVisible);
}
if (e.key.toLowerCase() === 'f') {
isFlying = !isFlying;
showNotification(isFlying ? '✈️ Flying Mode ON' : '🚶 Flying Mode OFF');
}
if (e.key.toLowerCase() === 'r') {
showNotification('🌧️ Rain toggled (feature pending)');
}
if (e.key.toLowerCase() === 'n') {
showNotification('❄️ Snow toggled (feature pending)');
}
if (e.key.toLowerCase() === 'g') {
showNotification('✨ Fireflies toggled (feature pending)');
}
if (e.key.toLowerCase() === 't') {
showNotification('🚀 Teleport menu (feature pending)');
}
});
window.addEventListener('keyup', (e) => {
keys[e.key.toLowerCase()] = false;
});
}
// ===== GAME LOOP =====
const moveSpeed = 10;
const flySpeed = 15;
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
gameTime += delta;
if (controls.isLocked) {
const velocity = new THREE.Vector3();
if (keys['w']) velocity.z -= 1;
if (keys['s']) velocity.z += 1;
if (keys['a']) velocity.x -= 1;
if (keys['d']) velocity.x += 1;
velocity.normalize();
velocity.multiplyScalar(moveSpeed * delta);
const forward = new THREE.Vector3();
camera.getWorldDirection(forward);
forward.y = 0;
forward.normalize();
const right = new THREE.Vector3();
right.crossVectors(forward, camera.up);
const movement = new THREE.Vector3();
movement.addScaledVector(forward, -velocity.z);
movement.addScaledVector(right, velocity.x);
camera.position.add(movement);
// Flying
if (isFlying) {
if (keys[' ']) camera.position.y += flySpeed * delta;
if (keys['shift']) camera.position.y -= flySpeed * delta;
} else {
// Keep at ground level
camera.position.y = Math.max(camera.position.y, 10);
}
// Update HUD
updateHUD();
}
renderer.render(scene, camera);
}
// ===== HUD UPDATES =====
function updateHUD() {
const hours = Math.floor(gameTime / 3600) % 24;
const minutes = Math.floor((gameTime % 3600) / 60);
document.getElementById('hud-time').textContent =
`${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
const pos = camera.position;
document.getElementById('hud-position').textContent =
`${pos.x.toFixed(0)}, ${pos.y.toFixed(0)}, ${pos.z.toFixed(0)}`;
document.getElementById('hud-biome').textContent = currentBiome;
}
// ===== NOTIFICATION SYSTEM =====
let notificationTimeout;
function showNotification(message) {
const notification = document.getElementById('notification');
notification.textContent = message;
notification.classList.add('show');
clearTimeout(notificationTimeout);
notificationTimeout = setTimeout(() => {
notification.classList.remove('show');
}, 3000);
}
// ===== WINDOW RESIZE =====
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// ===== START LOADING =====
loadSystems();
</script>
<!-- ===== AUTHENTICATION SYSTEM ===== -->
<script src="auth.js"></script>
<!-- ===== NEW PRODUCTION SYSTEMS ===== -->
<script src="audio-system.js"></script>
<script src="api-client.js"></script>
<script src="performance-optimizer.js"></script>
<!-- ===== INTEGRATION SYSTEM ===== -->
<script>
// Initialize all new systems after DOM load
let audioSystem, apiClient, performanceOptimizer;
async function initializeNewSystems() {
try {
console.log('🚀 Initializing production systems...');
// 1. Audio System
if (typeof AudioSystem !== 'undefined') {
audioSystem = new AudioSystem();
await audioSystem.init();
console.log('🎵 Audio system initialized');
// Auto-start music on first user interaction
const startAudio = () => {
audioSystem.startMusic(currentBiome.toLowerCase());
document.removeEventListener('click', startAudio);
document.removeEventListener('keydown', startAudio);
showNotification('🎵 Music started!');
};
document.addEventListener('click', startAudio);
document.addEventListener('keydown', startAudio);
}
// 2. Performance Optimizer
if (typeof PerformanceOptimizer !== 'undefined') {
performanceOptimizer = new PerformanceOptimizer(renderer, scene, camera);
console.log('⚡ Performance optimizer initialized');
// Add to render loop
const originalAnimate = window.animate;
if (originalAnimate) {
window.animate = function() {
performanceOptimizer.update();
originalAnimate();
};
}
}
// 3. API Client (optional - needs backend)
if (typeof APIClient !== 'undefined') {
apiClient = new APIClient('wss://api.blackroad.io/ws');
// Connect when user is logged in
apiClient.on('connected', () => {
console.log('🔌 Connected to BlackRoad API');
showNotification('🔌 Connected to backend!');
});
apiClient.on('agent_response', (data) => {
showNotification(`🤖 ${data.agent}: ${data.message}`);
});
console.log('🔌 API client initialized');
}
console.log('✅ All production systems ready!');
showNotification('✅ All systems online!');
} catch (error) {
console.warn('⚠️ System initialization warning:', error);
}
}
// Initialize after page load
window.addEventListener('load', () => {
setTimeout(initializeNewSystems, 1000);
});
// Add music toggle to M key
document.addEventListener('keydown', (e) => {
if (e.key === 'm' || e.key === 'M') {
if (audioSystem) {
audioSystem.toggleMusic();
showNotification(audioSystem.musicPlaying ? '🎵 Music ON' : '🔇 Music OFF');
}
}
});
</script>
</body>
</html>