doom-osm-godmode
v1.2.2
Published
DOOM OSM GODMODE — Turn any real-world place into a playable GZDoom WAD. Geocode → OSM → UDMF → PWAD. Zero dependencies.
Downloads
107
Maintainers
Readme
Give it a place name, a GeoJSON file, or a config — it geocodes through Nominatim, pulls polygon features from OpenStreetMap via Overpass, projects them into a connected room-and-corridor layout, and writes a PWAD in UDMF format ready for GZDoom.
Zero npm dependencies. Node 20+ only.
Quick Start
# run the bundled demo (offline, no network)
npm run demo
# build a real place
node src/cli.js build-place "Colosseum, Rome, Italy" --radius 450
node src/cli.js build-place "Eiffel Tower, Paris" --radius 450
# hand-crafted architecturally accurate building
node scripts/build-galway-cathedral.js
# search without building
node src/cli.js search "Eiffel Tower, Paris"
# build from a config file
node src/cli.js build-config examples/colosseum.jsonInstallation
git clone https://github.com/eoinjordan/doom-osm-godmode.git
cd doom-osm-godmode
npm test # verify everything works
npm run demo # generate a WAD from the bundled demo siteOr install globally from npm:
npx doom-osm-godmode build-place "Colosseum, Rome" --radius 450No npm install needed — there are no dependencies.
Start the map on GZDOOM or Ultimate on Windows:
"C:\Games\GZDoom\gzdoom.exe" -iwad "C:\Program Files (x86)\Steam\steamapps\common\Ultimate Doom\base\doom2\DOOM2.WAD" -file "C:\Users\Eoin\git\doom-osm-wad\output\galway-cathedral-v3\MAP01.wad" +map MAP01Screenshots
CLI Reference
doom-osm-godmode
Commands:
search <query> Search Nominatim for a place
build-place <query> [options] Geocode + OSM fetch + WAD export
build-config <config.json> Build from a JSON config file
demo [--out output/demo-site] Build the bundled demo WAD
Options for build-place:
--radius <meters> Override bounding box with a fixed radius (default: use Nominatim bbox)
--map <MAPXX> Map lump name (default: MAP01)
--max-rooms <count> Cap on rooms generated (default: 12)
--title <name> Override the map title
--out <dir> Output directory (default: output/<slugified-title>)Programmatic API
const { buildFromPlace, buildFromConfig, buildDemo } = require('doom-osm-godmode');
// from a place name (hits Nominatim + Overpass)
const result = await buildFromPlace('Sydney Opera House', {
radiusMeters: 500,
mapName: 'MAP01',
maxRooms: 10,
outputDir: './output/sydney'
});
// from a config file (place query or offline GeoJSON)
const result = await buildFromConfig('examples/colosseum.json');
// bundled demo (no network)
const result = await buildDemo('./output/demo');All functions return { outputDir, mapPath, featureCount, exportedFeatures }.
Output
Each build writes a folder containing:
| File | Description |
|------|-------------|
| site.json | Normalized feature set with projected metrics |
| layout.json | Room/corridor grid layout used for WAD generation |
| TEXTMAP.udmf | Human-readable UDMF map geometry |
| MAPINFO.txt | GZDoom map metadata (sky, music, title) |
| <MAPXX>.wad | Packaged PWAD — load this in GZDoom |
Config Format
Place query (network):
{
"title": "Colosseum District",
"mapName": "MAP01",
"placeQuery": "Colosseum, Rome, Italy",
"radiusMeters": 450,
"maxRooms": 10,
"outputDir": "../output/colosseum"
}Offline GeoJSON:
{
"title": "Demo Waterfront",
"mapName": "MAP01",
"inputGeoJson": "demo-site.geojson",
"maxRooms": 8,
"outputDir": "../output/demo-site"
}Relative paths in configs resolve from the config file's directory.
Architecture
place query / GeoJSON
│
▼
geocode.js ──► Nominatim API
│
▼
osm.js ────► Overpass API (polygon features)
│
▼
feature-set.js project to local coords, filter, sort
│
▼
layout.js grid-based room placement + corridors + theming
│
▼
udmf.js UDMF vertices, linedefs, sidedefs, sectors, things
│
▼
wad.js binary PWAD packaging
│
▼
MAP01.wad ready for GZDoomKey Source Files
| File | Purpose |
|------|---------|
| src/cli.js | CLI entry point and command router |
| src/workflow.js | Build pipeline orchestrator |
| src/geocode.js | Nominatim geocoding |
| src/osm.js | Overpass polygon fetching |
| src/feature-set.js | Feature normalization and projection |
| src/geo.js | Mercator projection, bbox, polygon math |
| src/layout.js | Grid room placement, corridors, theming |
| src/udmf.js | UDMF text map generation |
| src/wad.js | PWAD binary packing |
| src/lib/cli.js | Argument parser |
| src/lib/fs.js | File I/O utilities |
Room Theming
Features are themed by kind:
| Kind | Floor | Ceiling | Light | Notes | |------|-------|---------|-------|-------| | building | stone | flat | 152 | Default indoor | | water | nukage | flat | 144 | Floor at -24 | | park | grass | sky | 176 | Outdoor feel | | road | light stone | flat | 160 | Corridors |
Testing
npm test # smoke tests via node:test
npm run check # syntax-check all source files
npm run validate # structural WAD/UDMF validation (builds + verifies integrity)Agent Playtest via doom-mcp
Generated WADs can be playtested by an AI agent using doom-mcp — a Rust MCP server that embeds the real DOOM engine.
Quick setup
The repo includes a .mcp.json that configures doom-mcp to load your demo WAD:
# 1. Build a WAD first
npm run demo
# 2. Playtest it (automated agent navigation sequence)
npm run playtest -- output/demo-site/MAP01.wad
# 3. Or playtest a real-place WAD
node src/cli.js build-place "Colosseum" --radius 450
npm run playtest -- output/colosseum/MAP01.wad --skill 1 --ticks 300What the playtest validates
The automated playtest spawns doom-mcp, loads the WAD, and runs through a navigation sequence:
- Game starts — doom engine accepts the WAD
- Player spawns — HP > 0 at spawn point
- Player can move — position changes across multiple actions
- Player survives — doesn't die during baby-difficulty walkthrough
Using doom-mcp interactively
For interactive agent playtesting (e.g., in Claude Code or Cursor):
{
"mcpServers": {
"doom": {
"type": "stdio",
"command": "npx",
"args": ["-y", "doom-mcp"],
"env": {
"DOOM_WAD_PATH": "/path/to/your/MAP01.wad"
}
}
}
}Then tell the agent: "Play this DOOM level and tell me if it's fun"
CI integration
The playtest.yml workflow runs structural validation on every tag push and can be triggered manually for any place:
Actions → Playtest → Run workflow → Enter place name → GoPlaying the WAD
# GZDoom (adjust path to your install)
gzdoom -file output/colosseum/MAP01.wadThe WAD is a PWAD — it layers on top of any IWAD (DOOM2.WAD, FREEDOOM2.WAD, etc.).
Hand-Crafted Buildings
The OSM pipeline creates grid-based rooms. For architecturally accurate buildings (cathedrals, temples, etc.), use a hand-crafted script:
# Build the Galway Cathedral demo (cross-shaped plan, green copper dome, grand nave)
node scripts/build-galway-cathedral.js
# Launch in GZDoom
gzdoom -iwad DOOM2.WAD -file output/galway-cathedral-v3/MAP01.wad +map MAP01The cathedral builder demonstrates the UDMFBuilder class with polygon-based sector creation, grand interior aisle, raised chancel, self-validation, and exterior forecourt. Use it as a template for new hand-crafted maps.
Example maps included:
| Map | Type | Script / Command |
|-----|------|------------------|
| Galway Cathedral | Hand-crafted | node scripts/build-galway-cathedral.js |
| Colosseum, Rome | OSM / live | node src/cli.js build-place "Colosseum, Rome, Italy" --radius 450 |
| Eiffel Tower, Paris | OSM / live | node src/cli.js build-place "Eiffel Tower, Paris" --radius 450 |
| Demo Waterfront | Offline GeoJSON | npm run demo |
Validate any WAD
# Structural UDMF diagnostic (checks for duplicate linedefs, etc.)
node scripts/diagnose-udmf.js output/galway-cathedral-v3/TEXTMAP.udmf
# Agent playtest via doom-mcp
npm run playtest -- output/galway-cathedral-v3/MAP01.wadUDMF Geometry Rules
Common issues found when generating DOOM maps from real-world data:
| Issue | Symptom | Fix |
|-------|---------|-----|
| Duplicate linedefs (same vertex pair) | Flickering/transparent walls | Use a single polygon for complex shapes; deduplicate in addInnerSector |
| Collinear overlapping linedefs | See-through walls, BSP errors | Never let two linedefs share the same 2D line segment |
| Inner sector outside parent bounds | Rendering void, hall-of-mirrors | Ensure all inner sector vertices are strictly inside the outer polygon |
| Separate rooms at same crossing | Duplicate shared edges | Use ONE polygon for the union shape (e.g., a cross), not overlapping rectangles |
| Underground areas sharing x,y with upper | DOOM 2.5D sector conflict | Place underground areas at different x,y coords; connect via teleporter |
| Invisible walls on two-sided linedefs | Side walls transparent where floor heights match | Set wrapmidtex = true + blocking = true + texturemiddle on the two-sided linedef |
| Floating mid-textures on promoted linedefs | Choppy half-height walls | Clear texturemiddle when promoting one-sided to two-sided; use upper/lower instead |
| Zero-height pillar sectors (floor = ceiling) | Player stuck, node builder errors | Use Thing-based pillars (type 30) instead of sector-based pillars |
Repeatable pattern for future OSM meshes
- Run
node src/cli.js build-place ...for the grid-room WAD - Run
node scripts/diagnose-udmf.js output/<name>/TEXTMAP.udmfto check geometry - Run
npm run playtest -- output/<name>/MAP01.wadfor engine validation - If the grid rooms are too abstract, create a hand-crafted script (see
scripts/build-galway-cathedral.js) - Always run self-validation before writing the WAD
Notes
- Network commands use public OSM infrastructure (Nominatim, Overpass). Keep requests reasonable.
- Only polygon features become rooms. Roads and linear features are not yet carved into sectors.
- The layout is a blockout interpretation — connected rooms reflecting real feature hierarchy, not pixel-perfect street geometry.
Roadmap
- exact polygon-to-sector carving (replace grid rooms with real building outlines)
- road and canal line buffering into traversable sectors
- Doom encounter placement (monsters, ammo, weapons)
- texture themes per location style
- multi-map episode generation
- freeform agent-described layouts (no OSM needed)
- automatic duplicate linedef detection in the OSM pipeline
Contributing
PRs welcome. Run npm test and npm run check before submitting.
License
MIT — see LICENSE
