Major architectural improvements: - Simplified replay system from ~1,450 lines to ~320 lines (78% reduction) - Removed scene reconstruction complexity in favor of reusing game logic - Added isReplayMode parameter to Level1 and Ship constructors - Level1.initialize() now creates scene for both game and replay modes - ReplayPlayer simplified to find existing meshes instead of loading assets Replay system changes: - ReplayManager now uses Level1.initialize() to populate scene - Deleted obsolete files: assetCache.ts, ReplayAssetRegistry.ts - Removed full scene deserialization code from LevelDeserializer - Fixed keyboard input error when initializing in replay mode - Physics bodies converted to ANIMATED after Level1 creates them UI simplification for new users: - Hidden level editor, settings, test scene, and replay buttons - Hidden "Create New Level" link - Filtered level selector to only show recruit and pilot difficulties - Clean, focused experience for first-time users Technical improvements: - PhysicsRecorder now accepts LevelConfig via constructor - Removed sessionStorage dependency for level state - Fixed Color3 alpha property error in levelSerializer - Cleaned up unused imports and dependencies 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
436 lines
18 KiB
HTML
436 lines
18 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta content="width=device-width, initial-scale=1, height=device-height" name="viewport">
|
||
<link href="/styles.css" rel="stylesheet">
|
||
<title>Space Game</title>
|
||
<script>
|
||
navigator.serviceWorker.getRegistrations().then(registrations => {
|
||
for (const registration of registrations) {
|
||
registration.unregister();
|
||
}
|
||
});
|
||
</script>
|
||
<link rel="prefetch" href="/background.mp3"/>
|
||
<link rel="prefetch" href="/8192.webp"/>
|
||
</head>
|
||
<body>
|
||
<!-- Game View -->
|
||
<div data-view="game">
|
||
<canvas id="gameCanvas"></canvas>
|
||
<a href="#/editor" class="editor-link" style="display: none;">📝 Level Editor</a>
|
||
<a href="#/settings" class="settings-link" style="display: none;">⚙️ Settings</a>
|
||
<div id="mainDiv">
|
||
<div id="loadingDiv">Loading...</div>
|
||
<div id="levelSelect">
|
||
|
||
|
||
<!-- Controls Section -->
|
||
<div class="controls-info">
|
||
<h2>🎮 How to Play</h2>
|
||
<div class="controls-grid">
|
||
<div class="control-section">
|
||
<h3>VR Controllers (Required for VR)</h3>
|
||
<ul>
|
||
<li><strong>Left Thumbstick:</strong> Move forward/backward and yaw left/right</li>
|
||
<li><strong>Right Thumbstick:</strong> Pitch up/down and Roll left/right</li>
|
||
<li><strong>Front Trigger:</strong> Fire weapon</li>
|
||
</ul>
|
||
</div>
|
||
<div class="control-section">
|
||
<h3>Desktop Controls (Preview Mode)</h3>
|
||
<ul>
|
||
<li><strong>W/S:</strong> Move forward/backward</li>
|
||
<li><strong>A/D:</strong> Yaw left/right</li>
|
||
<li><strong>Arrow Keys:</strong> Pitch up, down</li>
|
||
<li><strong>Space:</strong> Fire weapon</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<p class="controls-note">
|
||
⚠️ <strong>Note:</strong> This game is designed for VR headsets with controllers. Desktop controls are provided for preview and testing purposes only.
|
||
</p>
|
||
</div>
|
||
<h1>Select Your Level</h1>
|
||
<div id="levelCardsContainer" class="card-container">
|
||
<!-- Level cards will be dynamically populated from localStorage -->
|
||
</div>
|
||
<div style="text-align: center; margin-top: 20px; display: none;">
|
||
<button id="testLevelBtn" class="test-level-button">
|
||
🧪 Test Scene (Debug)
|
||
</button>
|
||
<button id="viewReplaysBtn" class="test-level-button" style="margin-left: 10px;">
|
||
📹 View Replays
|
||
</button>
|
||
<br>
|
||
<a href="#/editor" style="color: #4CAF50; text-decoration: none; font-size: 1.1em;">
|
||
+ Create New Level
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Editor View -->
|
||
<div data-view="editor" style="display: none;">
|
||
<div class="editor-container">
|
||
<a href="#/" class="back-link">← Back to Game</a>
|
||
|
||
<h1>🚀 Level Editor</h1>
|
||
<p class="subtitle">Configure and generate custom level configurations</p>
|
||
|
||
<div class="section">
|
||
<h2>Difficulty Presets</h2>
|
||
<div class="preset-buttons">
|
||
<button class="preset-btn" data-difficulty="recruit">Recruit</button>
|
||
<button class="preset-btn" data-difficulty="pilot">Pilot</button>
|
||
<button class="preset-btn" data-difficulty="captain">Captain</button>
|
||
<button class="preset-btn" data-difficulty="commander">Commander</button>
|
||
<button class="preset-btn" data-difficulty="test">Test</button>
|
||
<button class="preset-btn" data-difficulty="custom">Custom</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="editor-grid">
|
||
<!-- Basic Settings -->
|
||
<div class="section">
|
||
<h2>⚙️ Basic Settings</h2>
|
||
<div class="form-group">
|
||
<label for="levelName">Level Name</label>
|
||
<input type="text" id="levelName" placeholder="my-custom-level">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="difficulty">Difficulty</label>
|
||
<select id="difficulty">
|
||
<option value="recruit">Recruit</option>
|
||
<option value="pilot">Pilot</option>
|
||
<option value="captain">Captain</option>
|
||
<option value="commander">Commander</option>
|
||
<option value="test">Test</option>
|
||
<option value="custom">Custom</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="author">Author (Optional)</label>
|
||
<input type="text" id="author" placeholder="Your name">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="description">Description (Optional)</label>
|
||
<input type="text" id="description" placeholder="Level description">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Ship Configuration -->
|
||
<div class="section">
|
||
<h2>🚀 Ship</h2>
|
||
<div class="form-group">
|
||
<label>Position</label>
|
||
<div class="vector-input">
|
||
<div>
|
||
<div class="vector-label">X</div>
|
||
<input type="number" id="shipX" value="0" step="0.1">
|
||
</div>
|
||
<div>
|
||
<div class="vector-label">Y</div>
|
||
<input type="number" id="shipY" value="1" step="0.1">
|
||
</div>
|
||
<div>
|
||
<div class="vector-label">Z</div>
|
||
<input type="number" id="shipZ" value="0" step="0.1">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Start Base Configuration -->
|
||
<div class="section">
|
||
<h2>🎯 Start Base</h2>
|
||
<div class="form-group">
|
||
<label>Position</label>
|
||
<div class="vector-input">
|
||
<div>
|
||
<div class="vector-label">X</div>
|
||
<input type="number" id="baseX" value="0" step="0.1">
|
||
</div>
|
||
<div>
|
||
<div class="vector-label">Y</div>
|
||
<input type="number" id="baseY" value="0" step="0.1">
|
||
</div>
|
||
<div>
|
||
<div class="vector-label">Z</div>
|
||
<input type="number" id="baseZ" value="0" step="0.1">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="baseDiameter">Diameter</label>
|
||
<input type="number" id="baseDiameter" value="10" step="1" min="1">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="baseHeight">Height</label>
|
||
<input type="number" id="baseHeight" value="1" step="0.1" min="0.1">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Sun Configuration -->
|
||
<div class="section">
|
||
<h2>☀️ Sun</h2>
|
||
<div class="form-group">
|
||
<label>Position</label>
|
||
<div class="vector-input">
|
||
<div>
|
||
<div class="vector-label">X</div>
|
||
<input type="number" id="sunX" value="0" step="1">
|
||
</div>
|
||
<div>
|
||
<div class="vector-label">Y</div>
|
||
<input type="number" id="sunY" value="0" step="1">
|
||
</div>
|
||
<div>
|
||
<div class="vector-label">Z</div>
|
||
<input type="number" id="sunZ" value="400" step="1">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="sunDiameter">Diameter</label>
|
||
<input type="number" id="sunDiameter" value="50" step="1" min="1">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Planet Generation -->
|
||
<div class="section">
|
||
<h2>🪐 Planets</h2>
|
||
<div class="form-group">
|
||
<label for="planetCount">Count</label>
|
||
<input type="number" id="planetCount" value="12" min="0" max="50">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="planetMinDiam">Min Diameter</label>
|
||
<input type="number" id="planetMinDiam" value="100" step="10" min="10">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="planetMaxDiam">Max Diameter</label>
|
||
<input type="number" id="planetMaxDiam" value="200" step="10" min="10">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="planetMinDist">Min Distance from Sun</label>
|
||
<input type="number" id="planetMinDist" value="1000" step="100" min="100">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="planetMaxDist">Max Distance from Sun</label>
|
||
<input type="number" id="planetMaxDist" value="2000" step="100" min="100">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Asteroid Generation -->
|
||
<div class="section">
|
||
<h2>☄️ Asteroids</h2>
|
||
<div class="form-group">
|
||
<label for="asteroidCount">Count</label>
|
||
<input type="number" id="asteroidCount" value="20" min="1" max="200">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="forceMultiplier">Force Multiplier</label>
|
||
<input type="number" id="forceMultiplier" value="1.2" step="0.1" min="0.1">
|
||
<div class="help-text">Controls asteroid speed</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="asteroidMinSize">Min Size</label>
|
||
<input type="number" id="asteroidMinSize" value="2" step="0.5" min="0.5">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="asteroidMaxSize">Max Size</label>
|
||
<input type="number" id="asteroidMaxSize" value="7" step="0.5" min="0.5">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="asteroidMinDist">Min Distance</label>
|
||
<input type="number" id="asteroidMinDist" value="100" step="10" min="10">
|
||
<div class="help-text">Distance from start base</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="asteroidMaxDist">Max Distance</label>
|
||
<input type="number" id="asteroidMaxDist" value="250" step="10" min="10">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="button-group">
|
||
<button class="btn-primary" id="generateBtn">Generate & Save</button>
|
||
<button class="btn-success" id="downloadBtn">Download JSON</button>
|
||
<button class="btn-secondary" id="copyBtn">Copy to Clipboard</button>
|
||
</div>
|
||
|
||
<div class="output-section" id="savedLevelsSection">
|
||
<h2>💾 Saved Levels</h2>
|
||
<div id="savedLevelsList"></div>
|
||
</div>
|
||
|
||
<div class="output-section" id="outputSection" style="display: none;">
|
||
<h2>Generated JSON</h2>
|
||
<p style="color: #aaa; font-size: 0.9em; margin-bottom: 15px;">
|
||
You can edit this JSON directly and save your changes.
|
||
</p>
|
||
<textarea id="jsonEditor" style="
|
||
width: 100%;
|
||
min-height: 400px;
|
||
background: #0a0a0a;
|
||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||
border-radius: 5px;
|
||
padding: 15px;
|
||
font-family: 'Courier New', monospace;
|
||
font-size: 0.85em;
|
||
color: #e0e0e0;
|
||
line-height: 1.5;
|
||
resize: vertical;
|
||
box-sizing: border-box;
|
||
"></textarea>
|
||
<div style="display: flex; gap: 10px; margin-top: 15px; justify-content: flex-end;">
|
||
<button class="btn-primary" id="saveEditedJsonBtn">Save Edited JSON</button>
|
||
<button class="btn-secondary" id="validateJsonBtn">Validate JSON</button>
|
||
</div>
|
||
<div id="jsonValidationMessage" style="margin-top: 10px;"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Settings View -->
|
||
<div data-view="settings" style="display: none;">
|
||
<div class="editor-container">
|
||
<a href="#/" class="back-link">← Back to Game</a>
|
||
|
||
<h1>⚙️ Game Settings</h1>
|
||
<p class="subtitle">Configure graphics quality and physics settings</p>
|
||
|
||
<div class="settings-grid">
|
||
<!-- Physics Settings -->
|
||
<div class="section">
|
||
<h2>⚛️ Physics</h2>
|
||
<p style="color: #aaa; font-size: 0.9em; margin-bottom: 20px;">
|
||
Disabling physics can significantly improve performance but will prevent gameplay.
|
||
</p>
|
||
|
||
<div class="form-group">
|
||
<label style="display: flex; align-items: center; cursor: pointer; user-select: none;">
|
||
<input type="checkbox" id="physicsEnabled" style="
|
||
width: 20px;
|
||
height: 20px;
|
||
margin-right: 10px;
|
||
cursor: pointer;
|
||
accent-color: #4CAF50;
|
||
">
|
||
<span>Enable Physics</span>
|
||
</label>
|
||
<div class="help-text">
|
||
Required for collisions, shooting, and asteroid movement. Disabling this will prevent gameplay but may help with debugging or viewing the scene.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Debug Settings -->
|
||
<div class="section">
|
||
<h2>🐛 Developer</h2>
|
||
<p style="color: #aaa; font-size: 0.9em; margin-bottom: 20px;">
|
||
Enable debug logging to console for troubleshooting and development.
|
||
</p>
|
||
|
||
<div class="form-group">
|
||
<label style="display: flex; align-items: center; cursor: pointer; user-select: none;">
|
||
<input type="checkbox" id="debugEnabled" style="
|
||
width: 20px;
|
||
height: 20px;
|
||
margin-right: 10px;
|
||
cursor: pointer;
|
||
accent-color: #2196F3;
|
||
">
|
||
<span>Enable Debug Logging</span>
|
||
</label>
|
||
<div class="help-text">
|
||
When enabled, debug messages will be shown in the browser console. Useful for development and troubleshooting issues.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Ship Physics Settings -->
|
||
<div class="section">
|
||
<h2>🚀 Ship Physics</h2>
|
||
<p style="color: #aaa; font-size: 0.9em; margin-bottom: 20px;">
|
||
Advanced tuning parameters for ship movement and handling. Adjust these to customize how the ship responds to controls.
|
||
</p>
|
||
|
||
<div class="form-group">
|
||
<label for="maxLinearVelocity">Max Linear Velocity</label>
|
||
<input type="number" id="maxLinearVelocity" value="200" step="10" min="50" max="1000">
|
||
<div class="help-text">
|
||
Maximum forward/backward speed of the ship. Higher values allow faster movement.
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="maxAngularVelocity">Max Angular Velocity</label>
|
||
<input type="number" id="maxAngularVelocity" value="1.4" step="0.1" min="0.5" max="5.0">
|
||
<div class="help-text">
|
||
Maximum rotation speed of the ship. Higher values allow faster turning.
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="linearForceMultiplier">Linear Force Multiplier</label>
|
||
<input type="number" id="linearForceMultiplier" value="800" step="50" min="100" max="3000">
|
||
<div class="help-text">
|
||
Acceleration power for forward/backward thrust. Higher values = faster acceleration.
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="angularForceMultiplier">Angular Force Multiplier</label>
|
||
<input type="number" id="angularForceMultiplier" value="15" step="1" min="5" max="50">
|
||
<div class="help-text">
|
||
Torque power for rotation. Higher values = faster rotational acceleration.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Info Section -->
|
||
<div class="section">
|
||
<h2>ℹ️ Quality Level Guide</h2>
|
||
<div style="color: #ccc; font-size: 0.9em; line-height: 1.8;">
|
||
<p><strong style="color: #4CAF50;">Wireframe:</strong> Minimal rendering, shows mesh structure only. Best for debugging or very low-end devices.</p>
|
||
<p><strong style="color: #4CAF50;">Simple Material:</strong> Basic solid colors without textures. Good performance with basic visuals.</p>
|
||
<p><strong style="color: #4CAF50;">Full Texture:</strong> Standard textures with procedural generation. Recommended for most users.</p>
|
||
<p><strong style="color: #4CAF50;">PBR Texture:</strong> Physically-based rendering with enhanced materials. Best visual quality but higher GPU usage.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Current Config Display -->
|
||
<div class="section">
|
||
<h2>💾 Storage Info</h2>
|
||
<div style="color: #ccc; font-size: 0.9em; line-height: 1.8;">
|
||
<p>Settings are automatically saved to your browser's local storage and will persist between sessions.</p>
|
||
<p style="color: #FF9800; margin-top: 10px;">
|
||
⚠️ Note: Changes will take effect when you start a new level. Restart the current level to see changes.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="button-group">
|
||
<button class="btn-primary" id="saveSettingsBtn">💾 Save Settings</button>
|
||
<button class="btn-secondary" id="resetSettingsBtn">🔄 Reset to Defaults</button>
|
||
</div>
|
||
|
||
<div id="settingsMessage" style="
|
||
text-align: center;
|
||
margin-top: 20px;
|
||
font-size: 1.1em;
|
||
opacity: 0;
|
||
transition: opacity 0.3s;
|
||
"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<script type="module" src="/src/main.ts"></script>
|
||
</body>
|
||
</html>
|