# Escape A Wolfenstein-3D-style raycaster game built with **Phaser 4**. The "3D" is an authentic raycasting engine: a 2D grid rendered as vertical wall strips. Graphics are intentionally stubbed (solid colors / placeholder shapes) so you can drop in your own spritesheets later. ## Run There is **no build step and no npm dependency**. The game is plain ES-module JavaScript; Phaser ships vendored in `vendor/`. Serve the folder with any static HTTP server and open it in a modern browser (Chrome/Edge/Firefox/Safari — needs [import map](https://caniuse.com/import-maps) support, ~2023+). ```bash ./start_web.sh # python3 -m http.server 3000 # or any equivalent: # npx serve . # php -S localhost:3000 # (drop the folder behind nginx / Apache / GitHub Pages / S3 / …) ``` Then open: - Game: http://localhost:3000/ - Editor: http://localhost:3000/editor.html > Opening the files via `file://` will **not** work — ES modules require an HTTP > origin. Any static server (above) is enough; no server-side code runs. ## Game controls | Action | Keys | | ----------------- | ------------------------------------- | | Move / strafe | `W` `A` `S` `D` (arrows also move) | | Turn | `←` `→`, or **mouse** (click to lock) | | Shoot | **Left-click** (mouse locked) / `Space` | The HUD shows health, ammo, and score. Walk over pickups to collect them; shoot guards to score. Click the view once to capture the mouse for look controls. ## Level editor (`/editor.html`) - **Palette** (top): pick a wall tile, the player **Start** marker, or an entity. - **Left-click / drag**: paint the selected brush. - **Right-click / drag**: erase (clears the tile and any entity in the cell). - **Arrow keys**: scroll the view. **Middle-drag**: pan. **Mouse wheel**: zoom. - **New**: start a fresh blank grid (default 72×72; see `NEW_LEVEL_WIDTH/HEIGHT` in `src/engine/constants.js`). **Save**: persist to browser storage. **Export/Import JSON**: files on disk. - **Play Test**: opens the game in a new tab running your current level. The grid is large enough to build big levels; only the on-screen cells are drawn, so bigger maps stay fast. Scroll with the arrow keys as your level grows past the visible area. Place an **Exit** entity in a level so the player can finish it; walking onto the exit advances to the next level in the campaign (see below). A level with no exit instead completes when every guard is defeated. ## Level file format Levels are plain JSON — exactly what the editor's **Export JSON** writes and what the game reads. To author levels, paint one in the editor, Export it, and drop the file in `src/levels/` (see the campaign section). The format is: ```jsonc { "version": 1, // schema version (stamped on export) "name": "Dungeon Entrance", // display name; also the export filename "width": 16, // grid columns "height": 16, // grid rows "tiles": [ /* … */ ], // row-major ints, length MUST equal width*height // 0 = floor; 1..5 = wall tiles (constants.js TILES) "playerStart": { "x": 2.5, "y": 3.5, "dir": 0 }, // cell units; dir in radians "entities": [ // may be empty { "type": "enemy", "x": 8.5, "y": 5.5 }, { "type": "ammo", "x": 4.5, "y": 4.5 }, { "type": "health", "x": 6.5, "y": 9.5 }, { "type": "treasure", "x": 12.5, "y": 2.5 }, { "type": "exit", "x": 14.5, "y": 8.5 } ] } ``` `entity.type` and tile ids come from the registries in `src/engine/constants.js`. Files are validated on load (`parseLevel`): `tiles.length` must equal `width*height` and `playerStart` must be present; unknown tile/entity ids fail safe rather than crashing. ## Campaign — using your files as game levels The game plays the level files listed, in order, in the manifest `src/levels/levels.json`: ```jsonc { "levels": ["level1.json", "level2.json"] } ``` To add a level: drop its `.json` in `src/levels/` and add its filename to the `levels` array. Reaching an **Exit** advances to the next file; after the last one the player sees a victory screen. Health, ammo, and score carry across levels. If the manifest can't be fetched, the game falls back to the bundled `level1.data.js`. ## Project layout ``` index.html Game page (import map → vendored Phaser) editor.html Editor page (import map → vendored Phaser) vendor/ phaser.esm.min.js (vendored so no npm install is needed) src/ engine/ Shared core: constants (TILE & entity registries), GameMap, Raycaster (DDA + ZBuffer), SpriteRenderer, Player, Entity, levelIO game/ Phaser bootstrap + Boot/Game/Hud scenes editor/ Phaser bootstrap + EditorScene levels/ levels.json (campaign manifest), level1.json / level2.json (levels), level1.data.js (bundled offline fallback, kept in sync with level1.json) ``` The game and editor are separate HTML entry points that share everything in `src/engine`, so a level you paint is exactly what plays. Native ES modules mean every relative import includes its `.js` extension, and the bare `phaser` specifier is resolved by the `