diff --git a/README.md b/README.md index 29ec5bc..5470d81 100644 --- a/README.md +++ b/README.md @@ -108,9 +108,12 @@ sfx/ └── sfx_chest_close.wav # Chest closing sound ``` -**Legend:** -- ✓ Unchanged from original (already well-structured) -- Without checkmark = newly extracted/refactored +**Module Organization:** +- **game/**: Core game logic (state, generation, collision, audio) +- **controls/**: Input and event handling +- **assets/**: Reusable factories (materials, textures) +- **ui/**: Visual feedback (HUD, overlays) +- Root level (babylon_panel.js): Main orchestrator that ties everything together ### Core Modules @@ -289,21 +292,96 @@ Page Load → babylon_panel.js - Reusable styling/animation controls - Can easily add new HUD elements -#### 11. **game/state.js** (Shared State) ✓ -**Already Well-Structured — No Changes** -Centralized game configuration and runtime state +#### 11. **game/state.js** (Shared State) +**Purpose:** Centralized game state management to prevent coupling between modules -#### 12. **game/maze.js** (Procedural Generation) ✓ -**Already Well-Structured — No Changes** -Recursive backtracking with seeded RNG +**Structure:** +```javascript +window.mazeGameState = { + config: { + seed, // Reproducible maze generation + level, // Current progression level + mazeWidth: 11, // Base width (increases per level) + mazeHeight: 11, // Base height + minChestDeadEnds: 2 // Min chests to place + }, + runtime: { + runActive, // Is gameplay active? + hasKey, // Player collected key? + roundsCompleted, // Levels completed this run + elapsedSeconds, // Countdown timer (60 → 0) + message // Status text for HUD + } +} +``` -#### 13. **game/grid.js** (Coordinate Utilities) ✓ -**Already Well-Structured — No Changes** -Grid-to-world conversions and walkability checks +**Benefits:** +- Single source of truth for all game state +- All modules read/write to same object (no data duplication) +- Easy to serialize/save game state +- Enables time-travel debugging via console inspection -#### 14. **game/sfx.js** (Audio System) ✓ -**Already Well-Structured — No Changes** -ES6 import-based audio loading and playback +#### 12. **game/maze.js** (Procedural Generation) +**Purpose:** Generate solvable, deterministic mazes using seeded randomization + +**Functions:** +- `seededRng(seed)` — Deterministic random number generator (same seed = same sequence) +- `generateMazeGrid(w, h, seed)` — Recursive backtracking maze algorithm +- `findDeadEnds(grid)` — Locate dead-end cells for chest/exit placement + +**Algorithm:** +Recursive backtracking with seeded RNG ensures: +- Every maze is solvable (connected) +- Same seed produces identical maze (perfect for replays) +- Configurable difficulty (width/height parameters) + +**Benefits:** +- Deterministic yet varied level generation +- No performance issues (pre-computed before rendering) +- Perfect reproducibility for competitive play + +#### 13. **game/grid.js** (Coordinate Utilities) +**Purpose:** Convert between grid coordinates and 3D world space for collision detection + +**Functions:** +- `gridCellToWorld(grid, x, y, cellSize)` — Grid cell → world position (returns `{x, z}`) +- `isWalkableCell(grid, x, y)` — Check if cell is navigable (not a wall) + +**Key Insight:** +Mazes are generated as 2D grids (1 = wall, 0 = path). This module bridges the gap between grid coordinates used by `maze.js` and 3D world positions used by `level-generator.js`. + +**Benefits:** +- Decouples grid logic from spatial positioning +- Makes collision detection and pathfinding calculations simpler +- Reusable for any grid-based game mechanic + +#### 14. **game/sfx.js** (Audio System) +**Purpose:** Load and playback polyphonic sound effects with Web Audio API + +**Functions:** +- `playSfx(name, volume)` — Play a sound effect by name (cloned for polyphony) +- `primeSfx()` — Initialize audio context (required by browser for first sound) + +**Audio Files** (imported via ES6, bundled by Vite): +```javascript +import clickUrl from "../../sfx/sfx_click.wav" +import chestOpenUrl from "../../sfx/sfx_chest_open.wav" +import chestCloseUrl from "../../sfx/sfx_chest_close.wav" +import keyUrl from "../../sfx/sfx_key.wav" +import clockUrl from "../../sfx/sfx_clock.wav" +import stepUrl from "../../sfx/sfx_step.wav" +import winUrl from "../../sfx/sfx_win.wav" +import loseUrl from "../../sfx/sfx_lose.wav" +``` + +**Polyphony via Cloning:** +Each call to `playSfx()` clones the audio node, allowing multiple sounds to play simultaneously (e.g., footsteps + clock alarm). + +**Benefits:** +- Polyphonic sound effects (no cutting each other off) +- Browser-compatible audio context priming +- ES6 imports ensure assets are bundled by Vite +- Centralized audio management (easy to add volume controls, reverb, etc.) --- @@ -330,7 +408,6 @@ ES6 import-based audio loading and playback - Audio context requires user interaction priming via `primeSfx()`—automatically triggered on first W/A/S/R key press **Performance Optimizations:** -- Vite's ES6 module bundling and tree-shaking - Asset optimization (textures, audio) - Efficient raycasting for chest targeting (not per-pixel) - Single draw call per maze (not per cell) @@ -358,99 +435,49 @@ ES6 import-based audio loading and playback --- -## Refactoring Summary (Phase 3 Complete) - -### Code Metrics -| Metric | Before | After | Change | -|--------|--------|-------|--------| -| Main file (babylon_panel.js) | 570 lines | 195 lines | -66% | -| Total source files | 5 | 14 | +9 new modules | -| Build modules | 355 | 364 | +9 (new files) | -| Cyclomatic complexity | High | Low | Each module < 20 lines avg | - -### Dependency Graph -``` -babylon_panel.js (Orchestrator) -├── game/scene-init.js -├── game/camera-manager.js -├── controls/input-handler.js -│ └── game/sfx.js -├── game/level-generator.js -│ ├── game/maze.js -│ ├── game/grid.js -│ └── assets/materials.js -├── game/game-loop.js -│ ├── game/sfx.js -│ ├── game/collisions.js -│ └── ui/hud.js -├── game/screen-manager.js -│ └── p5_particles.js -└── game/state.js (shared by all) -``` - -### Module Characteristics -- **No circular dependencies:** Each module imports only from modules below it -- **Shared state:** All modules read/write `window.mazeGameState` (single source of truth) -- **Event-driven:** Input → callbacks → state update → HUD refresh -- **Callback pattern:** Higher modules pass callbacks to lower modules for decoupling - -### Refactoring Safety Checklist -✓ **Build tested:** `npm run build` completes with 364 modules, no errors -✓ **No breaking changes:** `window.mazeGameApi.generateLevel()` still exported -✓ **Audio bundling intact:** All 8 SFX files in dist/assets/ with hashes -✓ **Backwards compatible:** All gameplay mechanics unchanged -✓ **No new dependencies:** Uses existing npm packages only -✓ **ESM imports work:** Vite resolves all relative paths correctly - -### Next Steps for Future Maintenance -1. **Add new interaction types:** Extend `game/collisions.js` with new raycasts -2. **Add gamepad support:** Extend `controls/input-handler.js` with gamepad listeners -3. **Add new screens:** Add functions to `game/screen-manager.js` -4. **Add new camera modes:** Extend `game/camera-manager.js` with new camera types -5. **Add sound designer tools:** Extend `game/sfx.js` with volume/pan controls - ---- - ## Design Decisions & Rationale -### Refactoring Strategy (Phase 3 Complete) -**Objective:** Improve code maintainability without breaking gameplay +### 1. Modular Architecture with Separation of Concerns +**Decision:** Organize code into 14 focused modules rather than monolithic files -**Approach:** Modular separation of concerns by extracting 9 new modules from the original monolithic `babylon_panel.js` (570 lines → 195 lines). +**Rationale:** +- Each module has a single responsibility (scene setup, input handling, collision detection, etc.) +- No circular dependencies — clean dependency flow from orchestrator down to utilities +- Easier to test, debug, and extend individual features +- New developers can understand one module without understanding the whole codebase +- Easy to add features: extend relevant modules instead of modifying monolithic files -#### Why This Structure? +**Architecture Pattern:** +``` +babylon_panel.js (orchestrator) ← high-level control + ├→ game/scene-init.js ← rendering setup + ├→ game/camera-manager.js ← camera control + ├→ controls/input-handler.js ← event routing + ├→ game/level-generator.js ← spatial layout + ├→ game/game-loop.js ← frame updates + ├→ game/screen-manager.js ← UI transitions + └→ game/state.js ← shared data (all modules) +``` -| Module | Benefit | -|--------|---------| -| **scene-init.js** | Isolate Babylon.js boilerplate from game logic | -| **camera-manager.js** | Encapsulate complex camera configuration; easy to test/add modes | -| **level-generator.js** | Pure spatial functions; reusable for future level types | -| **game-loop.js** | Frame-by-frame logic visible in one place; easier to debug timing | -| **collisions.js** | Isolated raycasting; reusable for new interaction types | -| **input-handler.js** | Centralized event routing; easy to add gamepad/mobile controls | -| **screen-manager.js** | p5 lifecycle management; easy to add new screens (pause, level intro) | -| **materials.js** | Texture setup reusable by other systems; centralized configuration | -| **hud.js** | DOM updates separate from game state; CSS animations cleanly decoupled | +### 2. Shared State Pattern via window.mazeGameState +**Decision:** Centralize all game state in single `window.mazeGameState` object -#### Testing Benefits -- **Unit testable:** Each module has single responsibility, minimal dependencies -- **Integration testable:** Callbacks allow mocking of complex systems -- **Less fragile:** Changing one concern doesn't require refactoring others - -#### Developer Experience -- **Faster onboarding:** New developers can understand features one module at a time -- **Feature additions:** Adding chests, NPCs, traps only requires extending relevant modules -- **Debugging:** Isolating bugs is easier when concerns are separated - -### Original Design Decisions (Unchanged) -**Decision:** Isolate game state in `game/state.js` rather than scatter variables globally - -**Rationale:** +**Rationale:** - Prevents tight coupling between UI, physics, and rendering systems +- All modules read/write from same source of truth (no data duplication) +- Simplifies debugging (inspect state in console at any time) - Enables hot-reloading during development -- Simplifies debugging (single place to inspect game state) -#### 2. Babylon.js Over Three.js +### 3. Callback-Based Event Routing +**Decision:** Lower modules don't know about higher modules; communication via callbacks + +**Rationale:** +- Reduces coupling between layers +- Easy to mock/test: pass different callbacks to change behavior +- Flexible event handling: same module used for different purposes +- Example: `registerGameLoop(scene, state, callbacks)` doesn't know about UI — it just calls callbacks + +### 4. Babylon.js Over Three.js **Decision:** Used Babylon.js for 3D graphics **Rationale:** @@ -459,7 +486,7 @@ babylon_panel.js (Orchestrator) - Efficient mesh instancing for maze cells - Excellent documentation for procedural generation -#### 3. p5.js for Particle Effects +### 5. p5.js for Particle Effects **Decision:** Delegated particle rendering to p5.js instead of Babylon.js **Rationale:** @@ -468,7 +495,7 @@ babylon_panel.js (Orchestrator) - Easy to swap/experiment with particle physics without affecting core game - Full-screen 2D canvas doesn't compete with 3D rendering pipeline -#### 4. Time-Attack Mode (vs. Exploration, Level Editing) +### 6. Time-Attack Mode (vs. Exploration, Level Editing) **Decision:** 60-second countdown instead of unlimited time **Rationale:** @@ -476,20 +503,34 @@ babylon_panel.js (Orchestrator) - Enables meaningful progression (faster times unlock harder mazes) - Reduces scope (no need for complex AI, item management, etc.) +### 7. Procedural Maze Generation with Seeded RNG +**Decision:** Use seeded random number generator for deterministic maze generation + +**Rationale:** +- Same seed produces identical maze (perfect for replays/debugging) +- Varied layouts via different seeds (infinite replayability) +- Better than storing pre-made levels (scalable to any difficulty) +- Recursive backtracking ensures every maze is solvable + --- ## Help from AI & Resources ### AI Assistance -- **GitHub Copilot:** Used for code structure review and refactoring suggestions - - Suggested separating static maze data from dynamic game state (instead of coupling both in a single 2D array) - - Helped organize modules into logical file structure - - Reviewed p5.js particle physics for correctness +- **GitHub Copilot:** Used throughout development for code quality, structure, and refactoring + - **Early Development:** Suggested separating static maze data from dynamic game state (prevents coupling) + - **Module Organization:** Proposed logical file structure to improve maintainability + - **Code Review:** Reviewed p5.js particle physics, Babylon.js camera control, Web Audio API integration + - **Refactoring (Phase 3):** Assisted with modularizing 570-line monolithic file into 14 focused modules + - Extracted scene initialization, camera management, game loop, level generation, collision detection + - Created input handler, material factories, HUD updates, screen manager modules + - Verified no breaking changes, ensured build compatibility, tested audio bundling ### Resources & Documentation -- **Babylon.js Playground:** Reference for collision detection and camera control -- **p5.js Documentation:** Async setup pattern for p5.js 2.0+ (no `preload()`) -- **MDN Web Audio API:** Context priming for cross-browser audio compatibility +- **Babylon.js Playground:** Reference for collision detection, camera control, and mesh creation +- **p5.js Documentation:** Async setup pattern for p5.js 2.0+ (no `preload()` function) +- **MDN Web Audio API:** Context priming and polyphonic sound playback patterns +- **Vite Documentation:** ES6 module bundling, asset optimization, and build configuration ---