# DungeonCraft An original dungeon building & defence game that runs in the browser (Rust + [macroquad](https://github.com/not-fl3/macroquad), compiled to WebAssembly). Dig out your underground realm, claim the portal to attract creatures, keep them fed, rested and paid, fend off endless hero waves that adapt to your army – and finally destroy the Hero Gate. - 3D isometric view with a freely rotatable and tiltable camera - Procedural pixel art, irregular cave walls, flickering torch light - CP437 text-mode style interface with the DINOBYTE bitmap font - Map sizes from 64×64 to 256×256 tiles (one portal per 64×64 tiles, guarded treasure caves on bigger maps) - English and German (language files in `lang/`, easy to extend) See [DESIGN.md](DESIGN.md) for all design decisions and [CREDITS.md](CREDITS.md) for third-party assets. ## Running The easiest way is Docker; the image builds the WASM file and serves it with nginx. A second container runs the savegame service (see below): ```sh docker compose up -d --build # open http://localhost:18093 ``` ### Building without Docker Requirements: Rust with the `wasm32-unknown-unknown` target. ```sh rustup target add wasm32-unknown-unknown cargo build --release --target wasm32-unknown-unknown cp target/wasm32-unknown-unknown/release/dungeoncraft.wasm www/ cp ~/.cargo/registry/src/*/macroquad-*/js/mq_js_bundle.js www/ python3 -m http.server 8080 -d www # open http://localhost:8080 ``` ### Testing the game logic The tests run natively: a headless simulation of whole games, a savegame round trip, rendering of all music modules and the audio engine: ```sh cargo test --release ``` ## Music The soundtrack consists of FastTracker 2 modules in `assets/music/`, generated by `tools/compose_music.py` (`python3 tools/compose_music.py`). They can be opened and edited in MilkyTracker. The game plays them with its own XM/MOD replayer (`src/xm.rs`); sound effects are synthesised chip sounds (`src/sfx.rs`). Both are mixed in `src/audio.rs` and streamed to the browser by `www/dc_plugin.js`. ## Controls | Input | Action | |---|---| | Esc | Pause menu (save, load, settings) | | Left click / drag | Use the active tool (dig and rooms: drag a rectangle) | | Right click | Drop a unit from your hand (prisoners anywhere on your ground; onto a free torture place = locked up) | | Shift + left click (hand) | Backhand own units (15 s faster, costs a little HP and mood) / punch heroes on your ground (15 damage, 1 s stun) | | Right drag, WASD, arrows, screen edge | Move the camera | | Middle drag, Q/E, R/F | Rotate / tilt the camera | | Mouse wheel | Zoom | | 1–9, 0, X, V | Select a tool or spell | | P / Space | Pause | | M, +/- | Sound on/off, volume | | G | Tile outlines on/off | | H | Help | All keys except Esc and the arrow keys can be changed in Settings → Keys. ## Savegames When the game is served together with the savegame service (`server/`, started by `docker compose`), *Save Game* and *Load Game* open a list of savegames stored on the server: save under a new name (type it into the name field), overwrite, load or delete (overwrite and delete ask for a second click). The list is shared by everybody who can reach the game - protect a public installation, for example with a password on the reverse proxy. If the service cannot be reached (for example when `www/` is opened as local files or served by a plain web server), the game falls back to savegame files: *Save Game* downloads a `.dcsave` file (JSON), *Load Game* opens the file picker; dropping the file onto the game window works too. Savegame files are also available from the server list (*Download as file* / *Load from file*). The game also autosaves into the browser every two minutes and when the tab is closed; *Continue* on the title screen resumes from there. ### Savegame service A small Rust service (axum) in `server/` stores savegames as files in the Docker volume `savegames`. nginx forwards `/api/` to it; the service itself is not published. | Request | Meaning | |---|---| | `GET /api/health` | `ok` when the service runs | | `GET /api/saves` | list (id, name, date, map size, wave, play time), newest first | | `POST /api/saves?name=...` | store a new savegame (body: savegame JSON) | | `PUT /api/saves/{id}?name=...` | overwrite a savegame | | `GET /api/saves/{id}` | savegame JSON | | `DELETE /api/saves/{id}` | delete a savegame | Limits: 1 MB per savegame, 500 MB in total, 30 write requests per minute. Settings: `SAVE_DIR` (default `/data`), `LISTEN` (default `0.0.0.0:8080`). ## Adding a language 1. Copy `lang/en.lang` to `lang/.lang` and translate the texts (placeholders `{0}`, `{1}` stay as they are). 2. Add the file to `SOURCES` in `src/i18n.rs`. 3. Note: the DINOBYTE font only covers the CP437 character set. ## License DungeonCraft is released under the [MIT License](LICENSE) – this covers the code, the generated music in `assets/music/` and the procedural graphics. The DINOBYTE font (`assets/dbyte_1x.png`) by mby is licensed separately under CC BY 4.0, see [CREDITS.md](CREDITS.md).