Something went wrong. Try again.
Steam Deck Doom modding setup automation for Doom Runner, UZDoom, Brutal Doom, and Project Brutality.
brutal-doom moddb python doom-runner linux-gaming project-brutality uzdoom doom steam-deck
Something went wrong. Try again.
10 kB · 244 lines
Python
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245"""Generate DoomDeck user-facing setup documentation."""from __future__ import annotations
import loggingfrom pathlib import Pathfrom typing import Any, Optional
from doomdeck.application.doomrunner import doomrunner_options_pathsfrom doomdeck.application.proton import proton_windows_pathfrom doomdeck.domain.deck import STEAM_DECK_HEIGHT, STEAM_DECK_TARGET_FPS, STEAM_DECK_WIDTHfrom doomdeck.domain.models import Dirsfrom doomdeck.domain.mods import PROJECT_BRUTALITY_MODfrom doomdeck.infrastructure.files import atomic_write_text
def render_content_group_guide(manifest: dict[str, Any]) -> str: content_groups = manifest.get("content_groups", {}) if not isinstance(content_groups, dict): return "" section_titles = { "presets": "Preset Groups", "map_packs": "Map Pack Groups", "mods": "Mod Groups", } output = "\n## Automatic Content Groups\n\nDoomDeck writes group metadata separately from launchable items. Group headings are labels only; the entries listed under them remain the selectable presets, WADs, map packs, and mods.\n" wrote_any = False for section_key, title in section_titles.items(): groups = content_groups.get(section_key, []) if not isinstance(groups, list) or not groups: continue wrote_any = True output += f"\n### {title}\n" for group in groups: if not isinstance(group, dict): continue output += f"\n#### {group.get('display_name', group.get('id', 'Group'))}\n" items = group.get("items", []) if not isinstance(items, list): continue for item in items: if not isinstance(item, dict): continue path = f" - `{item['path']}`" if item.get("path") else "" output += f"- `{item.get('display_name', item.get('id', 'item'))}`{path}\n" return output if wrote_any else ""
def write_docs( dirs: Dirs, manifest: dict[str, Any], brutal_path: Optional[Path], project_brutality_path: Optional[Path], dry_run: bool, logger: logging.Logger, *, appid: str, brutal_doom_source_url: str, project_brutality_repo: str,) -> None: # Generated Markdown, not a SQL expression. layout = ( # nosec B608 f"""# Doom Deck Setup Layout
Generated by `doomdeck`.
```text{dirs.root}/├── tools/doomrunner/ Windows Doom Runner executable for Proton├── source-ports/uzdoom/ Windows UZDoom executable for Proton├── iwads/ Legal IWAD copies from Steam app {appid}├── pwads/ User PWADs and map packs├── mods/brutal-doom/ Managed Brutal Doom .pk3 and update metadata├── mods/project-brutality/ Downloaded Project Brutality .pk3├── configs/doomrunner/ Generated manifest and Doom Runner setup guide├── configs/uzdoom/ Classic, modern, Brutal Doom, and Project Brutality config templates├── configs/steam-input/ Generated Steam Deck keyboard/mouse Steam Input profile├── launchers/ Generated preset helper scripts and launch metadata├── saves/ Save-game location for future use├── screenshots/ Screenshot location for future use├── downloads/ Downloaded upstream release assets├── backups/ Backups made before replacement/modification└── logs/ Script logs```""" ) steam_input = f"""# Steam Input Profiles for Doom Runner / UZDoom
DoomDeck installs the Windows builds of Doom Runner and UZDoom and forces the generated Doom Runner Steam shortcut to use the configured Steam compatibility tool. The default is `proton_10`.
DoomDeck also generates a Steam Deck Steam Input profile at:
`{dirs.steam_input_config / 'doomrunner' / 'controller_neptune.vdf'}`
When the Doom Runner shortcut is written, DoomDeck deploys the same profile into Steam's `Steam Controller Configs/<steam-user-id>/config/doom runner/controller_neptune.vdf` location and registers it in `configset_controller_neptune.vdf` when a Steam user is known.
The profile is named `DoomDeck Hybrid KB/M`. It emits keyboard and mouse input, not gamepad input, so the generated UZDoom profiles control exactly what the keys mean. Gyro is intentionally unbound/disabled.
The generated UZDoom configs default to fullscreen {STEAM_DECK_WIDTH}x{STEAM_DECK_HEIGHT}, vsync on, and a {STEAM_DECK_TARGET_FPS} FPS cap.
## Selected Steam Deck layout
- Left stick: WASD movement; L3 toggles UZDoom `cl_run` on/off.- Right trackpad: primary mouse aiming, sensitivity 50.- Right stick: conservative mouse aiming backup, sensitivity 35.- Right trigger: Mouse1 fire.- Left trigger: Mouse2 alt-fire / aim down sights.- Left/right bumpers: previous/next weapon by mouse wheel.- A: `E` use and menu select.- B: `Left Ctrl` crouch and menu back in UZDoom profiles that support crouch.- X: `R` reload.- Y and L4: `Space` jump, or use in the classic profile.- R4: `Left Ctrl` crouch without leaving aim.- L5: `F6` quick save.- R5: long-press `F9` quick load. No normal combat press emits quick load.- D-pad: arrow keys for menu navigation.- Menu/Start: `Escape` menu/pause.- View/Select: `Tab` automap.- Left trackpad radial: direct weapon slots `1`-`9`/`0`, plus Brutal Doom / Project Brutality utility keys `Q`, `F`, `G`, `U`, `Left Alt`, and `L`.
## Profile behavior
- `configs/uzdoom/classic/autoexec.cfg`: classic Doom feel, no freelook, no jump, no crouch, Always Run enabled.- `configs/uzdoom/modern/autoexec.cfg`: UZDoom keyboard/mouse FPS controls with freelook, jump, crouch, reload, alt-fire, quick save/load, and weapon slots.- `configs/uzdoom/brutal/autoexec.cfg`: modern controls while preserving `Q` for Brutal Doom's default kick binding.- `configs/uzdoom/project-brutality/autoexec.cfg`: modern controls while preserving Project Brutality KEYCONF defaults for `Q`, `F`, `G`, `U`, `Left Alt`, and `L`.
For mods with a verified flashlight key, use the left-trackpad radial `F` key. Stock UZDoom/GZDoom-family configs use `F` as zoom where no first-party flashlight binding was found.""" live_options_primary = doomrunner_options_paths(dirs)[0] doomrunner_guide = f"""# Doom Runner Setup Guide
DoomDeck installs the Windows builds of Doom Runner and UZDoom, copies Steam IWADs, creates one Doom Runner Steam shortcut that runs through Proton, writes Doom Runner's generated options file beside `DoomRunner.exe`, and writes a stable preset manifest:
`{live_options_primary}`
`{dirs.doomrunner_config / 'preset-manifest.json'}`
DoomDeck also writes a Doom Runner options copy into the generated Steam shortcut's Proton prefix when Steam shortcut creation is enabled. The portable options file beside `DoomRunner.exe` is the primary file for DoomDeck's user-writable Windows install; the Proton prefix copy covers Doom Runner's fallback `%AppData%` behavior. All generated presets are launched from inside Doom Runner.
Doom Runner should open through Steam/Proton with the generated UZDoom engine, IWADs, and presets already listed. Existing generated Doom Runner options are backed up under:
`{dirs.backups}`
## Initial Doom Runner check
1. Launch `Doom Runner` from Steam Gaming Mode.
2. Confirm this engine exists:
- Engine name: `UZDoom` - Executable path: `{proton_windows_path(dirs.uzdoom / 'uzdoom.exe')}` - Engine family: `UZDoom` if shown, otherwise `ZDoom` / `GZDoom-family` - Config directory: `{proton_windows_path(dirs.uzdoom_config)}` - Data directory: `{proton_windows_path(dirs.root)}`
3. Confirm IWADs are listed from:
`{dirs.iwads}`
4. Confirm the map directory is:
`{dirs.pwads}`
ModDB WAD archives installed with `doomdeck install-wads` are extracted here so they can be selected inside a preset.
5. Confirm these presets are listed:
""" for preset in manifest.get("presets", []): files = ", ".join(preset.get("files") or []) or "none" doomrunner_guide += f"- `{preset['name']}`\n - IWAD: `{preset['iwad']}`\n - Config: `{preset['config']}`\n - Autoexec: `{preset['autoexec']}`\n - Files/mods: `{files}`\n" doomrunner_guide += render_content_group_guide(manifest) doomrunner_guide += """## Preset launchers
DoomDeck creates helper scripts in `launchers/` for each preset. These scripts are not added to Steam. Open the single `Doom Runner` Steam shortcut and choose a generated preset there so Windows UZDoom runs through Proton with Steam Input.""" brutal = f"""# Brutal Doom
Brutal Doom is checked from the official ModDB project/download pages when you rerun:
```bashdoomdeck install```
The default update channel is `latest`, which can include beta/test builds such as v22 test releases. To prefer the newest non-beta candidate DoomDeck can identify, use:
```bashdoomdeck install --brutal-doom-channel stable```
Managed file:
`{brutal_path or 'not found yet'}`
Metadata:
`{dirs.brutal / 'brutal-doom.json'}`
To use a specific browser-downloaded file instead:
```bashdoomdeck install --brutal-doom-file /path/to/brutal-doom.pk3```
To skip Brutal Doom update checks:
```bashdoomdeck install --skip-brutal-doom```
Source project:
`{brutal_doom_source_url}`""" project_brutality = f"""# Project Brutality
Project Brutality is checked from GitHub and installed as a root-normalized `.pk3` for UZDoom/Doom Runner.
Managed file:
`{project_brutality_path or (dirs.project_brutality / PROJECT_BRUTALITY_MOD.alias)}`
To update it, rerun:
```bashdoomdeck install```
To use a manually downloaded file instead:
```bashdoomdeck install --project-brutality-file /path/to/Project_Brutality.pk3```
Source repository:
`https://github.com/{project_brutality_repo}`""" atomic_write_text(dirs.docs / "DIRECTORY_LAYOUT.md", layout, dry_run, logger) atomic_write_text(dirs.docs / "STEAM_INPUT_PROFILES.md", steam_input, dry_run, logger) atomic_write_text(dirs.docs / "DOOM_RUNNER_SETUP.md", doomrunner_guide, dry_run, logger) atomic_write_text(dirs.brutal / "README.md", brutal, dry_run, logger) atomic_write_text(dirs.project_brutality / "README.md", project_brutality, dry_run, logger)