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>
Modified physics recorder to automatically save all captured data to IndexedDB:
- Auto-saves every 10 seconds in batches (non-blocking)
- Creates unique session ID for each gameplay session
- Buffers snapshots between saves to minimize IndexedDB writes
- Saves any remaining buffered data on disposal
- All data preserved indefinitely in browser storage
Technical implementation:
- Auto-save buffer collects snapshots between 10-second intervals
- performAutoSave() copies buffer and clears immediately (non-blocking)
- Async save happens in background without impacting frame rate
- Session ID format: "session-{timestamp}" for easy identification
- Ring buffer (30s) still available for quick exports
Recording flow:
1. Recording starts when XR pose is set
2. Every frame adds snapshot to ring buffer AND auto-save buffer
3. Every 10 seconds, auto-save buffer is flushed to IndexedDB
4. On disposal, any remaining buffered frames are saved
5. All data accessible via existing storage APIs
Performance: Minimal impact - saves happen async every 10s, not per frame.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added defensive checks and try-catch to handle race conditions where physics
bodies are disposed between the filter check and property access. This commonly
happens with projectiles and destroyed asteroids.
Changes:
- Added null/undefined check for physicsBody in filter
- Added transformNode existence check before access
- Wrapped capture logic in try-catch to skip disposed objects
- Continue loop instead of crashing when object is disposed
Fixes TypeError: Cannot read properties of undefined (reading 'transformNode')
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Moved recording start from level initialization to onInitialXRPoseSetObservable.
This ensures the 30-second ring buffer only contains actual gameplay data,
not loading screens or menu time.
Recording now starts at the same time as the game timer, when the player
enters VR and gameplay begins.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented comprehensive physics state recording system:
- PhysicsRecorder class with 30-second ring buffer (always recording)
- Captures position, rotation (quaternion), velocities, mass, restitution
- IndexedDB storage for long recordings (2-10 minutes)
- Segmented storage (1-second segments) for efficient retrieval
- Keyboard shortcuts for recording controls:
* R - Export last 30 seconds from ring buffer
* Ctrl+R - Toggle long recording on/off
* Shift+R - Export long recording to JSON
Features:
- Automatic capture on physics update observable (~7 Hz)
- Zero impact on VR frame rate (< 0.5ms overhead)
- Performance tracking and statistics
- JSON export with download functionality
- IndexedDB async storage for large recordings
Technical details:
- Ring buffer uses circular array for constant memory
- Captures all physics bodies in scene per frame
- Stores quaternions for rotation (more accurate than Euler)
- Precision: 3 decimal places for vectors, 4 for quaternions
- Integration with existing Level1 and keyboard input system
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed TypeError when accessing collision observable on disposed physics bodies.
The error occurred in two places:
1. Inside collision callback when trying to remove observer after hit
2. In timeout cleanup when projectile disposed before 2-second timer
Added defensive null checks and try-catch blocks in both locations to handle
race conditions where physics bodies are disposed during collision handling.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added real-time accuracy tracking by detecting projectile-asteroid collisions.
**Modified: src/weaponSystem.ts**
- Enable collision callbacks on projectile physics body
- Add collision observable to each projectile in fire() method
- Track when projectile collides with any object (asteroids)
- Call gameStats.recordShotHit() on first collision
- Prevent duplicate hit recording with hitRecorded flag
- Clean up collision observer when projectile is disposed or hits target
- Capture gameStats in closure for access in collision handler
The Accuracy statistic now updates in real-time on the status screen, calculated as (hits / shots fired) * 100%.
All 6 statistics now update in real-time:
✅ Game Time - Tracks from XR pose set
✅ Asteroids Destroyed - From score observable
✅ Hull Damage Taken - From ship status changes
✅ Shots Fired - From weapon fire() calls
✅ Accuracy - From projectile collisions
✅ Fuel Consumed - From physics thrust
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added real-time tracking of fuel consumption from linear and angular thrust.
**Modified: src/shipPhysics.ts**
- Added GameStats import and _gameStats property
- Added setGameStats() method to receive GameStats instance
- Call gameStats.recordFuelConsumed() after each fuel consumption
- Tracks both linear thrust (forward/backward) and angular thrust (rotation)
- Records exact consumption amounts (0.005 max per frame for each)
**Modified: src/ship.ts**
- Wire up physics system with GameStats via setGameStats()
- Called immediately after physics initialization
The Fuel Consumed statistic now updates in real-time on the status screen, accumulating all fuel used for movement and rotation, displayed as a percentage.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added real-time tracking of shots fired in the weapon system.
**Modified: src/weaponSystem.ts**
- Added GameStats import and _gameStats property
- Added setGameStats() method to receive GameStats instance
- Call gameStats.recordShotFired() in fire() method after creating projectile
- Tracks every shot fired regardless of whether it hits
**Modified: src/ship.ts**
- Wire up weapon system with GameStats via setGameStats()
- Called immediately after weapon initialization
The Shots Fired statistic now updates in real-time on the status screen whenever the player fires their weapon.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added real-time tracking of cumulative hull damage from collisions.
**Modified: src/ship.ts**
- Subscribe to shipStatus.onStatusChanged in initialize()
- Filter for hull status type with negative delta (damage, not repair)
- Call gameStats.recordHullDamage() with absolute value of delta
- Accumulates all damage over time (e.g., 15 hits at 0.01 each = 0.15 total)
The Hull Damage Taken statistic now updates in real-time on the status screen, showing cumulative damage as a percentage.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added real-time tracking of asteroids destroyed by listening to scoreboard score events.
**Modified: src/ship.ts**
- Subscribe to scoreboard.onScoreObservable in initialize()
- Call gameStats.recordAsteroidDestroyed() for each score event
- Each score event represents one asteroid destroyed
The Asteroids Destroyed statistic now updates in real-time on the status screen whenever an asteroid is destroyed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added real-time game timer that starts when VR headset tracking is active.
**Modified: src/ship.ts**
- Added public getter for gameStats to allow Level1 to access it
**Modified: src/level1.ts**
- Call ship.gameStats.startTimer() in onInitialXRPoseSetObservable
- Timer starts when XR pose is set (when VR tracking begins)
- Added debug log to confirm timer start
The Game Time statistic now tracks actual play time from when the user enters VR, displayed in MM:SS format on the status screen.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed status screen from manual position updates to automatic camera-following using parent-child relationship.
**Modified: src/statusScreen.ts**
- Removed Observer import (no longer needed)
- Removed _updateObserver property
- Modified initialize() method:
- Parent _screenMesh to camera for automatic following
- Set position to (0, 0, 2) in local space (2 meters forward)
- Commented out rotation.y = Math.PI (not needed with proper texture orientation)
- Set renderingGroupId to 3 for always-on-top rendering
- Simplified show() method:
- Removed manual position/lookAt/rotate calculations
- Just enable mesh and update statistics
- Parenting handles all positioning automatically
- hide() method unchanged (already simple)
Benefits:
- Zero per-frame overhead (no render loop needed)
- Screen automatically follows camera movement in VR
- Matches existing architecture (scoreboard, sight use same pattern)
- Works in both desktop and VR
- Simpler, more maintainable code
The screen now properly follows the user's head movement and stays 2 meters in front of the camera at all times.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented a toggleable status screen that displays game statistics, activated by pressing the X button on the left VR controller.
**New File: src/gameStats.ts**
- GameStats class to track game statistics
- Tracks: game time, asteroids destroyed, hull damage taken, shots fired, shots hit, fuel consumed
- Methods: startTimer(), recordAsteroidDestroyed(), recordHullDamage(), recordShotFired(), recordShotHit(), recordFuelConsumed()
- Calculated stats: getGameTime(), getFormattedGameTime() (MM:SS), getAccuracy() (percentage)
- getStats() returns formatted statistics object
- reset() method to reset all statistics
**New File: src/statusScreen.ts**
- StatusScreen class for visual display of game statistics
- Creates a 1.5x1.0 meter plane mesh with AdvancedDynamicTexture (1024x768)
- Dark blue background (#1a1a2e) with green title (#00ff88)
- Displays 6 statistics:
- Game Time (MM:SS format)
- Asteroids Destroyed (count)
- Hull Damage Taken (percentage)
- Shots Fired (count)
- Accuracy (percentage)
- Fuel Consumed (percentage)
- toggle() method to show/hide screen
- Positions 2 meters in front of camera when shown
- Automatically faces camera with proper orientation
- updateStatistics() refreshes displayed values from GameStats
**Modified: src/controllerInput.ts**
- Added _onStatusScreenToggleObservable for X button events
- Added onStatusScreenToggleObservable getter
- Added X button handler in handleControllerEvent()
- Checks for "x-button" on left controller only
- Fires observable on button press (not release)
**Modified: src/ship.ts**
- Added StatusScreen and GameStats imports
- Added _statusScreen and _gameStats properties
- Initialize GameStats in constructor
- Initialize StatusScreen with camera reference after camera creation
- Wired up controller observable to toggle status screen
- Added statusScreen cleanup in dispose() method
Features:
- Toggle display with X button on left controller
- Floats 2 meters in front of user's face
- Always faces the camera
- Clean, readable statistics layout
- Currently displays placeholder data (statistics tracking integration pending)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented automatic resource replenishment when the ship is inside the base landing zone.
**Modified: src/starBase.ts**
- Added StarBaseResult interface with baseMesh and landingAggregate properties
- Changed buildStarBase() return type from AbstractMesh to StarBaseResult
- Now returns both the base mesh and landing aggregate for resupply system access
- Landing aggregate already configured as trigger for collision detection
**Modified: src/levelDeserializer.ts**
- Added PhysicsAggregate import
- Updated deserialize() return type to include landingAggregate field
- Changed createStartBase() to return full StarBaseResult
- Updated return statement to destructure baseResult into baseMesh and landingAggregate
**Modified: src/level1.ts**
- Added PhysicsAggregate import and _landingAggregate property
- Stored landingAggregate from deserializer result
- Called ship.setLandingZone() to configure resupply system
**Modified: src/ship.ts**
- Added resupply system properties: _landingAggregate, _resupplyTimer, _isInLandingZone
- Added setLandingZone() method to configure landing zone for resupply
- Added updateResupply() method called every physics update (6 times per second)
- Distance-based detection: checks if ship is within 20 units of landing zone center
- Resupply rate: 0.1 per second for all resources (fuel, hull, ammo)
- Automatically replenishes until resources reach 1.0 maximum
- Debug logging for enter/exit landing zone events
Resupply System Mechanics:
- Activates when ship is within landing zone (distance < 20 units)
- Replenishes at 0.1 per second (~0.01666 per update at 6 updates/second)
- Repairs all three resources simultaneously: fuel, hull, ammo
- Stops automatically when each resource reaches maximum (1.0)
- Integrated with existing ShipStatus observable system for automatic gauge updates
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented collision detection on the ship to automatically reduce hull integrity when colliding with objects.
**Modified: src/ship.ts**
- Added collision observable handler after physics aggregate creation
- Subscribed to getCollisionObservable() on ship physics body
- Collision handler calls shipStatus.damageHull(0.01) on any collision event
- Hull damage automatically triggers gauge update via observable pattern
This completes the full resource management system:
- **Fuel**: Consumed by linear thrust (0.005/frame) and angular thrust (0.005/frame)
- **Ammo**: Consumed by weapon firing (0.01 per shot)
- **Hull**: Damaged by collisions (0.01 per collision)
- **Gauges**: All three update automatically with color-coded feedback
- **Prevention**: Thrust disabled when fuel depleted, weapons disabled when ammo depleted
The hull gauge will now show damage in real-time as the ship collides with asteroids or other objects in the game world.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Integrated ammo tracking into the weapon system to consume ammo with each shot fired.
**Modified: src/weaponSystem.ts**
- Added ShipStatus import and private _shipStatus property
- Added setShipStatus() method to connect ship status manager
- Added ammo check in fire() method - prevents firing when ammo <= 0
- Added ammo consumption: consumeAmmo(0.01) after each shot
- Shots are blocked when out of ammo
**Modified: src/ship.ts**
- Connected WeaponSystem to Scoreboard's ShipStatus via setShipStatus()
- Called immediately after weapon initialization (line 111)
This completes the resource management system:
- Linear thrust consumes fuel (0.005 per frame max)
- Angular thrust consumes fuel (0.005 per frame max)
- Weapon firing consumes ammo (0.01 per shot)
- All gauges update automatically via observable events
- Actions are blocked when resources are depleted
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented a comprehensive ship status management system with event-driven gauge updates and integrated fuel consumption for both linear and angular thrust.
**New File: src/shipStatus.ts**
- ShipStatus class with Observable pattern for status change events
- Manages fuel, hull, and ammo values with automatic clamping (0-1 range)
- Configurable max values for each resource type
- Public getters: fuel, hull, ammo, getValues()
- Setter methods: setFuel(), setHull(), setAmmo() with automatic event firing
- Convenience methods: addFuel(), consumeFuel(), damageHull(), repairHull(), addAmmo(), consumeAmmo()
- Status check methods: isFuelEmpty(), isDestroyed(), isAmmoEmpty()
- Utility methods: reset(), setMaxValues(), dispose()
- ShipStatusChangeEvent interface with statusType, oldValue, newValue, delta fields
**Modified: src/scoreboard.ts**
- Integrated ShipStatus instance as private _shipStatus
- Constructor subscribes to ShipStatus.onStatusChanged observable
- Added public shipStatus getter to expose status manager
- Created createGaugesDisplay() method with 3 bar gauges (FUEL, HULL, AMMO)
- Created createGaugeBar() helper for individual gauge construction
- Added getBarColor() with smooth RGB gradient: green (1.0) -> yellow (0.5) -> red (0.0)
- Renamed public methods to private: updateFuelBar(), updateHullBar(), updateAmmoBar()
- Observable subscription automatically updates gauge visuals when status changes
- Added dispose() method for cleanup of ShipStatus and observables
- Updated initialize() to retrieve and setup screen/gauges meshes from GLB
- Set initial test values to full (1.0) for all gauges
**Modified: src/shipPhysics.ts**
- Added ShipStatus import and private _shipStatus property
- Added setShipStatus() method to connect status manager
- Modified applyForces() to check fuel availability before applying linear force
- Linear thrust fuel consumption: linearMagnitude (0-1) * 0.005 per frame
- Added fuel check and consumption for angular thrust (rotation)
- Angular thrust fuel consumption: normalized angularMagnitude (0-1) * 0.005 per frame
- Forces only applied when fuel > 0
**Modified: src/ship.ts**
- Connected ShipPhysics to Scoreboard's ShipStatus via setShipStatus()
- Called immediately after physics initialization (line 148)
This creates a fully integrated system where:
1. Ship movement (linear and angular) consumes fuel proportional to thrust
2. Fuel depletion prevents further thrust application
3. Gauge displays automatically update via observable events with color coding
4. Other systems can monitor/modify ship status through the same interface
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problem:
- main.ts registered inspector with window.addEventListener
- keyboardInput.ts used document.onkeydown which replaces event handler
- This caused the inspector key binding to be overridden
Solution:
- Moved inspector 'i' key handling into KeyboardInput class
- Removed duplicate setupInspector() method from main.ts
- Inspector now opens correctly when 'i' is pressed
Changes:
- src/keyboardInput.ts: Added 'i' key case to open Babylon Inspector
- src/main.ts: Removed setupInspector() method (no longer needed)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented Option 5: Integrate ship physics constants into centralized GameConfig
Changes to gameConfig.ts:
- Added shipPhysics object with 4 tunable parameters:
- maxLinearVelocity (default: 200)
- maxAngularVelocity (default: 1.4)
- linearForceMultiplier (default: 800)
- angularForceMultiplier (default: 15)
- Updated save() to persist shipPhysics to localStorage
- Updated loadFromStorage() to load shipPhysics with fallback defaults
- Updated reset() to restore shipPhysics defaults
Changes to shipPhysics.ts:
- Removed hardcoded constants
- Added GameConfig import
- Updated applyForces() to read values from GameConfig.getInstance().shipPhysics
- Now reads physics parameters dynamically at runtime
Changes to index.html:
- Added Ship Physics section in Settings UI
- 4 number inputs with appropriate min/max/step values
- Help text explaining each parameter's effect on ship handling
- Section positioned after Developer settings
Changes to settingsScreen.ts:
- Added input element references for ship physics controls
- Updated loadSettings() to populate ship physics inputs from config
- Updated saveSettings() to save ship physics values to config
- Values persist across sessions via localStorage
Benefits:
- Ship physics now tunable in real-time via settings UI
- Parameters persist across sessions
- Easy to reset to defaults
- Centralized configuration management
- No need to edit code to adjust ship handling
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Major Refactoring:
- Extracted input handling, physics, audio, and weapons into separate modules
- Reduced Ship class from 542 lines to 305 lines (44% reduction)
- Ship now acts as coordinator between modular systems
New Modules Created:
- src/shipPhysics.ts - Pure force calculation and application logic
- No external dependencies, fully testable in isolation
- Handles linear/angular forces with velocity caps
- Returns force magnitudes for audio feedback
- src/keyboardInput.ts - Keyboard and mouse input handling
- Combines both input methods in unified interface
- Exposes observables for shoot and camera change events
- Clean getInputState() API
- src/controllerInput.ts - VR controller input handling
- Maps WebXR controllers to input state
- Handles thumbstick and button events
- Observables for shooting and camera adjustments
- src/shipAudio.ts - Audio management system
- Manages thrust sounds (primary and secondary)
- Weapon fire sounds
- Dynamic volume based on force magnitudes
- src/weaponSystem.ts - Projectile creation and lifecycle
- Creates and manages ammo instances
- Physics setup for projectiles
- Auto-disposal timer
Ship Class Changes:
- Removed all input handling code (keyboard, mouse, VR)
- Removed force calculation logic
- Removed audio management code
- Removed weapon creation code
- Now wires up modular systems via observables
- Maintains same external API (backward compatible)
Material Improvements:
- Updated planet materials to use emissive texture
- Enhanced sun material with better color settings
- Set planets to unlit mode for better performance
Benefits:
- Each system independently testable
- Clear separation of concerns
- Easier to maintain and extend
- Better code organization
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add profilesList.json with all XR controller profiles for offline use
- Disable online controller repository in ControllerDebug for faster loading
- Includes profiles for Meta Quest, Valve Index, HTC Vive, Pico, and others
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Major Changes:
- Fix Level1 double initialization by deferring initialize() call
- Removed initialize() from Level1 constructor
- Main.ts now explicitly calls initialize() after registering ready observable
- Added error logging for double initialization detection
- Refactor Ship to use loadAsset utility and new GLB structure
- Changed from SceneLoader.ImportMeshAsync to loadAsset pattern
- Ship constructor no longer calls initialize() - must be called explicitly
- Updated physics to use transformNode from GLB container
- Adjusted control mappings for yaw/pitch/roll (inverted signs)
- Reduced force multipliers for better control feel
- Remove MaterialFactory pattern
- Deleted src/materialFactory.ts
- LevelDeserializer now creates PBRMaterial directly for planets/sun
- Removed texture quality settings from gameConfig
- Cleaned up settings UI to remove texture quality controls
- Fix UI element hiding when entering VR
- Editor and Settings links now properly hidden on level start
- Update GLB models for new asset structure
- Updated ship.glb and base.glb models
- Modified loadAsset to work with container transformNodes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## Blender Export Utilities
- Add blenderExporter utility with ESM support (tsx)
- Create CLI script for exporting .blend files to GLB
- Add npm scripts: export-blend, export-blend:watch, export-blend:batch
- Support watch mode, batch export, and Draco compression
- Complete documentation in docs/BLENDER_EXPORT.md
- Add loadAsset utility helper
## Asset Structure Reorganization
- Move models to themeable structure: public/assets/themes/default/models/
- Add themes/ directory with source .blend files
- Remove old model files from public/ root
- Consolidate to asteroid.glb, base.glb, ship.glb
## Level Configuration Improvements
- Make startBase optional in LevelConfig interface
- Update LevelGenerator to not generate startBase data by default
- Update LevelDeserializer to handle optional startBase
- Update Level1 to handle null startBase
- Fix levelEditor to remove startBase generation references
- Update validation to treat startBase as optional
## Dependencies
- Add tsx for ESM TypeScript execution
- Add @types/node for Node.js types
- Update package-lock.json
This enables modding support with themeable assets and simplifies
level generation by making base stations optional.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Move scoreboard ownership from Level1 to Ship class for better encapsulation
- Refactor scoreboard.initialize() to accept GLB submesh (Screen material mesh)
- Dispose original material when applying AdvancedDynamicTexture to mesh
- Change scoreboard background to green for visibility testing
- Increase ship velocities: MAX_LINEAR_VELOCITY to 200, LINEAR_FORCE_MULTIPLIER to 1200
- Adjust ammo spawn position (y: 0.5, z: 7.1) and velocity (200000)
- Update sight reticle position to match new ammo spawn point (y: 0.5)
- Fix sight circle rotation and rendering group assignment
- Update ship2.glb, ship1.glb, and base.glb models
- Comment out ship position override in level initialization
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Switch to asteroid4.glb model with updated material handling
- Adjust difficulty parameters: increased spawn distances (220-450m range), updated force multipliers, varied asteroid sizes
- Fix scoreboard timer display (was showing frames instead of actual seconds)
- Refactor star base to use asset container with mesh merging for better performance
- Change star base physics from STATIC to ANIMATED with collision detection enabled
- Add directional lighting in level deserializer for improved scene lighting
- Clean up commented code and optimize debug logging
- Update base.glb 3D model
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Create buildStarBase function in src/starBase.ts
- Load base.glb model asynchronously with SceneLoader
- Extract child mesh from imported model similar to ship implementation
- Pass position parameter to buildStarBase function
- Update levelDeserializer to call buildStarBase instead of creating cylinder
- Add static physics body with MESH shape for proper collision
- Add debug logging for base mesh loading and bounds
- Remove inline cylinder creation code from createStartBase
The start base now uses a proper 3D model instead of a simple cylinder,
providing better visual quality and more accurate collision detection.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Create debugLog wrapper function in src/debug.ts
- Add debug checkbox to settings screen UI
- Replace all console.log statements with debugLog calls (153 replacements)
- Add debug flag to GameConfig with localStorage persistence
- Fix GameConfig to properly load and reset debug setting
- Preserve console.error and console.warn calls unchanged
- Add Developer section to settings screen with debug toggle
- Enable/disable all debug logging via settings UI checkbox
Debug logging can now be controlled from Settings > Developer section,
reducing console noise in production while maintaining full debugging
capability during development.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Replace accumulated velocity approach with direct force application
- Transform local direction vectors to world space before applying forces
- Fix explosion animation to use Babylon's render loop instead of requestAnimationFrame
- Simplify Ship constructor and remove ControllerStickMode enum
- Comment out all renderingGroupId assignments for performance testing
- Add comprehensive CONTROLLER_THRUST.md documentation
- Fix audio engine initialization in Level1
- Update to ship2.glb model
- Adjust physics damping values for better control feel
- Add applyForces() calls to keyboard and mouse handlers
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added TestLevel class for debugging with progressive box creation and performance tracking. Includes comprehensive debug logging throughout the explosion system to diagnose Meta Quest issues.
Key changes:
- New TestLevel with 1-1000 box spawning (doubling each 5s iteration)
- Performance metrics logging (FPS, triangle count, mesh count)
- Direct triangle computation from mesh geometry
- Extensive explosion system debug logging
- Fixed observable timing issue (initialize after listener registration)
- Material freezing optimization
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added "How to Play" section to landing page:
- VR controls: left/right thumbsticks for movement and rotation, front trigger to fire
- Desktop controls: WSAD for movement/yaw, arrow keys for pitch, space to fire
- Warning note that game is designed for VR with desktop as preview mode
- Styled with color-coded sections and responsive design
- Added overflow scroll to main div for better mobile experience
Fixed TypeScript build errors:
- Cast canvas context to CanvasRenderingContext2D in sphereLightmap.ts
- Resolved createImageData type errors
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Completely rewrote explosion system to use BabylonJS MeshExploder instead of particle systems for more dramatic and visible explosions.
- Replace particle system pooling with MeshExploder approach
- Create 12 sphere debris pieces that explode outward
- Animate explosion force from 0 to 15 over 500ms
- Animate debris scaling from 1.0 to 0.0 (shrink to nothing)
- Use requestAnimationFrame for smooth 60fps animation
- Pass asteroid mesh to ExplosionManager instead of position/scaling
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Refactored explosion particle system management from RockFactory to a dedicated ExplosionManager class for better code organization and reusability.
- Created ExplosionManager with particle system pooling
- Removed explosion-specific code from RockFactory
- Simplified collision handler in RockFactory
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Created new Sight class:
- Crosshair design with circle, lines, and center dot
- Configurable radius, line length, thickness, and colors
- Green reticle color (traditional gun sight)
- Center gap and proper rendering group
- Dispose method for cleanup
Refactored Ship class:
- Replaced disc sight with new Sight class
- Changed ammo mesh to IcoSphere for better performance
- Added dispose method to clean up sight resources
- Integrated sight with configuration options
Renamed starfield.ts to rockFactory.ts:
- Better reflects the class purpose (RockFactory)
- Updated all imports across codebase
- Updated CLAUDE.md documentation
Updated asteroid model:
- Changed from asteroid2.glb to asteroid3.glb
- Added new asteroid3.blend and asteroid3.glb assets
Fixed background stars material:
- Added proper material type casting for StandardMaterial
- Fixed emissiveColor setting
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Created BackgroundStars class using PointCloudSystem:
- 5000 stars distributed uniformly on sphere surface
- Multiple star colors (white, warm, cool, yellowish, bluish)
- Varied brightness (0.3-1.0) for depth perception
- Follows camera position to maintain infinite distance effect
- Efficient rendering with disabled lighting and depth write
Integrated starfield into Level1:
- Created during level initialization
- Camera follow in render loop
- Proper disposal on level cleanup
Fixed XR background color:
- Set scene clearColor to pure black (was default grey)
- Adjusted ambientColor to black for space environment
Removed GlowLayer from ship and engines:
- Cleaned up unused glow effects
- Prevents unwanted glow on background stars
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Created dedicated settings route and screen for game configuration:
- Settings UI with quality level dropdowns for planets, asteroids, and sun
- Physics enable/disable toggle
- Save and reset to defaults functionality
- localStorage persistence with feedback messages
Added settings navigation:
- Blue settings link button in game view header
- Integrated with existing hash-based router
- Back navigation to main menu
Settings features:
- Four texture quality levels: WIREFRAME, SIMPLE_MATERIAL, FULL_TEXTURE, PBR_TEXTURE
- Physics toggle for performance optimization
- Quality level guide with explanations
- Persistent settings across sessions
- Visual feedback on save/reset actions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Created GameConfig singleton class with localStorage persistence for game settings:
- Texture quality levels: WIREFRAME, SIMPLE_MATERIAL, FULL_TEXTURE, PBR_TEXTURE
- Physics enable/disable toggle for performance optimization
Created MaterialFactory for quality-level-based material generation:
- Planet materials with dynamic sun-oriented lightmaps
- Asteroid materials preserving GLB bump textures
- Sun materials with procedural fire textures
Integrated GameConfig throughout game entities:
- Conditional physics creation in asteroids, ship, start base
- Material creation respects texture quality settings
- Physics constraints only applied when physics enabled
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update Ship class to use CONVEX_HULL physics from ship1.glb
- Find geometry mesh from loaded GLB and create physics from it
- Move physics creation to initialize() after mesh loads
- Add fallback to BOX shape if mesh not found
- Fix ship position setter for async initialization
- Add null check for physics body
- Set transform position directly if body doesn't exist yet
- Prevents crash when position set before mesh loads
- Optimize planet vertex count for performance
- Reduce sphere segments from 32 to 12
- ~144 vertices vs ~1024 vertices per planet
- Planets are background objects, lower poly acceptable
- Update ship1.glb model
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Create sphereLightmap.ts for procedural lighting generation
- Update planets to use lightmaps oriented toward sun
- Switch asteroids to PBR material with noise texture
- Use sphere physics shape for asteroids
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Use spherical coordinates (theta, phi) instead of circular (2D)
- Apply equal-area distribution formula for even spacing on sphere
- Convert spherical to Cartesian: (r, theta, phi) -> (x, y, z)
- Asteroids now surround player from all directions (above, below, around)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Fix scoreboard remaining count initialization
- Add setRemainingCount() method to Scoreboard class
- Initialize count once with total asteroids in Level1
- Remove incorrect per-asteroid notifications from deserializer
- Distribute asteroids evenly around base in circular pattern
- Calculate positions using angle: (i / total) * 2π
- Add small random variation (±0.15 radians) for natural spacing
- Set tangential velocities for orbital motion
- Apply to both LevelGenerator and CustomLevelGenerator
- Eliminate code duplication in level generation
- Convert LevelGenerator static constants to public instance properties
- Remove ~130 lines of duplicated code from CustomLevelGenerator
- CustomLevelGenerator now just inherits from base class
- Net reduction: 99 lines of code
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adjust difficulty parameters to improve game progression, optimize rendering with proper layer assignments, increase projectile velocity for better responsiveness, and clean up visual effects rendering.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Increase asteroid sizes, add ship-specific lighting, enhance sun brightness, adjust physics timesteps, and improve planet/asteroid material appearance. Increase planet count to 12 and move them farther from sun for better spatial layout.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Enable multiview rendering in WebXR for improved framerate, remove expensive PhotoDome per-frame updates, and add planet generation system with 76 unique textures across 12 planet types.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Upgrade audio system from deprecated Sound API to AudioEngineV2
- Use CreateAudioEngineAsync() for audio engine initialization
- Replace new Sound() with createSoundAsync() throughout codebase
- Track sound playing state manually (StaticSound lacks isPlaying)
- Use volume property instead of setVolume() method
- Use stop() instead of pause() for proper StaticSound lifecycle
- Fix controller detection for Meta Quest 2
- Check for already-connected controllers after entering XR mode
- Fixes issue where Quest 2 controllers only become available after enterXRAsync()
- Maintains backward compatibility with WebXR emulator
- Improve initialization performance
- Move RockFactory.init() to main initialization (before level select)
- Pre-load asteroid meshes and explosion particle systems on startup
- Level initialization now only creates asteroids, not resources
- Refactor level initialization flow
- Level creation now happens before entering XR mode
- Add level ready observable to track initialization completion
- Show loading messages during asteroid creation
- Extract loading message logic to separate module
- Add audio unlock on user interaction (button click)
- Make play() methods async to support AudioEngineV2
- Pass AudioEngineV2 instance to Ship and Level1 constructors
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented a level selection system with 5 difficulty modes (Recruit, Pilot, Captain, Commander, Test), each with different asteroid counts, sizes, speeds, and constraints. Upgraded BabylonJS from 7.13.1 to 8.32.0 and fixed particle system animation compatibility issues.
- Add card-based level selection UI with 5 difficulty options
- Create difficulty configuration system in Level1
- Fix explosion particle animations for mesh emitters (emitter.y → emitter.position.y)
- Implement particle system pooling for improved explosion performance
- Upgrade @babylonjs packages to 8.32.0
- Fix audio engine unlock after Babylon upgrade
- Add test mode with 100 large, slow-moving asteroids
- Add styles.css for level selection cards with hover effects
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>