diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..ab9ccdc --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,255 @@ +# Wolfenstein 3-D — Architecture + +This is the top-level entry point for understanding the source in this +repository. It was written by reading the code in `WOLFSRC/` directly; every +substantive claim can be cross-checked against `WOLFSRC/:`. Line +numbers refer to the tree at the tip of `main` (`778abe0`). + +--- + +## 1. What this code is + +This is the **complete original PC source code for Wolfenstein 3-D**, released +by id Software in 2012. A single source tree builds several products through +conditional compilation, selected by per-game version headers: + +| File | Product / flag | +|----------------------|----------------------------------------------------| +| `WOLF1VER.H` | Wolfenstein 3-D v1.0 | +| `WOLFVER.H` | Shareware / Apogee retail flags | +| `SODVER.H` | *Spear of Destiny* (full) | +| `SDMVER.H` | *Spear of Destiny* mission-disk build | +| `WOLFJVER.H` / `WLFJ1VER.H` | Japanese full / shareware | +| `SPANVER.H` | Spanish-language build | +| `WOLFGTV.H` | German-television variant | +| `FOREIGN.H` | Master switch enabling a foreign-language build | +| `F_SPEAR.H` | Spear-of-Destiny feature gates (`SPEAR`) | + +The central header **`WL_DEF.H`** (1276 lines) carries the type and constant +definitions that all of `WL_*.C` depend on — the `fixed` typedef, tile and +angle constants, and the actor/state structs (see §6). + +## 2. What's in the repository root + +``` +. +├── README.rst # README from id Software's 2012 release +├── DEICE.EXE # DeIce self-extractor that produced WOLFSRC +├── INSTALL.BAT # DOS bootstrap that invokes DEICE +├── WOLFSRC.1 / WOLFSRC.DAT # split-file continuation data for the extractor +├── WOLFSRC/ # ★ all source code lives here ★ +└── docs/ # ★ this documentation ★ +``` + +The `DEICE.EXE` self-extractor split the release across floppy disks; running +it reproduces `WOLFSRC/`. The release note `WOLFSRC/GOODSTUF.TXT` records that +the project was built with **Borland C++ 3.0** and is shipped with no support. + +## 3. The WOLFSRC layout + +`WOLFSRC/` holds the engine sources plus build assets and pre-built binaries. +The files fall into these classes: + +| Class | Examples | +|--------------|------------------------------------------------------------------| +| C source | `WL_*.C`, `ID_*.C`, plus `CONTIGSC.C`, `MUNGE.C`, `OLDSCALE.C`, `WOLFHACK.C` | +| C headers | `WL_*.H`, `ID_*.H`, per-game version flags, asset tables (`AUDIO*.H`, `GFX*.H`, `MAPS*.H`) | +| x86 assembly | `C0.ASM`, `H_LDIV.ASM`, `WL_*_A.ASM`, `ID_*_A.ASM`, `JABHACK.ASM`, `WHACK_A.ASM` | +| Equate tables| `ID_*.EQU`, `GFX*_*.EQU` (assembler `EQU` constants) | +| Build system | `WOLF3D.PRJ`, `WOLF.IDE`, `WOLF.DSW`, `WOLF.OBR`, `GO.BAT`, `RULES.ASI` | +| Binaries | `WOLF3D.EXE`, `WOLF.EXE`, `SV.EXE`, `WOLF3D.MAP`, `OBJ/` | +| Doc / misc | `GOODSTUF.TXT`, `README/`, `PICLIST.H`, `VERSION.H` | + +Approximate size, via `wc -l` (largest files): + +| File | Lines | File | Lines | +|-------------|-------|-------------|-------| +| `WL_MENU.C` | 3986 | `WL_DRAW.C` | 1403 | +| `WL_ACT2.C` | 3872 | `WL_DEF.H` | 1276 | +| `ID_SD.C` | 2367 | `ID_PM.C` | 1199 | +| `ID_CA.C` | 1767 | `ID_VL.C` | 1084 | +| `WL_INTER.C`| 1718 | `ID_IN.C` | 990 | +| `WL_MAIN.C` | 1616 | `ID_MM.C` | 953 | +| `WL_GAME.C` | 1484 | `WL_ACT1.C` | 900 | +| `WL_STATE.C`| 1480 | `WL_TEXT.C` | 859 | +| `WL_PLAY.C` | 1472 | `ID_US_1.C` | 755 | +| `WL_AGENT.C`| 1421 | `WL_SCALE.C`| 733 | + +### Naming convention + +The tree follows the **id engine convention** used across id's early games: + +* **`ID_*`** — reusable engine modules shared with id's other titles: + `CA` (cache), `VL`/`VH` (video low/high), `IN` (input), `PM` (page + manager), `MM` (memory manager), `SD` (sound), `US` (user/UI). Primarily + the work of **John Carmack** (renderer, cache) and **Jason Blochowiak** + (sound, input, paging, user). +* **`WL_*`** — Wolfenstein-specific gameplay, rendering, and UI, by Carmack + (renderer) and **Dave Taylor** / **John Romero** (gameplay, actors, menus). + +This portable/game split is exactly what let the engine be lifted into later +id titles. + +## 4. Build system + +The code targets **Borland C++ 3.0** for 16-bit DOS. `WOLF3D.PRJ` (with the +older `WOLF.IDE` / `WOLF.DSW`) drives the Borland toolchain; `GO.BAT` and the +Borland rules file `RULES.ASI` set the assembler/compiler rules. The idiomatic +build is: + +```bat +CD WOLFSRC +GO.BAT +``` + +> **This documentation adds no build.** It only describes the existing one. +> Do not expect to compile this without a vintage Borland C 3.0 toolchain and +> a copy of the retail Wolfenstein/Spear data files (the executable needs game +> data it does not ship with — see `README.rst`). + +The `IsA386` global (`WL_MAIN.C:47`, detected at `WL_MAIN.C:251-261`) selects +386-specific fast-path integer math at runtime, falling back to a software +path on a 286. + +## 5. Runtime architecture + +### 5.1 The main loop + +The boot path: + +1. `main()` — entry, `WL_MAIN.C` (calls follow below). +2. `InitGame()` (`WL_MAIN.C:1145`) — configures memory (MM/PM), video, + audio, input; builds trig tables via `BuildTables()` (`WL_MAIN.C:586`, + called at `WL_MAIN.C:1233`); loads graphics; plays the signon screen. +3. `DemoLoop()` (`WL_MAIN.C:1411`) — the front-end carousel: title, credits, + and attract-mode demos, until the player starts a game. Called at + `WL_MAIN.C:1612`. +4. `GameLoop()` (`WL_GAME.C:1238`) — per-game driver: sets up the level + (`SetupGameLevel`, `WL_GAME.C:625`), draws the play screen + (`DrawPlayScreen`, `WL_GAME.C:868`), then repeatedly calls `PlayLoop()`. +5. `PlayLoop()` (in `WL_PLAY.C`) — the per-tick gameplay driver: polls + controls, moves the player, ticks every actor's state machine, then calls + `ThreeDRefresh()`. +6. `ThreeDRefresh()` (`WL_DRAW.C:72`, defined near the end of `WL_DRAW.C`) — + the raycaster: `TransformActor` (`WL_DRAW.C:210`), the wall cast with + `HitVertWall`/`HitHorizWall` (`WL_DRAW.C:477` / `:550`), then + `DrawScaleds()` (`WL_DRAW.C:1054`) for sprites and the player weapon. + +Sound plays asynchronously: `ID_SD.C` installs an IRQ-driven callback that +refills the AdLib / Sound-Blaster / PC-speaker output each timer tick. + +### 5.2 Data flow + +``` + ┌───────────────────────────────────────────────┐ + │ main → InitGame → DemoLoop (WL_MAIN.C) │ + └───────────────────────┬───────────────────────┘ + ▼ + ┌───────────────────────────────────────────────┐ + │ GameLoop (per game) (WL_GAME.C) │ + └───────┬───────────────┬───────────────┬───────┘ + ▼ ▼ ▼ + ┌──────────────┐ ┌──────────────┐ ┌────────────────────┐ + │ ID_IN.C │ │ WL_PLAY.C │ │ ThreeDRefresh │ + │ input polling│ │ PlayLoop │ │ (WL_DRAW.C + asm) │ + └──────────────┘ └──────┬───────┘ └─────────┬──────────┘ + ▼ ▼ + ┌────────────────────┐ ┌────────────────────┐ + │ WL_STATE/ACT1/ACT2 │ │ DrawScaleds │ + │ actor state ticks │ │ (WL_SCALE.C + asm) │ + └─────────┬──────────┘ └─────────┬──────────┘ + ▼ ▼ + ┌───────────────────────────────────────────────┐ + │ ID_VL.C / ID_VH.C Mode-X / VESA page flip │ + └───────────────────────────────────────────────┘ + + ┌───────────────────────────────────────────────────────────┐ + │ ID_CA.C asset cache — Carmack RLE + Huffman decompression │ + │ ↑ ID_PM.C (EMS paging) ↑ ID_MM.C (near/far heap) │ + └───────────────────────────────────────────────────────────┘ + + ┌───────────────────────────────────────────────────────────┐ + │ ID_SD.C AdLib + Sound Blaster + PC speaker (ID_SD_A.ASM) │ + └───────────────────────────────────────────────────────────┘ +``` + +The whole thing is a **single-threaded polling loop**. The only concurrency +is BIOS/hardware interrupt service routines the engine installs — the timer +IRQ, its own keyboard ISR (`ID_IN.C`), and the sound-card DMA/IRQ path +(`ID_SD.C`). There are no OS threads. + +### 5.3 Subsystem map + +| Subsystem | Primary files | Guide | +|-------------------------------|--------------------------------------------------------|-------| +| Engine core & types | `WL_MAIN.C`, `WL_DEF.H` | [game-logic](systems/game-logic.md) | +| 3-D raycaster | `WL_DRAW.C`, `WL_DR_A.ASM` | [rendering](systems/rendering.md) | +| Scaled-shape renderer | `WL_SCALE.C`, `WL_AGENT.C` | [rendering](systems/rendering.md) | +| VGA Mode-X driver | `ID_VL.C`, `ID_VL_A.ASM`, `ID_VL.H`, `ID_VL.EQU` | [rendering](systems/rendering.md) | +| VESA/hi-color driver | `ID_VH.C`, `ID_VH_A.ASM`, `ID_VH.H` | [rendering](systems/rendering.md) | +| Actor AI / state machines | `WL_STATE.C`, `WL_ACT1.C`, `WL_ACT2.C` | [actordata](systems/actordata.md) | +| Game loop, save/load, intermissions | `WL_GAME.C`, `WL_PLAY.C`, `WL_INTER.C` | [game-logic](systems/game-logic.md) | +| UI / menus / text | `WL_MENU.C`, `WL_TEXT.C`, `ID_US_1.C` | [ui-menu](systems/ui-menu.md) | +| Input polling | `ID_IN.C`, `ID_IN.H` | [input](systems/input.md) | +| Audio | `ID_SD.C`, `ID_SD_A.ASM`, `ID_SD.H`, `ID_SD.EQU` | [audio](systems/audio.md) | +| Asset cache & decompression | `ID_CA.C`, `ID_CA.H` | [file-cache](systems/file-cache.md) | +| Memory & page managers | `ID_MM.C`, `ID_PM.C` | [game-logic](systems/game-logic.md) | +| Debug overlay | `WL_DEBUG.C` | [game-logic](systems/game-logic.md) | +| Build / tools | `WOLF3D.PRJ`, `GO.BAT`, `RULES.ASI`, `JABHACK.ASM`, `*.EQU` | [tools-build](systems/tools-build.md) | + +## 6. The fixed-point model + +Wolfenstein 3-D predates cheap consumer floating point. Every position, +velocity, and rotation in the renderer is a **16.16 signed fixed-point +integer**, declared `typedef long fixed;` (`WL_DEF.H:480`). + +| Symbol | Value | Meaning | +|---------------|----------------|--------------------------------------------------| +| `fixed` | `long` | 32-bit, 16.16 signed fixed-point | +| `PI` | `3.141592657` | Used only when generating trig tables | +| `GLOBAL1` | `(1l<<16)` | "1.0" in fixed-point (`0x10000`) | +| `TILEGLOBAL` | `GLOBAL1` | one tile == 1.0 | +| `TILESHIFT` | `16` | right-shift converting fixed → tile integer | +| `ANGLES` | `360` | angle units in a full turn (must divide by 4) | +| `FINEANGLES` | `3600` | sub-angle resolution for sine/cosine LUTs | + +Trig tables (`sintable`, `costable`, `finetangent`) are precomputed once in +`BuildTables()` (`WL_MAIN.C:586`); the hot path uses table lookup only, never +`sin()`/`cos()`. + +## 7. The coordinate system + +* Maps are **64 × 64 tiles**; a tile is 64 × 64 units, so `TILEGLOBAL` fixed + units span one tile. +* `MAPSPOT(x,y,plane)` (`WL_DEF.H:35`) indexes a map plane: + `*(mapsegs[plane] + farmapylookup[y] + x)`. +* `viewx,viewy,viewangle` — camera position (fixed) and facing (`ANGLES`). +* Walls are **axis-aligned** (never 45°). The cast is a DDA over horizontal + and vertical tile intercepts: `HitVertWall` and `HitHorizWall` + (`WL_DRAW.C:477` / `:550`). Because every wall is exactly one tile thick, + the raycaster is *tile-aligned* — which is why the game has no angled walls. + +## 8. Fixed limits (gotchas) + +The engine sizes its tables at compile time: + +* `MAXACTORS = 150` (`WL_DEF.H:49`) — live enemies/objects per map. +* `MAXSTATS = 400` (`WL_DEF.H:50`) — static objects (lamps, treasure). +* `MAXDOORS = 64` (`WL_DEF.H:51`) — sliding doors per map. + +Bumping any of these ripples into the cache-chunk budget in `ID_CA` / +`ID_PM`. Doors are hard-coded directional variants in `WL_ACT1.C`; adding a +new door type needs both new state-machine entries and new graphics. There is +no multithreading — the interrupt handlers are the only concurrency. + +## 9. Where to start reading + +1. `WL_MAIN.C` — `main` → `InitGame()` (1145) → `BuildTables()` (586). +2. `WL_GAME.C` — `GameLoop()` (1238) and how it dispatches `PlayLoop()`. +3. `WL_DRAW.C` — `ThreeDRefresh` → `TransformActor` (210) → + `HitVertWall`/`HitHorizWall` (477/550) → `DrawScaleds` (1054). +4. `ID_CA.C` — `CAL_CarmackExpand` (609), `CAL_HuffExpand` (418), + `CA_LoadFile` (347). +5. `ID_US_1.C` + `WL_TEXT.C` — UI/text rendering and IRQ-driven input. + +See [`README.md`](README.md) for the full documentation index. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..278fd3e --- /dev/null +++ b/docs/README.md @@ -0,0 +1,64 @@ +# Wolfenstein 3-D — Documentation + +An architecture reference for the original 1992 Wolfenstein 3-D source code in +`WOLFSRC/`. Written by reading the source directly and cross-checking each +claim against `WOLFSRC/:` at the tip of `main` (`778abe0`). + +## Start here + +* **[`ARCHITECTURE.md`](ARCHITECTURE.md)** — top-level overview: what the code + is, directory layout, main loop, data-flow diagram, fixed-point model, + coordinate system, build system, and where to start reading. + +## Per-subsystem guides — `systems/` + +Each guide explains what a subsystem does, which files it owns, and how it +talks to the rest of the engine. + +* [`systems/rendering.md`](systems/rendering.md) — raycaster, scaled-shape + renderer, VGA Mode-X and VESA drivers, view setup. +* [`systems/audio.md`](systems/audio.md) — AdLib (OPL2), Sound Blaster DAC, + PC speaker, and the IRQ-driven mixing callback. +* [`systems/input.md`](systems/input.md) — keyboard ISR, mouse, joystick, and + the control-abstraction layer. +* [`systems/game-logic.md`](systems/game-logic.md) — main loop, level setup, + save/load, memory manager, page manager, debug overlay. +* [`systems/file-cache.md`](systems/file-cache.md) — chunk-based asset cache, + Carmack RLE + Huffman decompression, VGAGRAPH/VSWAP/audio file I/O. +* [`systems/ui-menu.md`](systems/ui-menu.md) — front-end menus, in-game text + pager, font rendering. +* [`systems/actordata.md`](systems/actordata.md) — actor structs, AI state + machines, projectiles, and hitscan. +* [`systems/tools-build.md`](systems/tools-build.md) — project files, `GO.BAT`, + `RULES.ASI`, the JAB HACK patcher, and the asm `EQU` tables. + +## Per-file reference — `files/` + +A page per major source file, each listing the file's role, its public +functions with a one-line summary, and its key globals. Covers all `WL_*.C` +and `ID_*.C` engine modules. + +## Reference indexes — `reference/` + +Exhaustive lookup tables: + +* [`reference/functions.md`](reference/functions.md) — functions grouped by + file, with signature and role. +* [`reference/constants.md`](reference/constants.md) — `#define`s, `enum`s, + and assembler equates, grouped by semantic family. + +## How to read this + +**First time:** read `ARCHITECTURE.md` end to end, pick a subsystem from the +§5.3 table, open its `systems/.md` page, then drop into `files/` for the +specific source file. + +**Hacking on the code:** find the file you're editing, read its `files/.md` +page for what's already there, and cross-reference `reference/constants.md` +for any constant you encounter. + +## Status + +This documentation describes the source as released; it does not modify, +build, or run any code. It was checked against the tree at git revision +`778abe0`. diff --git a/docs/files/ID_CA.md b/docs/files/ID_CA.md new file mode 100644 index 0000000..d38ac44 --- /dev/null +++ b/docs/files/ID_CA.md @@ -0,0 +1,54 @@ +# ID_CA.C + +The chunk-based asset cache. It opens the compiled game files (EGAGRAPH/VGAGRAPH, GAMEMAPS, AUDIOT/AUDIO, VSWAP), reads their chunk-offset tables, and expands individual chunks on demand through three cascaded decompressors: Carmack near-pointer expansion, Huffman expansion, and 16-bit RLEW expansion. It manages per-chunk mark/purge state so levels can be cached and released as the player moves between them. + +Part of the **file-cache** subsystem — see [systems/file-cache.md](../systems/file-cache.md). + +## Key globals + +| Global | Purpose | +|--------|---------| +| `mapon` (`ID_CA.C:56`) | Current map number | +| `mapsegs[MAPPLANES]` (`ID_CA.C:58`) | Decompressed map plane pointers | +| `grsegs[NUMCHUNKS]` (`ID_CA.C:61`) | Loaded graphics chunk pointers | +| `grneeded[NUMCHUNKS]` (`ID_CA.C:63`) | Per-chunk mark flags for caching | +| `audioname[13]` (`ID_CA.C:68`) | Audio data filename base (`"AUDIO."`) | +| `grstarts` / `audiostarts` (`ID_CA.C:99`,`:100`) | Chunk-offset tables (-1 = sparse) | +| `grhandle` / `maphandle` / `audiohandle` (`ID_CA.C:115`–`:117`) | Open file handles | +| `chunkcomplen` / `chunkexplen` (`ID_CA.C:119`) | Current chunk compressed / expanded lengths | + +## Functions + +| Function | Line | Role | +|----------|------|------| +| `GRFILEPOS()` | `ID_CA.C:132` | Look up a graphics chunk's file offset | +| `CA_OpenDebug()` / `CA_CloseDebug()` | `ID_CA.C:171`,`:177` | Open/close the profiling debug file | +| `CAL_GetGrChunkLength()` | `ID_CA.C:195` | Read a chunk's compressed length | +| `CA_FarRead()` | `ID_CA.C:213` | Far-pointer file read (>64 KB safe) | +| `CA_FarWrite()` | `ID_CA.C:249` | Far-pointer file write | +| `CA_ReadFile()` | `ID_CA.C:286` | Read a whole named file into a memptr | +| `CA_WriteFile()` | `ID_CA.C:315` | Write a buffer to a named file | +| `CA_LoadFile()` | `ID_CA.C:347` | Allocate + `CA_FarRead` a whole file | +| `CAL_OptimizeNodes()` | `ID_CA.C:387` | Pre-convert a huffnode table to pointers | +| `CAL_HuffExpand()` | `ID_CA.C:418` | Huffman decompression | +| `CAL_CarmackExpand()` | `ID_CA.C:609` | Carmack near/far RLE decompression | +| `CA_RLEWCompress()` | `ID_CA.C:677` | 16-bit RLEW compress | +| `CA_RLEWexpand()` | `ID_CA.C:734` | 16-bit RLEW expand | +| `CAL_SetupGrFile()` | `ID_CA.C:861` | Open graphics file, load huffman + chunk tables | +| `CAL_SetupMapFile()` | `ID_CA.C:942` | Open GAMEMAPS, load map headers | +| `CAL_SetupAudioFile()` | `ID_CA.C:1026` | Open AUDIOT, load chunk table | +| `CA_Startup()` / `CA_Shutdown()` | `ID_CA.C:1083`,`:1113` | Init / tear down the cache | +| `CA_CacheAudioChunk()` | `ID_CA.C:1134` | Load one audio chunk | +| `CA_CacheGrChunk()` | `ID_CA.C:1318` | Load + expand one graphics chunk | +| `CA_CacheMap()` | `ID_CA.C:1428` | Load + expand a level's map planes | +| `CA_UpLevel()` / `CA_DownLevel()` | `ID_CA.C:1506`,`:1533` | Push/pop the cache mark level | +| `CA_ClearMarks()` / `CA_ClearAllMarks()` | `ID_CA.C:1554`,`:1575` | Reset chunk mark flags | +| `CA_CacheMarks()` | `ID_CA.C:1651` | Load all currently-marked chunks | + +## Notes + +- Graphics chunks are stored Huffman-compressed; map planes are Carmack-then-RLEW compressed. `CA_CacheMap` chains `CAL_CarmackExpand` → `CA_RLEWexpand` (`ID_CA.C:1428`). +- `CAL_HuffExpand` (`:418`) has a `screenhack` path and hand-tuned inline-asm inner loops (see the asm fragments around `:495`/`:567`); `CAL_OptimizeNodes` (`:387`) pre-resolves node indices to pointers to speed it up. +- `grstarts`/`audiostarts` may hold -1 for sparse (missing) chunks; `GRFILEPOS` returns that sentinel. +- All file I/O funnels through `CA_FarRead`/`CA_FarWrite` because Borland's `read`/`write` cannot handle far pointers or >64 KB in one call. +- Memory for chunks comes from the memory manager (`ID_MM.C`); the cache only decides *what* is resident via mark/purge state. diff --git a/docs/files/ID_IN.md b/docs/files/ID_IN.md new file mode 100644 index 0000000..5777180 --- /dev/null +++ b/docs/files/ID_IN.md @@ -0,0 +1,48 @@ +# ID_IN.C + +The input manager. It hooks the keyboard interrupt (INT 9) to maintain a keydown table and ASCII/scancode state, detects and calibrates mouse and joysticks, and merges all three into a device-independent `ControlInfo` (movement direction + button state) that the game polls each frame. `INL_*` functions are internal helpers; `IN_*` functions are the public API. + +Part of the **input** subsystem — see [systems/input.md](../systems/input.md). + +## Key globals + +| Global | Purpose | +|--------|---------| +| `MousePresent` (`ID_IN.C:53`) | Mouse detected | +| `JoysPresent[MaxJoys]` (`ID_IN.C:54`) | Per-joystick detection | +| `JoyPadPresent` (`ID_IN.C:55`) | Pad-detection flag | +| `ASCIINames[]` (`ID_IN.C:81`) | Scancode → unshifted ASCII table | +| `IN_Started` (`ID_IN.C:119`) | Manager initialized | +| `CapsLock` (`ID_IN.C:120`) | Caps-lock toggle state | +| `CurCode`, `LastCode` (`ID_IN.C:121`) | ISR edge-detection scancode pair | +| `DirTable[]` (`ID_IN.C:123`) | Key-pair → movement `Direction` lookup | + +## Functions + +| Function | Line | Role | +|----------|------|------| +| `INL_KeyService()` | `ID_IN.C:143` | Keyboard ISR (INT 9); updates keydown table | +| `INL_GetMouseDelta()` | `ID_IN.C:218` | Read raw mouse movement | +| `INL_GetMouseButtons()` | `ID_IN.C:232` | Read mouse button mask | +| `IN_GetJoyAbs()` | `ID_IN.C:247` | Read raw joystick axis counts | +| `INL_GetJoyDelta()` | `ID_IN.C:324` | Scaled joystick delta vs. calibration | +| `INL_GetJoyButtons()` | `ID_IN.C:390` | Read joystick button mask | +| `IN_SetupJoy()` | `ID_IN.C:516` | Store joystick calibration min/max | +| `INL_StartKbd()` / `INL_ShutKbd()` | `ID_IN.C:430`,`:446` | Install / remove the INT 9 hook | +| `INL_StartMouse()` / `INL_ShutMouse()` | `ID_IN.C:459`,`:490` | Mouse driver setup / teardown | +| `INL_StartJoy()` / `INL_ShutJoy()` | `ID_IN.C:547`,`:572` | Joystick setup / teardown | +| `IN_Startup()` | `ID_IN.C:584` | Detect devices, install hooks | +| `IN_Shutdown()` | `ID_IN.C:641` | Restore original vectors | +| `IN_SetKeyHook()` | `ID_IN.C:663` | Register an external key listener | +| `IN_ClearKeysDown()` | `ID_IN.C:674` | Clear the keydown table | +| `IN_ReadControl()` | `ID_IN.C:691` | Fill a `ControlInfo` for a player | +| `IN_WaitForKey()` / `IN_WaitForASCII()` | `ID_IN.C:835`,`:852` | Block for a keypress | +| `IN_StartAck()` / `IN_CheckAck()` / `IN_Ack()` | `ID_IN.C:871`,`:891`,`:918` | "Press any input" acknowledge cycle | +| `IN_UserInput()` | `ID_IN.C:935` | Wait for input up to a timeout | + +## Notes + +- `INL_KeyService` (`:143`) is the raw INT 9 handler: it decodes make/break codes, tracks `CapsLock`, updates `Keyboard[]`, and chains `INL_KeyHook` before EOI. It must stay small and reentrancy-safe. +- `IN_ReadControl` (`:691`) is the single unifying poll: it reads whichever device the player's `ControlType` selects and normalizes to a `Direction` (via `DirTable`) plus buttons. +- Joysticks are read as capacitor-charge timing counts, so `IN_SetupJoy` calibration min/max are mandatory before `INL_GetJoyDelta` gives sane values. +- `IN_StartAck`/`IN_CheckAck` implement the non-blocking "wait for any key/button/joystick" used by title and intermission screens. diff --git a/docs/files/ID_MM.md b/docs/files/ID_MM.md new file mode 100644 index 0000000..5a90e88 --- /dev/null +++ b/docs/files/ID_MM.md @@ -0,0 +1,65 @@ +# `ID_MM.C` — memory manager (near / far / EMS block heap) + +The block allocator. `MML_*` functions are internal helpers; `MM_*` +functions are the public API. See +[`systems/game-logic.md`](../systems/game-logic.md) for the design +walkthrough. + +## Includes + +`WL_DEF.H` (which pulls `ID_MM.H`). `#pragma hdrstop`. + +## Globals + +| Symbol | Where | Brief | +|-------------------|---------|-------------------------------------------------------------| +| `bufferseg` | line 73 | memptr — the always-resident scratch buffer | +| `mmerror` | line 74 | boolean — last-op error flag | +| `beforesort` / `aftersort` | line 76 | function pointers run around `MM_SortMem` | +| `mmstarted` | line 87 | boolean — manager initialized | +| `farheap` | line 89 | void far * — far heap base | +| `nearheap` | line 90 | void * — near heap base | +| `bombonerror` | line 95 | boolean — abort on allocation failure | +| `XMSaddr` | line 99 | function pointer — far pointer to the XMS driver | + +## Internal helpers (`MML_*`) + +| Function | Where | Brief | +|-------------------------|-----------|-----------------------------------------------------------| +| `MML_CheckForXMS` | line 129 | Detect an XMS driver | +| `MML_SetupXMS` | line 156 | Allocate XMS handles | +| `MML_ShutdownXMS` | line 208 | Free XMS handles | +| `MML_UseSpace` | line 237 | Reserve a segment range from the block table | +| `MML_ClearBlock` | line 298 | Zero out a purgeable block | + +> `MML_CheckForEMS`, `MML_ShutdownEMS`, and `MM_MapEMS` are declared at +> lines 109–112 but their EMS logic overlaps `ID_PM.C`; the shipping +> Wolf3-D build routes EMS through the page manager. + +## Public API (`MM_*`) + +| Function | Where | Brief | +|-------------------------|-----------|-----------------------------------------------------------| +| `MM_Startup` | line 333 | Initialize the memory manager, near/far/EMS heaps | +| `MM_Shutdown` | line 413 | Free all heaps | +| `MM_GetPtr` | line 435 | Allocate a block of `size` bytes into `*baseptr` | +| `MM_FreePtr` | line 559 | Free a block | +| `MM_SetPurge` | line 594 | Set a block's purge level (0=locked … 3=purgeable) | +| `MM_SetLock` | line 630 | Lock / unlock a block against relocation | +| `MM_SortMem` | line 666 | Compact the heap, purging where allowed | +| `MM_ShowMemory` | line 772 | Debug — draw the heap map on screen | +| `MM_DumpData` | line 822 | Debug — write a heap report to file | +| `MM_UnusedMemory` | line 889 | Return count of free (unlocked) bytes | +| `MM_TotalFree` | line 919 | Return total free bytes including purgeable | +| `MM_BombOnError` | line 948 | Toggle abort-on-failure behavior | + +## Constants + +`SAVENEARHEAP=0x400`, `SAVEFARHEAP=0`, `BUFFERSIZE=0x1000`, +`MAXBLOCKS=700`, plus the `EMS_*` INT-0x67 function codes — see +[`reference/constants.md`](../reference/constants.md). + +## See also + +* [`systems/game-logic.md`](../systems/game-logic.md) +* [`files/ID_PM.md`](ID_PM.md) diff --git a/docs/files/ID_PM.md b/docs/files/ID_PM.md new file mode 100644 index 0000000..c65b93d --- /dev/null +++ b/docs/files/ID_PM.md @@ -0,0 +1,64 @@ +# `ID_PM.C` — EMS/XMS page manager + +The page manager provides a paged memory pool backed by main memory, +EMS (expanded memory), and XMS (extended memory). `PML_*` functions are +internal helpers; `PM_*` functions are the public API. See +[`systems/game-logic.md`](../systems/game-logic.md) for the design +walkthrough. + +## Includes + +`WL_DEF.H` (which pulls `ID_PM.H`). `#pragma hdrstop`. + +## Globals + +| Symbol | Where | Brief | +|-------------------|---------|----------------------------------------------------| +| `ParmStrings[]` | line 46 | `{"nomain","noems","noxms",nil}` | +| `EMMDriverName[9]` | line 79 | `"EMMXXXX0"` — the EMS driver device name | + +## Internal helpers (`PML_*`) + +| Function | Where | Brief | +|-------------------------|-----------|-----------------------------------------------------------| +| `PML_MapEMS` | line 58 | Map a logical page to a physical EMS frame | +| `PML_StartupEMS` | line 82 | Detect and allocate EMS pages | +| `PML_ShutdownEMS` | line 172 | Free EMS pages | +| `PML_StartupXMS` | line 197 | Detect and allocate XMS blocks | +| `PML_XMSCopy` | line 244 | Copy to/from XMS | +| `PML_CopyToXMS` | line 284 | Copy a page into XMS | +| `PML_CopyFromXMS` | line 294 | Copy a page out of XMS | +| `PML_ShutdownXMS` | line 304 | Free XMS blocks | +| `PML_StartupMainMem` | line 419 | Allocate the main-memory page pool | +| `PML_ShutdownMainMem` | line 448 | Free the main-memory pool | +| `PML_ReadFromFile` | line 469 | Read page data from the page file | +| `PML_OpenPageFile` | line 485 | Open the VSWAP page file | +| `PML_ClosePageFile` | line 535 | Close the page file | +| `PML_GetEMSAddress` | line 562 | Return the EMS frame address for a page | +| `PML_GiveLRUPage` | line 646 | Evict the least-recently-used main page | +| `PML_GiveLRUXMSPage` | line 678 | Evict the least-recently-used XMS page | +| `PML_PutPageInXMS` | line 705 | Move a page into XMS | +| `PML_TransferPageSpace` | line 735 | Move a page between backing stores | +| `PML_GetAPageBuffer` | line 780 | Acquire a page buffer | +| `PML_GetPageFromXMS` | line 832 | Fetch a page from XMS | +| `PML_LoadPage` | line 859 | Load a page from the page file | + +## Public API (`PM_*`) + +| Function | Where | Brief | +|-------------------------|-----------|-----------------------------------------------------------| +| `PM_SetMainMemPurge` | line 327 | Set the main-memory purge level | +| `PM_CheckMainMem` | line 350 | Verify main-memory pool integrity | +| `PM_GetPageAddress` | line 628 | Return a page's current physical address | +| `PM_GetPage` | line 877 | Get (loading if needed) a page's address | +| `PM_SetPageLock` | line 934 | Lock / unlock a page (`PMLockType`) | +| `PM_Preload` | line 948 | Preload all pages, calling a progress callback | +| `PM_NextFrame` | line 1073 | Advance the page manager one frame (updates LRU) | +| `PM_Reset` | line 1114 | Reset all page state | +| `PM_Startup` | line 1142 | Initialize the page manager | +| `PM_Shutdown` | line 1188 | Tear down the page manager | + +## See also + +* [`systems/game-logic.md`](../systems/game-logic.md) +* [`files/ID_MM.md`](ID_MM.md) diff --git a/docs/files/ID_SD.md b/docs/files/ID_SD.md new file mode 100644 index 0000000..7b82326 --- /dev/null +++ b/docs/files/ID_SD.md @@ -0,0 +1,80 @@ +# `ID_SD.C` — sound manager (Sound Blaster / AdLib / PC speaker / Sound Source) + +The sound manager. `SDL_*` functions are internal helpers; `SD_*` +functions are the public API. See +[`systems/audio.md`](../systems/audio.md) for the design walkthrough. + +## Includes + +`WL_DEF.H` (which pulls `ID_SD.H` / `ID_SD.EQU`). `#pragma hdrstop`. + +## Globals (selected) + +| Symbol | Where | Brief | +|-----------------------|---------|----------------------------------------------------------| +| `SD_Started` | line 85 | boolean — manager initialized | +| `SoundUserHook` | line 100| function pointer — per-tick game callback | +| `sbSamplePlaying` | line 118| volatile boolean — SB DMA in flight | +| `sbDMA` | line 121| byte = 1 — SB DMA channel | +| `sbInterrupt` | line 126| int = 7 — SB IRQ number | +| `carriers[9]` | line 155| byte[] — AdLib carrier register offsets | +| `alFXReg` | line 163| word — cached AdLib FX register | +| `tracks[sqMaxTracks]` | line 164| ActiveTrack*[] — active music tracks | + +## Internal helpers (`SDL_*`) + +Sound Blaster: `SDL_SetIntsPerSec` (208), `SDL_SetTimerSpeed` (215), +`SDL_SBStopSample` (263), `SDL_SBPlaySeg` (296), `SDL_SBService` (346), +`SDL_SBPlaySample` (383), `SDL_PositionSBP` (421), `SDL_CheckSB` (448), +`SDL_DetectSoundBlaster` (499), `SDL_SBSetDMA` (534), `SDL_StartSB` +(551), `SDL_ShutSB` (607). + +Sound Source (Disney/Covox): `SDL_SSStopSample` (637), `SDL_SSService` +(653), `SDL_SSPlaySample` (702), `SDL_StartSS` (719), `SDL_ShutSS` +(745), `SDL_CheckSS` (757), `SDL_DetectSoundSource` (808). + +PC speaker: `SDL_PCPlaySample` (830), `SDL_PCStopSample` (853), +`SDL_PCPlaySound` (879), `SDL_PCStopSound` (901), `SDL_PCService` +(922), `SDL_ShutPC` (977). + +Digitised routing: `SDL_LoadDigiSegment` (995), `SDL_PlayDigiSegment` +(1028), `SDL_DigitizedDone` (1165), `SDL_SetupDigi` (1235). + +AdLib / OPL2: `alOut` (1272), `SDL_SetInstrument` (1340), +`SDL_ALStopSound` (1389), `SDL_AlSetFXInst` (1401), `SDL_ALPlaySound` +(1433), `SDL_ALSoundService` (1471), `SDL_ALService` (1498), +`SDL_ShutAL` (1532), `SDL_CleanAL` (1551), `SDL_StartAL` (1571), +`SDL_DetectAdLib` (1585). + +Device lifecycle: `SDL_ShutDevice` (1723), `SDL_CleanDevice` (1743), +`SDL_StartDevice` (1755). + +## Public API (`SD_*`) + +| Function | Where | Brief | +|-------------------------|-----------|-----------------------------------------------------------| +| `SD_StopDigitized` | line 1045 | Stop digitised playback | +| `SD_Poll` | line 1084 | Per-frame digitised streaming poll | +| `SD_SetPosition` | line 1109 | Set stereo pan for digitised sound | +| `SD_PlayDigitized` | line 1130 | Play a digitised sample by chunk id | +| `SD_SetDigiDevice` | line 1193 | Choose the digitised backend (`SDSMode`) | +| `SD_SetSoundMode` | line 1774 | Choose the FX backend (`SDMode`) | +| `SD_SetMusicMode` | line 1830 | Choose the music backend (`SMMode`) | +| `SD_Startup` | line 1868 | Initialize the sound manager | +| `SD_Default` | line 2012 | Apply hardware-default modes | +| `SD_Shutdown` | line 2063 | Tear down the sound manager | +| `SD_SetUserHook` | line 2098 | Register the per-tick callback | +| `SD_PositionSound` | line 2110 | Set stereo pan for FX | +| `SD_PlaySound` | line 2123 | Play a sound effect (`soundnames`) | +| `SD_SoundPlaying` | line 2211 | Return the currently-playing sound id | +| `SD_StopSound` | line 2237 | Hard-stop the current FX | +| `SD_WaitSoundDone` | line 2263 | Block until the FX finishes | +| `SD_MusicOn` | line 2275 | Enable music | +| `SD_MusicOff` | line 2286 | Disable music | +| `SD_StartMusic` | line 2309 | Start a `MusicGroup` | +| `SD_FadeOutMusic` | line 2334 | Fade the current track out | +| `SD_MusicPlaying` | line 2352 | True if music is currently playing | + +## See also + +* [`systems/audio.md`](../systems/audio.md) diff --git a/docs/files/ID_US_1.md b/docs/files/ID_US_1.md new file mode 100644 index 0000000..81641a8 --- /dev/null +++ b/docs/files/ID_US_1.md @@ -0,0 +1,48 @@ +# ID_US_1.C + +The "user manager" (US) support library: bordered text windows, cursor-positioned string printing with a pluggable font backend, centered/multi-line text, window save/restore, and interactive single-line text input. `USL_*` functions are internal helpers; `US_*` are the public API used by the menu and text subsystems. + +Part of the **ui-menu** subsystem — see [systems/ui-menu.md](../systems/ui-menu.md). + +Note: definitions here are K&R-style with the return type on the preceding line (e.g. `US_Startup(void)` at `ID_US_1.C:169`), so grep the name and read the line above for the type. + +## Key globals + +| Global | Purpose | +|--------|---------| +| `ParmStrings[]` | `{"TEDLEVEL","NOWAIT"}` command-line parm table (`ID_US_1.C:45`) | +| `ParmStrings2[]` | `{"COMP","NOCOMP"}` command-line parm table (`ID_US_1.C:46`) | +| `US_Started` | Whether the user manager is initialized (`ID_US_1.C:47`) | +| `buf[32]` | Static number-format scratch buffer (`ID_US_1.C:88`) | +| `wr` | Current `WindowRec` (active window rectangle) (`ID_US_1.C:89`) | + +## Functions + +| Function | Line | Role | +|----------|------|------| +| `USL_HardError()` | `ID_US_1.C:81` | DOS critical-error (INT 24h) handler | +| `US_Startup()` | `ID_US_1.C:169` | Initialize the user manager, parse relevant parms | +| `US_Shutdown()` | `ID_US_1.C:221` | Tear down the user manager | +| `US_CheckParm()` | `ID_US_1.C:237` | Match a command-line arg against a string table | +| `US_SetPrintRoutines()` | `ID_US_1.C:275` | Install measure/print callbacks (font backend) | +| `US_Print()` | `ID_US_1.C:288` | Print a string at the current cursor, honoring newlines | +| `US_PrintUnsigned()` | `ID_US_1.C:325` | Print an unsigned long | +| `US_PrintSigned()` | `ID_US_1.C:338` | Print a signed long | +| `USL_PrintInCenter()` | `ID_US_1.C:351` | Center a string within a `Rect` | +| `US_PrintCentered()` | `ID_US_1.C:371` | Print centered in the current window | +| `US_CPrintLine()` | `ID_US_1.C:390` | Print one horizontally-centered line | +| `US_CPrint()` | `ID_US_1.C:411` | Print centered, multi-line | +| `US_ClearWindow()` | `ID_US_1.C:440` | Clear the current window to background | +| `US_DrawWindow()` | `ID_US_1.C:453` | Draw a bordered window at `(x,y,w,h)` | +| `US_CenterWindow()` | `ID_US_1.C:489` | Draw a window centered on screen | +| `US_SaveWindow()` | `ID_US_1.C:501` | Save the current window rectangle into a `WindowRec` | +| `US_RestoreWindow()` | `ID_US_1.C:519` | Restore a saved window rectangle | +| `USL_XORICursor()` | `ID_US_1.C:538` | XOR-draw/erase the input caret | +| `US_LineInput()` | `ID_US_1.C:574` | Interactive one-line text entry with default and escape | + +## Notes + +- Printing is backend-agnostic: `US_SetPrintRoutines` installs the measure/print callbacks so the same `US_Print*` code works with the game's scaled font. +- Window state is a single global `wr` (`WindowRec`); `US_SaveWindow`/`US_RestoreWindow` let callers nest windows by stashing and restoring it. +- `US_LineInput` and `USL_XORICursor` implement the caret and editing used by save-game naming in `WL_MENU.C`. +- `US_CheckParm`/`ParmStrings*` support the developer command-line switches (TEDLEVEL, NOWAIT, COMP/NOCOMP) parsed at `US_Startup`. diff --git a/docs/files/ID_VH.md b/docs/files/ID_VH.md new file mode 100644 index 0000000..2f976ec --- /dev/null +++ b/docs/files/ID_VH.md @@ -0,0 +1,45 @@ +# ID_VH.C + +The mid-level "view helper" layer above the VGA driver. It draws proportional-font strings, tiles, and cached pics into an off-screen buffer; tracks dirty rectangles so `VW_UpdateScreen` only flushes changed 16×16 blocks; preloads latch graphics; and implements the fizzle-fade screen transition. This is the drawing API the game UI and menus actually call. + +Part of the **rendering** subsystem — see [systems/rendering.md](../systems/rendering.md). + +## Key globals + +| Global | Purpose | +|--------|---------| +| `update[UPDATEHIGH][UPDATEWIDE]` (`ID_VH.C:21`) | Dirty-block bitmap for incremental redraw | +| `px`, `py` (`ID_VH.C:28`) | Current text cursor position | +| `fontcolor`, `backcolor` (`ID_VH.C:29`) | Current font fg/bg colors | +| `fontnumber` (`ID_VH.C:30`) | Current font index | +| `bufferwidth`, `bufferheight` (`ID_VH.C:31`) | Back-buffer dimensions | + +## Functions + +| Function | Line | Role | +|----------|------|------| +| `VW_DrawPropString()` | `ID_VH.C:40` | Draw a proportional-font string | +| `VW_DrawColorPropString()` | `ID_VH.C:96` | Draw a string in a given color | +| `VL_MungePic()` | `ID_VH.C:170` | Planar-reorder a pic for fast blitting | +| `VWL_MeasureString()` | `ID_VH.C:206` | Internal string pixel-size measure | +| `VW_MeasurePropString()` | `ID_VH.C:214` | Measure a single-line string | +| `VW_MeasureMPropString()` | `ID_VH.C:219` | Measure a multiline string | +| `VW_MarkUpdateBlock()` | `ID_VH.C:246` | Mark a rectangle dirty | +| `VWB_DrawTile8()` | `ID_VH.C:291` | Draw an 8×8 tile (marks dirty) | +| `VWB_DrawTile8M()` | `ID_VH.C:297` | Draw an 8×8 masked tile | +| `VWB_DrawPic()` | `ID_VH.C:304` | Draw a graphics chunk | +| `VWB_DrawPropString()` | `ID_VH.C:320` | Draw a font string + mark dirty | +| `VWB_Bar()` | `ID_VH.C:329` | Filled bar + mark dirty | +| `VWB_Plot()` | `ID_VH.C:335` | Plot + mark dirty | +| `VWB_Hlin()` / `VWB_Vlin()` | `ID_VH.C:341`,`:347` | Marked line primitives | +| `VW_UpdateScreen()` | `ID_VH.C:353` | Flush dirty blocks to the framebuffer | +| `LatchDrawPic()` | `ID_VH.C:375` | Draw a preloaded latch pic | +| `LoadLatchMem()` | `ID_VH.C:397` | Preload latch graphics into VRAM | +| `FizzleFade()` | `ID_VH.C:471` | Pseudo-random-pixel fizzle transition | + +## Notes + +- The `VWB_*` (buffered) entry points wrap the `VL_*` primitives *and* call `VW_MarkUpdateBlock`; the `VW_*` variants draw without marking. Choosing the wrong one causes either missing redraws or full-screen flushes. +- `VW_UpdateScreen` scans the `update[][]` grid and copies only marked 16×16 cells, which is what keeps the game fast in software rendering. +- `FizzleFade` (`:471`) uses a 17-bit LFSR to visit every pixel once in a scrambled order — the classic Wolf3-D death/transition effect. +- `VL_MungePic` (`:170`) pre-swizzles a chunky pic into VGA plane order so `VWB_DrawPic` can blit it without per-pixel plane selects. diff --git a/docs/files/ID_VL.md b/docs/files/ID_VL.md new file mode 100644 index 0000000..5101f16 --- /dev/null +++ b/docs/files/ID_VL.md @@ -0,0 +1,47 @@ +# ID_VL.C + +The low-level VGA Mode-X driver. It sets up planar 256-color mode, manages the DAC palette (including fade in/out), and provides the primitive draw operations (plot, hline, vline, bar) plus the latch/screen block copies used to blit precompiled tiles and pics directly in VGA memory. Higher layers (`ID_VH.C`, the ray caster) build on these primitives. + +Part of the **rendering** subsystem — see [systems/rendering.md](../systems/rendering.md). + +## Key globals + +| Global | Purpose | +|--------|---------| +| `screenfaded` (`ID_VL.C:23`) | True when the DAC is faded to black | +| `fastpalette` (`ID_VL.C:26`) | Use batched `outsb` palette writes | + +## Functions + +| Function | Line | Role | +|----------|------|------| +| `VL_Startup()` | `ID_VL.C:71` | Detect Mode-X-capable hardware | +| `VL_Shutdown()` | `ID_VL.C:101` | Restore text mode | +| `VL_SetVGAPlaneMode()` | `ID_VL.C:115` | Enter planar Mode-X | +| `VL_SetTextMode()` | `ID_VL.C:133` | Return to 80×25 text | +| `VL_ClearVideo()` | `ID_VL.C:151` | Fill the whole screen with a color | +| `VL_DePlaneVGA()` | `ID_VL.C:192` | Convert planar → chunky (porting aid) | +| `VL_SetLineWidth()` | `ID_VL.C:246` | Change CRTC scanline length | +| `VL_SetSplitScreen()` | `ID_VL.C:277` | Set the status-bar split line | +| `VL_FillPalette()` | `ID_VL.C:309` | Set all 256 DAC entries to one color | +| `VL_SetColor()` / `VL_GetColor()` | `ID_VL.C:332`,`:350` | Set / read one DAC entry | +| `VL_SetPalette()` / `VL_GetPalette()` | `ID_VL.C:371`,`:428` | Write / read the full 768-byte palette | +| `VL_FadeOut()` / `VL_FadeIn()` | `ID_VL.C:450`,`:500` | Palette fade animations | +| `VL_ColorBorder()` | `ID_VL.C:569` | Overscan-border color trick | +| `VL_Plot()` | `ID_VL.C:601` | Plot one pixel | +| `VL_Hlin()` / `VL_Vlin()` | `ID_VL.C:620`,`:665` | Horizontal / vertical line | +| `VL_Bar()` | `ID_VL.C:692` | Filled rectangle | +| `VL_MemToLatch()` | `ID_VL.C:752` | Copy pic data into a latch plane | +| `VL_MemToScreen()` | `ID_VL.C:791` | Blit chunky source to planar screen | +| `VL_MaskedToScreen()` | `ID_VL.C:826` | Blit with a mask plane | +| `VL_LatchToScreen()` | `ID_VL.C:861` | Fast latch → screen copy | +| `VL_ScreenToScreen()` | `ID_VL.C:909` | On-VRAM block copy | +| `VL_DrawTile8String()` | `ID_VL.C:960` | Draw an 8×8-tile text string | +| `VL_SizeTile8String()` | `ID_VL.C:1071` | Measure a tile8 string | + +## Notes + +- Two `VL_Startup` definitions exist (`:51` and `:71`); the shipping build compiles the `:71` variant — the earlier one is bracketed out. +- Latch operations (`VL_MemToLatch`, `VL_LatchToScreen`) exploit VGA's 4-plane latch registers to copy 4 pixels per bus cycle; this is why tiles are 8×8 and 4-pixel aligned. +- Palette fades run in `steps` DAC writes; `VL_WaitVBL` (declared `:37`, defined in asm) synchronizes each step to the vertical blank to avoid tearing. +- `VL_VideoID`, `VL_SetCRTC`, `VL_SetScreen`, `VL_WaitVBL` are declared here (`:34`–`:37`) but implemented in the accompanying `.ASM`. diff --git a/docs/files/WL_ACT1.md b/docs/files/WL_ACT1.md new file mode 100644 index 0000000..212175a --- /dev/null +++ b/docs/files/WL_ACT1.md @@ -0,0 +1,45 @@ +# WL_ACT1.C + +The non-AI actor infrastructure: static map things (lamps, treasure, decorations), the door subsystem (spawning, opening/closing, per-tick animation), area flood-fill connectivity, and pushwalls. No enemy thinking lives here — that is `WL_ACT2.C`. + +Part of the **actordata** subsystem — see [systems/actordata.md](../systems/actordata.md). + +## Key globals + +| Global | Purpose | +|--------|---------| +| `statobjlist[MAXSTATS]`, `laststatobj` | Static-thing array and end pointer (`WL_ACT1.C:15`) | +| `doorobjlist[MAXDOORS]`, `lastdoorobj` | Door object array and end pointer (`WL_ACT1.C:272`) | +| `doornum` | Current door count (`WL_ACT1.C:273`) | +| `doorposition[MAXDOORS]` | Leading edge of each door, 0=closed (`WL_ACT1.C:275`) | +| `areabyplayer[NUMAREAS]` | Which areas connect to the player's area this tick (`WL_ACT1.C:280`) | +| `pwallstate`, `pwallpos`, `pwalldir` | Active pushwall animation state (`WL_ACT1.C:719`) | + +## Functions + +| Function | Line | Role | +|----------|------|------| +| `InitStaticList()` | `WL_ACT1.C:124` | Reset the static-object list at level load | +| `SpawnStatic()` | `WL_ACT1.C:139` | Insert a static thing from a map tile into `statobjlist` | +| `PlaceItemType()` | `WL_ACT1.C:199` | Place a pickup of a given type at a tile | +| `RecursiveConnect()` | `WL_ACT1.C:293` | Flood-fill connect one area number through open doors | +| `ConnectAreas()` | `WL_ACT1.C:308` | Recompute area connectivity from the player's area | +| `InitAreas()` | `WL_ACT1.C:316` | Initialize `areabyplayer` tracking | +| `InitDoorList()` | `WL_ACT1.C:332` | Reset the door list at level load | +| `SpawnDoor()` | `WL_ACT1.C:350` | Create a door object at a tile (vertical/horizontal, lock) | +| `OpenDoor()` | `WL_ACT1.C:400` | Switch a door to its opening state | +| `CloseDoor()` | `WL_ACT1.C:417` | Switch a door to its closing state (with actor check) | +| `OperateDoor()` | `WL_ACT1.C:498` | Player-triggered open/close, checks locks/keys | +| `DoorOpen()` | `WL_ACT1.C:538` | Per-tick handler while a door is fully open (auto-close timer) | +| `DoorOpening()` | `WL_ACT1.C:554` | Per-tick handler advancing an opening door | +| `DoorClosing()` | `WL_ACT1.C:617` | Per-tick handler advancing a closing door | +| `MoveDoors()` | `WL_ACT1.C:686` | Iterate all doors and run their per-tick handlers | +| `PushWall()` | `WL_ACT1.C:732` | Begin pushing a secret pushwall in a direction | +| `MovePWalls()` | `WL_ACT1.C:809` | Advance the active pushwall each tick | + +## Notes + +- Doors are a fixed pool indexed by tile value; `doorposition[]` is the animation edge read by the raycaster in `WL_DRAW.C`. +- `ConnectAreas`/`RecursiveConnect` power sound propagation and the `areabyplayer` gate that determines which actors can hear the player. +- Only one pushwall moves at a time — `pwallstate`/`pwallpos`/`pwalldir` are scalar globals, not an array. +- Static things live in `statobjlist` separate from live actors; `SpawnStatic` and `PlaceItemType` are the two entry points, called from map setup and `DropItem` (in `WL_STATE.C`) respectively. diff --git a/docs/files/WL_ACT2.md b/docs/files/WL_ACT2.md new file mode 100644 index 0000000..a89c147 --- /dev/null +++ b/docs/files/WL_ACT2.md @@ -0,0 +1,57 @@ +# WL_ACT2.C + +The per-enemy AI file and the largest `WL_*` source. Contains the spawn routines for every enemy and boss, their `T_*` think handlers and `A_*` action handlers, the projectile system, and the BJ victory-run sequence. It builds entirely on the movement/sight/damage primitives in `WL_STATE.C`. + +Part of the **actordata** subsystem — see [systems/actordata.md](../systems/actordata.md). + +## Key globals + +| Global | Purpose | +|--------|---------| +| `dirtable[9]` | Map of `dirtype` to screen/step direction (`WL_ACT2.C:39`) | +| `starthitpoints[4][NUMENEMIES]` | Per-difficulty hit points for each enemy type (`WL_ACT2.C:42`) | +| `s_rocket`, `s_smoke1..4`, `s_boom1..3` | Projectile/explosion state tables (`WL_ACT2.C:181`) | +| `s_hrocket`, `s_hsmoke*`, `s_hboom*` | Hitler-rocket projectile state tables (`WL_ACT2.C:203`) | +| `s_grdstand`, `s_grdpath1`, ... | Static `statetype` chains driving each enemy's animation graph (`WL_ACT2.C:418`) | + +## Functions + +| Function | Line | Role | +|----------|------|------| +| `A_Smoke()` | `WL_ACT2.C:233` | Attach trailing smoke to a projectile | +| `ProjectileTryMove()` | `WL_ACT2.C:266` | Move a projectile one step, test for hits/walls | +| `T_Projectile()` | `WL_ACT2.C:302` | Per-tick projectile think (fireball/rocket) | +| `SpawnStand()` | `WL_ACT2.C:847` | Spawn a standing guard/enemy of `enemy_t which` | +| `SpawnPatrol()` | `WL_ACT2.C:983` | Spawn a patrolling enemy on a path | +| `A_DeathScream()` | `WL_ACT2.C:1061` | Play the death sound for an actor | +| `SpawnUber()` | `WL_ACT2.C:1327` | Spawn an Uber mutant | +| `T_UShoot()` | `WL_ACT2.C:1351` | Uber-mutant shoot handler | +| `SpawnWill()` / `T_Will()` | `WL_ACT2.C:1426` / `1450` | Spawn and think for Will (rocket boss) | +| `SpawnDeath()` / `T_Launch()` | `WL_ACT2.C:1601` / `1624` | Spawn Death Knight and its rocket-launch handler | +| `A_Slurpie()` / `A_Breathing()` | `WL_ACT2.C:1775` / `1781` | Boss ambient-sound actions | +| `SpawnAngel()` | `WL_ACT2.C:1794` | Spawn the Angel of Death | +| `A_Victory()` / `A_StartAttack()` / `A_Relaunch()` | `WL_ACT2.C:1820` / `1834` / `1848` | Angel attack-cycle actions | +| `SpawnSpectre()` / `A_Dormant()` | `WL_ACT2.C:1913` / `1934` | Spawn a Spectre and its dormant/wake action | +| `SpawnGhosts()` | `WL_ACT2.C:1994` | Spawn a Pac-Man ghost | +| `SpawnSchabbs()` / `T_Schabb()` | `WL_ACT2.C:2212` / `2380` | Spawn Dr. Schabbs and his think handler | +| `SpawnGift()` / `T_Gift()` | `WL_ACT2.C:2241` / `2472` | Spawn Gift-mutant and its think handler | +| `SpawnFat()` / `T_Fat()` | `WL_ACT2.C:2270` / `2564` | Spawn Fat-mutant and its think handler | +| `T_SchabbThrow()` / `T_GiftThrow()` | `WL_ACT2.C:2299` / `2339` | Boss projectile-throw handlers | +| `SpawnFakeHitler()` / `T_Fake()` | `WL_ACT2.C:2826` / `2974` | Spawn Fake Hitler and its think/teleport handler | +| `SpawnHitler()` / `A_HitlerMorph()` | `WL_ACT2.C:2856` / `2886` | Spawn Hitler and morph into Mecha-Hitler | +| `T_Stand()` | `WL_ACT2.C:3047` | Standing-guard think (wait for sight) | +| `T_Chase()` | `WL_ACT2.C:3069` | Generic chase think used by most enemies | +| `T_Ghosts()` | `WL_ACT2.C:3207` | Ghost chase think | +| `T_DogChase()` | `WL_ACT2.C:3257` | Attack dog chase think | +| `SelectPathDir()` / `T_Path()` | `WL_ACT2.C:3340` / `3367` | Follow patrol path markers | +| `T_Shoot()` | `WL_ACT2.C:3444` | Ranged-attack handler | +| `T_Bite()` | `WL_ACT2.C:3530` | Dog melee-bite handler | +| `SpawnBJVictory()` | `WL_ACT2.C:3621` | Start the BJ end-of-level victory run | +| `T_BJRun()` / `T_BJJump()` / `T_BJYell()` / `T_BJDone()` | `WL_ACT2.C:3643` / `3681` / `3698` / `3713` | BJ victory-run animation stages | + +## Notes + +- Every `Spawn*` calls `SpawnNewObj`/`SpawnStand` (in `WL_STATE.C`) to allocate the objtype, then points it at a `statetype` chain declared as globals in this file. +- `starthitpoints[difficulty][enemy]` is indexed by the current `gamestate.difficulty`, so the same spawn code scales enemy HP across skill levels. +- Projectiles (`T_Projectile`/`ProjectileTryMove`) are actors too, using the `s_rocket`/`s_smoke*`/`s_boom*` state tables and `A_Smoke` for trails. +- `T_Chase` is the shared workhorse think; the boss- and dog-specific handlers (`T_DogChase`, `T_Ghosts`, `T_Fake`) diverge only in movement/attack detail while reusing `SelectChaseDir`/`SightPlayer` from `WL_STATE.C`. diff --git a/docs/files/WL_AGENT.md b/docs/files/WL_AGENT.md new file mode 100644 index 0000000..caca5a0 --- /dev/null +++ b/docs/files/WL_AGENT.md @@ -0,0 +1,59 @@ +# `WL_AGENT.C` — player input, status-bar widgets, weapon code + +The player (a.k.a. "agent") per-tick logic. Contains all input +processing for player movement, weapon firing, status-bar widget +drawing, and player damage / ammo / score / lives / keys logic. + +## Includes + +`WL_DEF.H`. `#pragma hdrstop`. + +## Globals defined here + +| Symbol | Where | Type / Initial value | +|---------------------------------------|-----------|----------------------------------------------------------------------------| +| `running` | line 35 | boolean — true while player holds the run key | +| `thrustspeed` | line 36 | long — current thrust scalar | +| `anglefrac` | line 40 | int — sub-angle precision | +| `gotgatgun` | line 41 | int (JR) — machine-gun-pickup flag | +| `playerxmove`, `playerymove` | line 61 | long — fixed-point x/y motion this tick | +| `strafeangle[9]` | line 76 | int[9] — strafe direction LUT (forward, left, right, etc.) | + +## Public functions + +| Function | Where | Brief | +|---------------------------|-------------|----------------------------------------------------------------------------------| +| `CheckWeaponChange` | line 117 | React to a "weapon change" keypress | +| `ControlMovement` | line 149 | Translate input → `playerxmove`, `playerymove`, `anglefrac` | +| `StatusDrawPic` | line 244 | Draw a chunk-graphics pic into the status bar | +| `DrawFace` | line 270 | Draw B.J.'s current face pic to the HUD | +| `UpdateFace` | line 305 | Animate the face pic depending on player state | +| `facecount` | line 305 | Static — current animation phase | +| `LatchNumber` | line 337 | Write a fixed-width decimal number into a status-bar field | +| `DrawHealth` | line 372 | Update the health bar | +| `TakeDamage` | line 386 | Apply damage; flash the screen red | +| `HealSelf` | line 434 | Increment health; update HUD | +| `DrawLevel` | line 457 | Write the level number to the HUD | +| `DrawLives` | line 478 | Write the lives-icon count | +| `GiveExtraMan` | line 492 | Increment `gamestate.lives` | +| `DrawScore` | line 510 | Write the current score | +| `GivePoints` | line 523 | Add points (long) and trigger HIGH-score check | +| `DrawWeapon` | line 544 | Update the held-weapon icon | +| `DrawKeys` | line 558 | Draw the gold/silver/blue key icons | +| `GiveWeapon` | line 581 | Add a weapon to `gamestate.weapons` | +| `DrawAmmo` | line 603 | Update the ammo field | +| `GiveAmmo` | line 617 | Add ammo to inventory | +| `GiveKey` | line 643 | Mark a key as collected | +| `TryMove` | line 94 | Validate `ob`'s move against walls + actors; used by `T_Player` | +| `T_Player` | line 95 | Player state tick | +| `ClipMove` | line 97 | Sub-tile clipping step | + +## Constants + +* `MAXMOUSETURN 10` +* `MOVESCALE 150l`, `BACKMOVESCALE 100l`, `ANGLESCALE 20` + +## See also + +* [`systems/input.md`](../systems/input.md) +* [`systems/rendering.md`](../systems/rendering.md) — for `DrawWeapon` etc. diff --git a/docs/files/WL_DEBUG.md b/docs/files/WL_DEBUG.md new file mode 100644 index 0000000..6c57f1d --- /dev/null +++ b/docs/files/WL_DEBUG.md @@ -0,0 +1,32 @@ +# WL_DEBUG.C + +Carmack's developer debug overlay, gated on the `DEBUGKEYS` compile flag. Provides memory/actor reports, the overhead map viewer, sprite/shape testing, and the F-key debug menu used during development. + +Part of the **game-logic** subsystem — see [systems/game-logic.md](../systems/game-logic.md). + +## Key globals + +| Global | Purpose | +|--------|---------| +| `maporgx` | Overhead-map viewport origin X (`WL_DEBUG.C:38`) | +| `maporgy` | Overhead-map viewport origin Y (`WL_DEBUG.C:39`) | +| `buf[10]` | Static number-format scratch buffer (`WL_DEBUG.C:221`) | + +## Functions + +| Function | Line | Role | +|----------|------|------| +| `DebugMemory()` | `WL_DEBUG.C:54` | Print a memory-manager usage report | +| `CountObjects()` | `WL_DEBUG.C:86` | Count and report active actors in the pool | +| `PicturePause()` | `WL_DEBUG.C:137` | Freeze the screen for a dev screenshot | +| `ShapeTest()` | `WL_DEBUG.C:217` | Step through and display sprite/shape chunks | +| `DebugKeys()` | `WL_DEBUG.C:415` | Read F-key input and dispatch debug submenus/cheats | +| `OverheadRefresh()` | `WL_DEBUG.C:611` | Redraw the overhead map each tick while active | +| `ViewMap()` | `WL_DEBUG.C:669` | Enter the scrollable overhead minimap viewer | + +## Notes + +- The whole file is meant to be compiled out of shipping builds; entry points are reached only when `DEBUGKEYS` is set and the tab/debug key is held. +- `ViewMap`/`OverheadRefresh` use `maporgx`/`maporgy` to pan a tile-for-tile top-down render of the current level. +- `DebugKeys` is the dispatcher: it invokes the reporting routines (`DebugMemory`, `CountObjects`), `PicturePause`, and toggles cheats like god mode and no-clip. +- `ShapeTest` reads directly from the loaded sprite chunks (`ID_CA.C`/`ID_PM.C`), making it a quick way to validate the compiled shape data. diff --git a/docs/files/WL_DRAW.md b/docs/files/WL_DRAW.md new file mode 100644 index 0000000..29103ca --- /dev/null +++ b/docs/files/WL_DRAW.md @@ -0,0 +1,72 @@ +# `WL_DRAW.C` — 3-D raycaster, scaled sprites, gameplay tick + +The biggest rendering file in the engine. Contains: + +1. The 3-D **raycaster** — per-column wall drawing. +2. The **scaled-shape** renderer (`DrawScaleds`). +3. The **`PlayLoop`** function — gameplay per-tick. +4. The **`CalcTics`** function — timer-drop measure. + +## Includes + +`WL_DEF.H`, ``. `#pragma hdrstop`. + +## Globals defined here + +| Symbol | Where | Type / Initial value | +|---------------------|-------------|----------------------------------------------------------------------------| +| `lasttimecount` | line 39 | long — for `CalcTics` | +| `frameon` | line 40 | long — for `CalcTics` | +| `tileglobal` | line 44 | fixed = `TILEGLOBAL` | +| `mindist` | line 45 | fixed = `MINDIST` (close-clip distance) | +| `pixelangle[]` | line 51 | int[MAXVIEWWIDTH] — per-column angle offset | +| `finetangent[]` | line 52 | far long[FINEANGLES/4] — atan LUT for vertical DDA | +| `sintable[]` | line 53 | far fixed[ANGLES+ANGLES/4] — sine LUT | +| `costable` | line 53 | far *fixed (=sintable + ANGLES/4) — cos LUT | +| `viewx`, `viewy` | line 58 | fixed — camera position | +| `viewangle` | line 59 | int — camera heading | +| `viewsin`, `viewcos`| line 60 | fixed — sin/cos of camera heading | +| `wallheight[]` | line 102 | int[MAXWALLTILES] — wall texture heights | +| `weaponscale[]` | line 1198 | int[NUMWEAPONS] — weapon frame scaling factors | + +## Public functions + +| Function | Where | Brief (one-line role) | +|---------------------------|---------------|--------------------------------------------------------------------------------------------------| +| `FixedByFrac` | line 141 | 16.16 fixed-point multiply | +| `TransformActor` | line 210 | Project an actor's world position into screen-space | +| `TransformTile` | line 287 | Project a map tile (used for door / pushwall column) | +| `CalcHeight` | line 359 | Compute wall column height + post source for the current ray | +| `ScalePost` (near) | line 400 | VGA-mode scaled post strip — column-by-column draw | +| `FarScalePost` | line 460 | Far-pointer variant of `ScalePost` (long-distance column) | +| `HitVertWall` | line 477 | DDA hit on a vertical wall | +| `HitHorizWall` | line 550 | DDA hit on a horizontal wall | +| `HitHorizDoor` | line 620 | Hit when ray crosses a vertical-axis door | +| `HitVertDoor` | line 688 | Hit when ray crosses a horizontal-axis door | +| `HitHorizPWall` | line 759 | Hit when ray crosses a vertical-axis pushwall | +| `HitVertPWall` | line 823 | Hit when ray crosses a horizontal-axis pushwall | +| `VGAClearScreen` | line 970 | Plain VGA clear (Mode-X variant) | +| `ClearScreen` | line 889 | Programmatic clear of the back buffer | +| `CalcRotate` | line 1024 | Compute the actor's frame index from viewangle | +| `DrawScaleds` | line 1072 | Per-tick sprite-scale drawer | +| `weaponscale[NUMWEAPONS]` | line 1198 | Holds the per-weapon sprite scaling factors | +| `DrawPlayerWeapon` | line 1201 | Draw B.J.'s held weapon on the screen | +| `CalcTics` | line 1236 | Tick rate measurement | +| `ThreeDRefresh` | `WL_DRAW.C:1336`| Per-tick entry — orchestrates the renderer | +| `PlayLoop` | `WL_DRAW.C:1368`| Per-gameplay-tick loop — drives player + actor state | + +## Static / local helpers + +`AsmRefresh` is declared in C at line 64 (`void AsmRefresh (void); // in +WL_DR_A.ASM`); the asm provides the actual implementation. + +## Constants specific to this file + +* `DOORWALL (PMSpriteStart-8)` (line 19) +* `ACTORSIZE 0x4000` (line 21) + +## See also + +* [`systems/rendering.md`](../systems/rendering.md) +* [`systems/game-logic.md`](../systems/game-logic.md) — for `PlayLoop`. +* [`WL_DR_A.ASM`](#) — assembly-side hot paths. \ No newline at end of file diff --git a/docs/files/WL_GAME.md b/docs/files/WL_GAME.md new file mode 100644 index 0000000..547eebf --- /dev/null +++ b/docs/files/WL_GAME.md @@ -0,0 +1,41 @@ +# `WL_GAME.C` — main game loop and level setup + +## Includes + +`WL_DEF.H`. Optionally `` under `MYPROFILE` define. + +## Globals + +| Symbol | Where | Brief | +|------------------------------|---------|----------------------------------------------------------------------| +| `ingame`, `fizzlein` | line 28 | boolean — in-game status, intro-fizzle-in flag | +| `spearx`, `speary` | line 32 | long — Spear-of-Destiny final level x/y | +| `spearflag` | line 34 | boolean | +| `ElevatorBackTo[]` | line 39 | int[] — per-episode elevator back-to map | + +## Public functions + +| Function | Where | Brief | +|---------------------------|-------------|---------------------------------------------------------------------------------------| +| `ScanInfoPlane` | line 112 | (declared 41) — Walk the map, place statstructs / actors, build `areabyplayer` | +| `PlaySoundLocGlobal` | line 170 | Play a sound at a global map coordinate | +| `UpdateSoundLoc` | line 181 | Per-tick update of streaming sound positions | +| `ClearMemory` | line 203 | Reset global per-level state | +| `ScanInfoPlane` | line 221 | (def) — see line 112 | +| `SetupGameLevel` | line 625 | Load GAMEMAPS, parse map, init actors | +| `DrawPlayBorderSides` | line 777 | Draw the side strips of the status bar | +| `DrawAllPlayBorderSides` | line 800 | Draw both sides + the bottom | +| `DrawAllPlayBorder` | line 820 | Draw the full status-bar | +| `DrawPlayBorder` | line 841 | Clean draw of the play border (no widget overdraw) | +| `DrawPlayScreen` | line 868 | Full status-bar draw | +| `StartDemoRecord` | line 914 | Begin demo recording | +| `demoname[13]` | line 935 | `"DEMO?."` | +| `FinishDemoRecord` | line 937 | Close the demo buffer | +| `RecordDemo` | line 979 | Enter demo-record mode (after Tab held at title screen) | +| `PlayDemo` | line 1044 | Play back a saved demo buffer | +| `Died` | line 1114 | Player-death screen | +| `GameLoop` | line 1238 | Per-tick game-loop driver | + +## See also + +* [`systems/game-logic.md`](../systems/game-logic.md) diff --git a/docs/files/WL_INTER.md b/docs/files/WL_INTER.md new file mode 100644 index 0000000..c0c8901 --- /dev/null +++ b/docs/files/WL_INTER.md @@ -0,0 +1,31 @@ +# `WL_INTER.C` — intermission screens (level / episode / death) + +Manages the screens between gameplay: level-completed animation, +victory sequence, BJ breathe animation, PG13 splash, the +copy-protection lookup screen and the high-scores draw. + +## Includes + +`WL_DEF.H`. `#pragma hdrstop`. + +## Public functions + +| Function | Where | Brief | +|---------------------------|-------------|--------------------------------------------------------------------------------| +| `ClearSplitVWB` | line 17 | Wipe the split-VWB back buffer | +| `EndScreen` | line 37 | Per-episode end-cutscene | +| `EndSpear` | line 50 | Spear-of-Destiny end-cutscene | +| `Victory` | line 108 | Episode-completion cutscene (BJ breathe, etc.) | +| `PG13` | line 310 | Splash screen | +| `Write` | line 331 | String + cursor writer | +| `BJ_Breathe` | line 392 | BJ Victory animation | +| `LevelCompleted` | line 429 | Per-level stats screen | +| `PreloadUpdate` | line 972 | Tick-progress hook for `PreloadGraphics` | +| `PreloadGraphics` | line 995 | Pre-load level graphics | +| `CopyProFailedStrs[][]` | line 1321 | Copy-protection failure strings | +| `BackDoor` | line 1461 | Hidden debug menu | +| `CopyProtection` | line 1485 | Copy-protection screen | + +## See also + +* [`systems/ui-menu.md`](../systems/ui-menu.md) diff --git a/docs/files/WL_MAIN.md b/docs/files/WL_MAIN.md new file mode 100644 index 0000000..1e2a2d4 --- /dev/null +++ b/docs/files/WL_MAIN.md @@ -0,0 +1,104 @@ +# `WL_MAIN.C` — entry, configuration, projection, signon + +The engine entry point and the engine-wide configuration layer. + +## Includes + +``, `WL_DEF.H` only. `#pragma hdrstop` per Borland convention. + +## Globals defined here + +| Symbol | Where | Type / Initial value | +|-------------------------|--------------|----------------------------------------------------------------------------| +| `str[80]`, `str2[20]` | `WL_MAIN.C:43` | Sprintf scratch buffers | +| `tedlevelnum` | `WL_MAIN.C:44`| int — TED level index, set by command-line | +| `tedlevel` | `WL_MAIN.C:45`| boolean — `tedlevel` flag set by `US_CheckParm("TEDLEVEL")` | +| `nospr` | `WL_MAIN.C:46`| boolean — disable sprites (debug) | +| `IsA386` | `WL_MAIN.C:47`| boolean — set true by `Patch386` if 386 detected | +| `dirangle[9]` | `WL_MAIN.C:48`| int[9] — diaganol angle LUT | +| `focallength` | `WL_MAIN.C:54`| fixed — current focal length | +| `screenofs` | `WL_MAIN.C:55`| unsigned — back-buffer row offset | +| `viewwidth` / `viewheight` | `WL_MAIN.C:56-57` | int — current view-window size | +| `centerx` | `WL_MAIN.C:58`| int — horizontal center of view | +| `shootdelta` | `WL_MAIN.C:59`| int — pixels away from centerx a target can be | +| `scale`, `maxslope` | `WL_MAIN.C:60`| fixed — projection scale / slope | +| `heightnumerator` | `WL_MAIN.C:61`| long — fixed-point numerator for wall heights | +| `minheightdiv` | `WL_MAIN.C:62`| int — minimum height divisor | +| `startgame`, `loadedgame`, `virtualreality` | `WL_MAIN.C:67` | boolean — boot flags | +| `mouseadjustment` | `WL_MAIN.C:68`| int — mouse sensitivity multiplier | +| `configname[13]` | `WL_MAIN.C:70`| `"CONFIG."`. Set per-variant by `FOREIGN.H` / version flags | +| `Scores[7]` | `ID_US_1.C:57`| HighScores table (declared `extern` in `ID_US.H:84`); read/written by `ReadConfig`/`WriteConfig` in `WL_MAIN.C` | + +## Public functions + +| Function | Where | Brief | +|---------------------------|--------------------|------------------------------------------------------------------------------------------------| +| `ReadConfig` | `WL_MAIN.C:90` | Read `CONFIG.` from disk into the global config state | +| `WriteConfig` | `WL_MAIN.C:193` | Write the global config state back to `CONFIG.` | +| `JHParmStrings[]` | `WL_MAIN.C:240` | Static `{"no386", nil}` parser string list | +| `Patch386` | `WL_MAIN.C:243` | Sniff the CPU; sets `IsA386` via the `JABHACK.ASM` glue | +| `NewGame` | `WL_MAIN.C:276` | Initialize `gamestate` for a new game at `(difficulty, episode)` | +| `DiskFlopAnim` | `WL_MAIN.C:293` | Play the floppy-disk animation when saving | +| `DoChecksum` | `WL_MAIN.C:304` | Compute the rolling 16-bit XOR checksum | +| `SaveTheGame` | `WL_MAIN.C:323` | Save full game state to disk; uses `CA_FarWrite` and `RLEWCompress` | +| `LoadTheGame` | `WL_MAIN.C:443` | Load full game state from disk; verifies checksum | +| `ShutdownId` | `WL_MAIN.C:557` | Tear down all engine subsystems (video, audio, paging, etc.) | +| `BuildTables` | `WL_MAIN.C:586` | Pre-compute `sintable[]`, `costable[]`, `finetangent[]`, `pixelangle[]`, `wallheight[]` | +| `CalcProjection` | `WL_MAIN.C:641` | Update the projection state for the current view size and focal length | +| `SetupWalls` | `WL_MAIN.C:706` | Compute per-wall-pixel column offsets for the scaling-strip drawer | +| `SignonScreen` | `WL_MAIN.C:727` | Draw the soft-open signon animation (VGA-only variant) | +| `FinishSignon` | `WL_MAIN.C:765` | Final fade-out at the end of the signon | +| `MS_CheckParm` | `WL_MAIN.C:819` | Check command-line argv against the engine-name list | +| `wolfdigimap[]` | `WL_MAIN.C:849` | Static — chmod mapping for digitised sound chunk numbers | +| `InitDigiMap` | `WL_MAIN.C:962` | Apply the `wolfdigimap` mapping to digitised sound chunks | +| `DoJukebox` | `WL_MAIN.C:1015` | Play a slide-show of digitised audio files | +| `InitGame` | `WL_MAIN.C:1145` | The first-time setup; runs everything before `DemoLoop` is allowed to enter | +| `SetViewSize` | `WL_MAIN.C:1278` | Update `viewwidth`, `viewheight`, `centerx`, `screenofs`, `ylookup` for a given view area | +| `ShowViewSize` | `WL_MAIN.C:1309` | Draw the "view size" border box used while resizing | +| `NewViewSize` | `WL_MAIN.C:1325` | Update `viewsize` from the current keypress (plus/minus) | +| `Quit` | `WL_MAIN.C:1346` | Hard-exit with an error string | +| `DemoLoop` | `WL_MAIN.C:1411` | Theoline to game loop — title page, credits, high scores, demo, control panel, game | +| `main` | `WL_MAIN.C:1586` | Entry point | +| `radtoint` | `WL_MAIN.C:584` | `const float = (float)FINEANGLES / (2 * PI)` — used by trig-table generation | + +## Static / local helpers + +| Function | Where | Brief | +|---------------------------|---------------------|--------------------------------------------------------------------| +| `wolfdigimap[]` | `WL_MAIN.C:849` | Static lookup of digitised sound chunk numbers across game variants | +| `nosprtxt[]` | `WL_MAIN.C:1584` | `{"nospr", nil}` — used only via `MS_CheckParm` | + +## Constants specific to this file + +* `FOCALLENGTH = 0x5700l` (`WL_MAIN.C:24`) — default focal length +* `VIEWGLOBAL = 0x10000` (`WL_MAIN.C:25`) — flush-to-wall global view distance +* `VIEWWIDTH = 256` (`WL_MAIN.C:27`) — design-time view window width +* `VIEWHEIGHT = 144` (`WL_MAIN.C:28`) — design-time view window height + +## Calls + +`BuildTables` is called from `InitGame`, `SetViewSize` from the menu / +view-size change handler, and `CalcProjection` from various places +that change the view size. `SaveTheGame` / `LoadTheGame` are called +from `WL_MENU.C::CP_SaveGame` / `CP_LoadGame`. + +## Where it is called from + +* `main` is called by the DOS loader. +* `Patch386`, `InitGame`, `DemoLoop` are called by `main`. +* `Quit` is called from many places. + +## Notes + +* The function `SetupWalls` is unique to this file but writes into + the `horizwall[]` and `vertwall[]` arrays exported by `WL_DEF.H` — + anything that draws walls reads these. +* `fadetime` style palette-fade animations can be triggered from here + via direct VGA-register access in `SignonScreen`. + +## Cross-references + +* See [`systems/rendering.md`](../systems/rendering.md) for how + `BuildTables`, `SetupWalls`, `CalcProjection` feed the renderer. +* See [`systems/audio.md`](../systems/audio.md) for how `InitDigiMap` + affects `SD_Startup`. diff --git a/docs/files/WL_MENU.md b/docs/files/WL_MENU.md new file mode 100644 index 0000000..139b3df --- /dev/null +++ b/docs/files/WL_MENU.md @@ -0,0 +1,56 @@ +# WL_MENU.C + +The front-end control panel and the largest `WL_*` source. Owns the entire menu state machine: main carousel, new-game/episode/difficulty pickers, load/save, sound and control-configuration menus, joystick calibration, custom keybindings, high scores, and the shared menu-rendering helpers. + +Part of the **ui-menu** subsystem — see [systems/ui-menu.md](../systems/ui-menu.md). + +## Key globals + +| Global | Purpose | +|--------|---------| +| `endStrings[9][80]` | Randomized "really quit?" quip strings (`WL_MENU.C:27`) | +| `color_hlite[]` | Highlight-color palette indices (`WL_MENU.C:273`) | +| `EpisodeSelect[6]` | Which episodes are selectable for this SKU (`WL_MENU.C:287`) | +| `SaveGamesAvail[10]`, `StartGame`, `SoundStatus`, `pickquick` | Slot-availability mask and front-end state (`WL_MENU.C:290`) | +| `SaveGameNames[10][32]`, `SaveName[13]` | Save-slot display names and `"SAVEGAM?."` template (`WL_MENU.C:291`) | +| `mbarray[4][3]` | Mouse-button label lookup (`WL_MENU.C:2046`) | +| `moveorder[4]` | Movement-key order lookup (`WL_MENU.C:2134`) | + +## Functions + +| Function | Line | Role | +|----------|------|------| +| `US_ControlPanel()` | `WL_MENU.C:332` | Top-level control-panel state machine / entry point | +| `DrawMainMenu()` | `WL_MENU.C:539` | Draw the main menu carousel | +| `CP_ReadThis()` | `WL_MENU.C:610` | Slideshow of the "Read This!" help pages | +| `CP_CheckQuick()` | `WL_MENU.C:650` | Quick-save/load/quit hotkey dispatcher | +| `CP_EndGame()` | `WL_MENU.C:864` | Confirm and abandon the current game | +| `CP_ViewScores()` | `WL_MENU.C:893` | High-scores screen | +| `CP_NewGame()` | `WL_MENU.C:926` | New-game episode/difficulty flow | +| `CP_Sound()` | `WL_MENU.C:1135` | Sound-options menu | +| `CP_LoadGame()` | `WL_MENU.C:1373` | Load-game slot picker | +| `PrintLSEntry()` | `WL_MENU.C:1516` | Render one load/save slot row | +| `CP_SaveGame()` | `WL_MENU.C:1538` | Save-game slot picker (with name entry) | +| `CalibrateJoystick()` | `WL_MENU.C:1663` | Interactive joystick calibration | +| `CP_Control()` | `WL_MENU.C:1761` | Control-configuration menu | +| `MouseSensitivity()` | `WL_MENU.C:1884` | Mouse-sensitivity slider menu | +| `CustomControls()` | `WL_MENU.C:2050` | Custom keybinding menu | +| `EnterCtrlData()` | `WL_MENU.C:2136` | Callback-driven keybinding capture loop | +| `FixupCustom()` | `WL_MENU.C:2369` | Commit custom keybindings into config | +| `IntroScreen()` | `WL_MENU.C:2882` | Draw the pre-game intro/PC-speaker screen | +| `SetupControlPanel()` | `WL_MENU.C:3029` | Allocate/cache assets when entering the panel | +| `CleanupControlPanel()` | `WL_MENU.C:3089` | Release panel assets on exit | +| `HandleMenu()` | `WL_MENU.C:3106` | Generic menu-driver (cursor, input, callbacks) | +| `DrawMenu()` | `WL_MENU.C:3423` | Render an item list from a `CP_itemtype` array | +| `ReadAnyControl()` | `WL_MENU.C:3489` | Poll keyboard/mouse/joystick into a `ControlInfo` | +| `Confirm()` | `WL_MENU.C:3592` | Yes/no confirmation prompt | +| `Message()` | `WL_MENU.C:3724` | Draw a centered popup message | +| `StartCPMusic()` | `WL_MENU.C:3766` | Start the menu music track | +| `CheckForEpisodes()` | `WL_MENU.C:3882` | Enable episodes based on the installed data files | + +## Notes + +- `HandleMenu` is the shared driver: nearly every `CP_*`/`Draw*` screen is a `CP_iteminfo`/`CP_itemtype` list handed to it with a per-item routine callback; the `CP_itemtype`/`CustomCtrls` shapes live in `WL_MENU.H`. +- Save/load state (`SaveGamesAvail`, `SaveGameNames`, `SaveName`, `pickquick`) is shared between `CP_LoadGame`, `CP_SaveGame`, and the quick-save path in `CP_CheckQuick`. +- Text entry for save-slot names uses the user-manager (`US_LineInput` in `ID_US_1.C`); menu text rendering also flows through the US print routines. +- `SetupControlPanel`/`CleanupControlPanel` bracket the panel, caching graphics through `ID_CA.C` and restoring the game view on exit; `CheckForEpisodes` gates `EpisodeSelect` by which map/data files are present. diff --git a/docs/files/WL_PLAY.md b/docs/files/WL_PLAY.md new file mode 100644 index 0000000..a71d3a3 --- /dev/null +++ b/docs/files/WL_PLAY.md @@ -0,0 +1,34 @@ +# `WL_PLAY.C` — declarations only (logic lives in `WL_DRAW.C`) + +This file contains **no** top-level function definitions. It exists as +a transverse header used by the renderer and gameplay stack to share +a few constants and the `PlayLoop` prototype. + +The actual `PlayLoop` is implemented in `WL_DRAW.C` (which is +counterintuitive at first glance). + +## Includes + +`WL_DEF.H`. `#pragma hdrstop`. + +## Constants local to this file + +| Constant | Where | Brief | +|----------------|----------------|--------------------------------------------------------------------------------| +| `sc_Question` | `WL_PLAY.C:18` | scancode for the `?` key | + +## Globals + +| Symbol | Where | Brief | +|-------------|--------|------------------------------------------------------------------------| +| `madenoise` | line 27 | boolean — true while a gunshot is being played | +| `playstate` | line 30 | `exit_t` — current state machine state of the gameplay loop | +| `DebugOk` | line 32 | int — debug-flags battle-table | + +## Functions + +None defined here. `PlayLoop` is defined in `WL_DRAW.C`. + +## See also + +* [`files/WL_DRAW.md`](WL_DRAW.md) diff --git a/docs/files/WL_SCALE.md b/docs/files/WL_SCALE.md new file mode 100644 index 0000000..2ff479d --- /dev/null +++ b/docs/files/WL_SCALE.md @@ -0,0 +1,34 @@ +# `WL_SCALE.C` — scaled-shape renderer + +Implements the sprite scaling that `DrawScaleds` uses. + +## Includes + +`WL_DEF.H`. `#pragma hdrstop`. + +## Globals + +| Symbol | Where | Brief | +|---------------------------------|----------|------------------------------------------------------------| +| `fullscalefarcall[]` | line 17 | long[MAXSCALEHEIGHT+1] — scaling-strip dispatch table | +| `maxscale`, `maxscaleshl2` | line 19 | int — sprite max height scaling | +| `insetupscaling` | line 21 | boolean | +| `stepbytwo` | line 34 | int — for the unrolled column loop | +| `longtemp` | line 419 | static long — temp used inside scaling routines | +| `slinex`, `slinewidth` | line 729 | int — current scanline x / pixel width | +| `linescale` | line 731 | long — scale factor | + +## Public functions + +| Function | Where | Brief | +|---------------------------|-----------|------------------------------------------------------------------| +| `SetupScaling` | line 60 | Build the scaling-strip lookup tables for a max sprite height | +| `BadScale` (far) | line 46 | Out-of-bounds handler | +| `ScaleLine` (near) | line 249 | Per-line scaled-strip renderer | +| `ScaleShape` | line 421 | Full shape-scale renderer (clipped) | +| `SimpleScaleShape` | line 625 | Unclipped shape-scale (used for player weapon) | + +## See also + +* [`systems/rendering.md`](../systems/rendering.md) +* [`files/WL_DRAW.md`](WL_DRAW.md) diff --git a/docs/files/WL_STATE.md b/docs/files/WL_STATE.md new file mode 100644 index 0000000..127af2e --- /dev/null +++ b/docs/files/WL_STATE.md @@ -0,0 +1,38 @@ +# WL_STATE.C + +Actor state-machine infrastructure shared by every enemy type: object allocation, state transitions, tile-based movement and pathfinding, damage/death, and line-of-sight/sighting logic. The per-enemy `T_*`/`A_*` handlers in `WL_ACT2.C` build on these primitives. + +Part of the **actordata** subsystem — see [systems/actordata.md](../systems/actordata.md). + +## Key globals + +| Global | Purpose | +|--------|---------| +| `opposite[9]` | Lookup of the reverse of each `dirtype` (`WL_STATE.C:24`) | +| `diagonal[9][9]` | Diagonal-direction lookup keyed by two cardinal dirs (`WL_STATE.C:27`) | + +## Functions + +| Function | Line | Role | +|----------|------|------| +| `SpawnNewObj()` | `WL_STATE.C:81` | Allocate an objtype from the pool at `(tilex,tiley)` and set its state | +| `NewState()` | `WL_STATE.C:113` | Set the actor's current state pointer and reset tic counter | +| `TryWalk()` | `WL_STATE.C:181` | Attempt to step the actor one tile in its current direction | +| `SelectDodgeDir()` | `WL_STATE.C:359` | Pick a sidestep direction toward the player on collision | +| `SelectChaseDir()` | `WL_STATE.C:475` | Choose a step direction that pathfinds toward the player | +| `SelectRunDir()` | `WL_STATE.C:585` | Choose a step direction fleeing away from the player | +| `MoveObj()` | `WL_STATE.C:659` | Advance the actor in fixed-point coords along `dir` | +| `DropItem()` | `WL_STATE.C:778` | Spawn a `statobj_t` pickup at a tile on actor death | +| `KillActor()` | `WL_STATE.C:815` | Handle death: score, drop item, decrement counts, enter death state | +| `DamageActor()` | `WL_STATE.C:964` | Apply damage; wake the actor or run its death state if `hp<=0` | +| `CheckLine()` | `WL_STATE.C:1037` | Bresenham line-of-sight test from actor to player | +| `CheckSight()` | `WL_STATE.C:1187` | Field-of-view + `CheckLine` sight test | +| `FirstSighting()` | `WL_STATE.C:1253` | Transition a stationary actor to its chase state on first contact | +| `SightPlayer()` | `WL_STATE.C:1404` | Common "did I see the player?" gate used by chase handlers | + +## Notes + +- `TryWalk`, `MoveObj`, and the `Select*Dir` routines operate on the tilemap and `actorat[][]`; they are the movement backbone for all `T_*` handlers in `WL_ACT2.C`. +- Sighting uses two stages: `CheckSight`/`CheckLine` compute visibility, then `FirstSighting`/`SightPlayer` drive the actual state transition to chase. +- `DamageActor` and `KillActor` mutate global kill/score counters read by the HUD and level-completion logic. +- `opposite`/`diagonal` are consumed heavily by the direction-selection routines here and mirror the `dirtype` enum in `WL_DEF.H`. diff --git a/docs/files/WL_TEXT.md b/docs/files/WL_TEXT.md new file mode 100644 index 0000000..4698bc8 --- /dev/null +++ b/docs/files/WL_TEXT.md @@ -0,0 +1,46 @@ +# WL_TEXT.C + +Drives the inter-mission and help "article" text screens: a small runtime macro language (`@` commands for pictures, timed images, layout) is parsed and laid out into scaled-font pages, then paged through by the player. Also handles the end-of-game text and help carousel. + +Part of the **ui-menu** subsystem — see [systems/ui-menu.md](../systems/ui-menu.md). + +## Key globals + +| Global | Purpose | +|--------|---------| +| `pagenum`, `numpages` | Current and total article page indices (`WL_TEXT.C:51`) | +| `leftmargin[]`, `rightmargin[]` | Per-row text margins for layout (`WL_TEXT.C:53`) | +| `text` | `char far *` current parse cursor into the article (`WL_TEXT.C:54`) | +| `rowon` | Current output row during layout (`WL_TEXT.C:55`) | +| `picx`, `picy`, `picnum`, `picdelay` | Per-page embedded-image parameters (`WL_TEXT.C:57`) | +| `layoutdone` | Flag set when a page finishes laying out (`WL_TEXT.C:58`) | +| `endextern`, `helpextern` | Article-lump sentinels (`WL_TEXT.C:730`, `732`) | +| `helpfilename[13]` | `"HELPART."` filename template (`WL_TEXT.C:735`) | + +## Functions + +| Function | Line | Role | +|----------|------|------| +| `RipToEOL()` | `WL_TEXT.C:71` | Advance the parse cursor past the rest of the line | +| `ParseNumber()` | `WL_TEXT.C:86` | Parse an integer argument from the macro stream | +| `ParsePicCommand()` | `WL_TEXT.C:125` | Parse a `@PICTURE` command (y,x,pic) | +| `ParseTimedCommand()` | `WL_TEXT.C:134` | Parse a `@TIMEDPICTURE` command (y,x,pic,delay) | +| `TimedPicCommand()` | `WL_TEXT.C:155` | Execute a timed picture command | +| `HandleCommand()` | `WL_TEXT.C:186` | Top-level `@`-macro dispatcher | +| `NewLine()` | `WL_TEXT.C:289` | Advance layout to the next text row | +| `HandleCtrls()` | `WL_TEXT.C:329` | Process control characters in the stream | +| `HandleWord()` | `WL_TEXT.C:352` | Measure and render one word, wrapping as needed | +| `PageLayout()` | `WL_TEXT.C:412` | Lay out a full page, optionally drawing the page number | +| `BackPage()` | `WL_TEXT.C:518` | Move back one page | +| `CacheLayoutGraphics()` | `WL_TEXT.C:543` | Preload the graphics referenced by a page | +| `ShowArticle(int)` | `WL_TEXT.C:599` | Display a cached article lump by index | +| `ShowArticle(char far *)` | `WL_TEXT.C:601` | Display an article from an in-memory string | +| `HelpScreens()` | `WL_TEXT.C:747` | Run the help-article carousel | +| `EndText()` | `WL_TEXT.C:800` | Run the episode end-of-game text | + +## Notes + +- `text` is a global parse cursor mutated by `ParseNumber`/`RipToEOL`/`HandleWord`; the parser is stateful and single-pass per page. +- There are two `ShowArticle` overloads (lump index vs. raw string) sharing the same layout core — the string form powers help screens loaded from `HELPART.`. +- Page images are described inline via `@PICTURE`/`@TIMEDPICTURE`; `CacheLayoutGraphics` walks a page ahead of drawing to preload those chunks through `ID_CA.C`. +- Text is rendered with the scaled font routines from the video/user-manager layer, so word wrapping depends on `leftmargin`/`rightmargin` computed during `PageLayout`. diff --git a/docs/reference/constants.md b/docs/reference/constants.md new file mode 100644 index 0000000..1a51639 --- /dev/null +++ b/docs/reference/constants.md @@ -0,0 +1,122 @@ +# Reference — Constants + +Constants, `enum`s, and assembler equates grouped by semantic family. Line +numbers are `WOLFSRC/:` at the tip of `main` (`778abe0`). This is a +lookup index, not an exhaustive listing of every `#define` in the tree; the +"where defined" column tells you the file to grep for the full set. + +## Fixed-point & projection (`WL_DEF.H`) + +| Symbol | Value | Line | Meaning | +|---------------|-----------------|-------------------|--------------------------------------| +| `fixed` | `typedef long` | `WL_DEF.H:480` | 16.16 signed fixed-point number | +| `PI` | `3.141592657` | `WL_DEF.H:95` | used only in trig-table generation | +| `GLOBAL1` | `(1l<<16)` | `WL_DEF.H:97` | 1.0 in fixed-point (`0x10000`) | +| `TILEGLOBAL` | `GLOBAL1` | `WL_DEF.H:98` | one tile == 1.0 | +| `TILESHIFT` | `16l` | `WL_DEF.H:100` | fixed → tile integer shift | +| `ANGLES` | `360` | `WL_DEF.H:103` | angle units per full turn | +| `FINEANGLES` | `3600` | `WL_DEF.H:105` | sub-angle LUT resolution | +| `MINDIST` | `0x5800l` | `WL_DEF.H:115` | near clip / player radius base | +| `RUNSPEED` | `6000` | `WL_DEF.H:75` | player run speed | + +## Screen & view geometry (`WL_DEF.H`) + +| Symbol | Value | Line | Meaning | +|-------------------|----------|------------------|--------------------------------| +| `SCREENSEG` | `0xa000` | `WL_DEF.H:77` | VGA framebuffer segment | +| `MAXVIEWWIDTH` | `320` | `WL_DEF.H:120` | widest 3-D view in pixels | +| `MAXSCALEHEIGHT` | `256` | `WL_DEF.H:118` | tallest scaled shape | +| `STATUSLINES` | `40` | `WL_DEF.H:129` | HUD height in scanlines | +| `MAPSIZE` | `64` | `WL_DEF.H:122` | map is 64 × 64 tiles | +| `PLAYERSIZE` | `MINDIST`| `WL_DEF.H:88` | player collision radius | +| `NUMLATCHPICS` | `100` | `WL_DEF.H:92` | preloaded VGA latch pictures | + +## Table sizes / limits (`WL_DEF.H`) + +| Symbol | Value | Line | Meaning | +|--------------|-------|----------------|----------------------------------| +| `MAXACTORS` | `150` | `WL_DEF.H:49` | live actors per map | +| `MAXSTATS` | `400` | `WL_DEF.H:50` | static objects per map | +| `MAXDOORS` | `64` | `WL_DEF.H:51` | sliding doors per map | + +## Map access macro (`WL_DEF.H`) + +| Macro | Line | Meaning | +|---------------------------|---------------|--------------------------------------------| +| `MAPSPOT(x,y,plane)` | `WL_DEF.H:35` | `*(mapsegs[plane]+farmapylookup[y]+x)` | + +## Scancodes (`ID_IN.H`) + +71 `sc_*` keyboard scancodes. Representative entries: + +| Symbol | Value | Line | +|----------------|--------|-----------------| +| `sc_Escape` | `0x01` | `ID_IN.H:25` | +| `sc_Return` | `0x1c` | `ID_IN.H:23` | +| `sc_Space` | `0x39` | `ID_IN.H:26` | +| `sc_Control` | `0x1d` | `ID_IN.H:30` | +| `sc_Alt` | `0x38` | `ID_IN.H:29` | +| `sc_UpArrow` | `0x48` | `ID_IN.H:34` | +| `sc_DownArrow` | `0x50` | `ID_IN.H:35` | + +Grep `ID_IN.H` for the full `#define sc_` set. + +## Actor / AI enums (`WL_DEF.H`) + +| Enum | Line | Meaning | +|--------------|-------------------|---------------------------------------------| +| `dir` set | `WL_DEF.H:482` | movement directions (N, NE, E, …) | +| `activetype` | `WL_DEF.H:498-503`| actor active state (`ac_badobject` …) | +| `stat_t` | `WL_DEF.H:505-560`| static-object kinds (lamps, treasure, ammo) | +| `enemy_t` | `WL_DEF.H:562-599`| enemy species | + +## Core structs (`WL_DEF.H`) + +| Struct | Line | Meaning | +|---------------|----------------|--------------------------------------------------| +| `statestruct` | `WL_DEF.H:602` | a state-machine node (sprite, tics, think, next) | +| `statstruct` | `WL_DEF.H:618` | a static object instance | +| `doorstruct` | `WL_DEF.H:634` | a door instance | +| `objstruct` | `WL_DEF.H:650` | a live actor (`objtype`) | +| `gametype` | `WL_DEF.H:715` | the `gamestate` block (see below) | + +## `gamestate` fields (`WL_DEF.H` `gametype`, from ~`:715`) + +| Field | Line | Meaning | +|-------------------------------------------|-------------------|----------------------------| +| `difficulty` | `WL_DEF.H:717` | skill level | +| `mapon` | `WL_DEF.H:718` | current map index | +| `lives`, `health`, `ammo` | `WL_DEF.H:720-722`| player HUD state | +| `attackframe`, `attackcount`, `weaponframe`| `WL_DEF.H:727` | weapon animation state | +| `episode`, `secretcount`, `treasurecount`, `killcount` | `WL_DEF.H:729` | per-level stats | + +## Audio enums (`ID_SD.H`) + +| Symbol | Meaning | +|-------------|--------------------------------------------------| +| `SDMode` | effects device: off / PC speaker / AdLib / SB | +| `SMMode` | music device: off / AdLib | +| `SDSMode` | digitized-sound device selection | +| `soundnames`| symbolic sound-effect ids (per-product tables) | + +Per-product sound-name tables: `AUDIOWL1.H`, `AUDIOWL6.H`, `AUDIOSOD.H`, +`AUDIOSDM.H` (and `BUDIO*.H`). + +## Video register equates (`ID_VL.H`, `ID_VL.EQU`) + +VGA CRTC / sequencer / graphics-controller register addresses used by the +Mode-X driver. Grep `ID_VL.H` and `ID_VL.EQU` for the `ID_*` register names. + +## Graphics / map chunk tables + +Per-product chunk-index name tables live in `GFXV_WL1.H`/`GFXE_WL1.H`, +`GFXV_WL6.H`/`GFXE_WL6.H`, `GFXV_SOD.H`/`GFXE_SOD.H`, `GFXV_SDM.H`, and the map +tables `MAPSWL1.H`, `MAPSWL6.H`, `MAPSSOD.H`, `MAPSSDM.H`, `MAPSWLF.H`. The +matching `.EQU` files expose the same ids to the assembler. + +## Product-selection flags + +Compile-time build selectors: `WOLF1VER.H`, `WOLFVER.H`, `SODVER.H`, +`SDMVER.H`, `WOLFJVER.H`, `WLFJ1VER.H`, `SPANVER.H`, `WOLFGTV.H`, plus +`FOREIGN.H` (foreign-build master switch) and `F_SPEAR.H` (`SPEAR` feature +gates). See [ARCHITECTURE §1](../ARCHITECTURE.md). diff --git a/docs/reference/functions.md b/docs/reference/functions.md new file mode 100644 index 0000000..e26ea74 --- /dev/null +++ b/docs/reference/functions.md @@ -0,0 +1,209 @@ +# Reference — Functions + +Key functions grouped by file, with line and role. Line numbers are +`WOLFSRC/:` at the tip of `main` (`778abe0`). This index lists the +architecturally important entry points; per-file pages under +[`files/`](../files/) enumerate each file's functions more fully. + +## `WL_MAIN.C` — startup & front end + +| Function | Line | Role | +|----------|------|------| +| `ReadConfig` / `WriteConfig` | `:90` / `:193` | load/save the config file | +| `NewGame` | `:276` | initialise a fresh `gamestate` | +| `SaveTheGame` / `LoadTheGame` | `:323` / `:443` | save-slot serialize/restore | +| `DoChecksum` | `:304` | save-file integrity check | +| `BuildTables` | `:586` | precompute sine/cosine/tangent LUTs | +| `CalcProjection` | `:641` | build per-column projection table | +| `SetupWalls` | `:706` | wall-texture lookup setup | +| `SignonScreen` / `FinishSignon` | `:727` / `:765` | boot splash | +| `InitGame` | `:1145` | full engine init | +| `DemoLoop` | `:1411` | attract-mode carousel | + +## `WL_GAME.C` — game & level + +| Function | Line | Role | +|----------|------|------| +| `PlaySoundLocGlobal` | `:170` | positional sound trigger | +| `ScanInfoPlane` | `:221` | read the map's info plane to spawn actors | +| `SetupGameLevel` | `:625` | build map, actors, doors, areas | +| `DrawPlayScreen` | `:868` | paint HUD frame | +| `StartDemoRecord` | `:914` | begin recording a demo | +| `GameLoop` | `:1238` | per-game driver loop | + +## `WL_PLAY.C` — per-tick play loop + +| Function | Role | +|----------|------| +| `PlayLoop` | per-tick driver: poll → move → tick actors → refresh | +| `PollControls` | read Input Manager, form player intents | +| `FinishPaletteShifts` | apply damage/pickup screen flashes | + +(K&R-style definitions — grep `WL_PLAY.C` for exact lines.) + +## `WL_DRAW.C` — raycaster + +| Function | Line | Role | +|----------|------|------| +| `TransformActor` | `:210` | rotate an actor into view space | +| `HitVertWall` | `:477` | resolve a vertical grid-crossing wall hit | +| `HitHorizWall` | `:550` | resolve a horizontal grid-crossing wall hit | +| `DrawScaleds` | `:1054` | draw depth-sorted sprites | +| `ThreeDRefresh` | `:72` (proto) | render one frame | + +## `WL_SCALE.C` — compiled scalers + +| Function | Line | Role | +|----------|------|------| +| `SetupScaling` | `:60` | allocate scaler table | +| `BuildCompScale` | `:153` | compile one height's scaler | +| `ScaleLine` | `:249` | per-column scale primitive | +| `ScaleShape` | `:421` | draw a masked shape | +| `SimpleScaleShape` | `:625` | draw an unmasked shape | + +## `WL_AGENT.C` — player actor & weapon + +| Function | Line | Role | +|----------|------|------| +| `CheckWeaponChange` | `:117` | switch active weapon | +| `ControlMovement` | `:149` | apply movement to the player object | +| `StatusDrawPic` | `:244` | blit a HUD picture | +| `T_Player` / `T_Attack` | `:54` / `:55` (proto) | player think / attack tickers | + +## `WL_INTER.C` — intermissions + +| Function | Line | Role | +|----------|------|------| +| `Victory` | `:108` | victory screen | +| `LevelCompleted` | `:429` | bonus tally between levels | +| `DrawHighScores` / `CheckHighScore` | `:1030` / `:1195` | high-score table | +| `PreloadGraphics` | `:995` | preload level graphics | + +## `WL_STATE.C` — actor state engine + +| Function | Line | Role | +|----------|------|------| +| `SpawnNewObj` | `:81` | create an actor | +| `NewState` | `:113` | transition actor state | +| `TryWalk` | `:181` | attempt a tile move | +| `SelectChaseDir` / `SelectDodgeDir` / `SelectRunDir` | `:475` / `:359` / `:585` | pick a direction | +| `MoveObj` | `:659` | advance along direction | +| `DamageActor` / `KillActor` | `:964` / `:815` | damage & death | +| `CheckSight` / `SightPlayer` | `:1187` / `:1404` | line-of-sight | + +## `WL_ACT1.C` — doors, statics, areas + +| Function | Line | Role | +|----------|------|------| +| `SpawnStatic` / `PlaceItemType` | `:139` / `:199` | static objects | +| `SpawnDoor` | `:350` | register a door | +| `OpenDoor` / `CloseDoor` / `OperateDoor` | `:400` / `:417` / `:498` | door control | +| `ConnectAreas` / `RecursiveConnect` | `:308` / `:293` | area sound flood | + +## `WL_ACT2.C` — enemies & projectiles + +| Function | Line | Role | +|----------|------|------| +| `SpawnStand` / `SpawnPatrol` | `:847` / `:983` | basic enemy spawn | +| `SpawnBoss` / `SpawnGretel` / `SpawnTrans` / `SpawnUber` / `SpawnWill` | `:936` / `:959` / `:1246` / `:1327` / `:1450` | boss spawns | +| `T_Projectile` / `ProjectileTryMove` | `:302` / `:266` | projectile motion | +| `A_DeathScream` | `:1061` | death-sound action | + +## `WL_MENU.C` — control panel + +| Function | Line | Role | +|----------|------|------| +| `US_ControlPanel` | `:332` | menu entry point | +| `DrawMainMenu` | `:539` | main menu | +| `CP_NewGame` | `:926` | start new game | +| `CP_Sound` | `:1135` | sound options | +| `CP_ViewScores` / `CP_EndGame` | `:893` / `:864` | scores / quit | +| `BossKey` | `:627` | panic hide screen | + +## `WL_TEXT.C` — text pager + +| Function | Line | Role | +|----------|------|------| +| `HandleCommand` | `:186` | parse layout command | +| `PageLayout` / `BackPage` | `:412` / `:518` | lay out / step pages | +| `ShowArticle` | `:599` | display an article | + +## `ID_CA.C` — asset cache + +| Function | Line | Role | +|----------|------|------| +| `CA_LoadFile` | `:347` | read a file into memory | +| `CAL_HuffExpand` | `:418` | Huffman decompression | +| `CAL_CarmackExpand` | `:609` | Carmack RLE decompression | + +## `ID_VL.C` — VGA driver + +| Function | Line | Role | +|----------|------|------| +| `VL_SetVGAPlaneMode` | `:115` | enter Mode-X | +| `VL_SetLineWidth` / `VL_SetSplitScreen` | `:246` / `:277` | display layout | +| `VL_SetPalette` / `VL_SetColor` | `:371` / `:332` | palette | + +## `ID_VH.C` — view layer + +| Function | Line | Role | +|----------|------|------| +| `VW_DrawPropString` | `:40` | proportional-font text | +| `VWB_DrawPic` / `VWB_DrawTile8` | `:304` / `:291` | pic/tile blit | +| `VWB_Bar` / `VWB_Plot` / `VWB_Hlin` / `VWB_Vlin` | `:329`–`:347` | primitives | +| `VL_MungePic` | `:170` | linear → planar rearrange | + +## `ID_IN.C` — input + +| Function | Line | Role | +|----------|------|------| +| `INL_GetJoyDelta` | `:324` | read joystick axes | +| `IN_StartAck` / `IN_CheckAck` / `IN_Ack` | `:871` / `:891` / `:918` | acknowledge loop | +| `IN_UserInput` | `:935` | wait-for-input with timeout | +| `IN_MouseButtons` / `IN_JoyButtons` | `:959` / `:979` | device buttons | + +## `ID_SD.C` — sound + +| Function | Line | Role | +|----------|------|------| +| `SD_SetDigiDevice` | `:1193` | select DAC path | +| `SD_SetSoundMode` / `SD_SetMusicMode` | `:1774` / `:1830` | choose devices | +| `SD_Startup` | `:1868` | probe hardware, install ISR | +| `SD_PlaySound` | `:2123` | play an effect | +| `SD_MusicOn` / `SD_StartMusic` | `:2275` / `:2309` | music | + +## `ID_PM.C` — page manager + +| Function | Line | Role | +|----------|------|------| +| `PML_MapEMS` | `:58` | map logical→physical EMS page | +| `PML_StartupEMS` / `PML_ShutdownEMS` | `:82` / `:172` | EMS lifecycle | +| `PML_StartupXMS` / `PML_XMSCopy` | `:197` / `:244` | XMS backing store | + +## `ID_MM.C` — memory manager + +| Function | Line | Role | +|----------|------|------| +| `MM_Startup` / `MM_Shutdown` | `:333` / `:413` | lifecycle | +| `MM_GetPtr` / `MM_FreePtr` | `:435` / `:559` | allocate / free block | +| `MM_SetPurge` / `MM_SetLock` | `:594` / `:630` | purgeable / locked flags | +| `MM_SortMem` | `:666` | compact the heap | +| `MM_ShowMemory` / `MM_DumpData` | `:772` / `:822` | diagnostics | + +## `ID_US_1.C` — user manager + +| Function | Line | Role | +|----------|------|------| +| `US_Startup` / `US_Shutdown` | `:169` / `:221` | lifecycle | +| `US_CheckParm` | `:237` | command-line parse | +| `US_SetPrintRoutines` | `:275` | plug in measure/print | +| `US_Print` / `US_CPrint` | `:288` / — | window text output | + +## `WL_DEBUG.C` — debug overlay + +| Function | Line | Role | +|----------|------|------| +| `DebugKeys` | `:415` | debug-key dispatcher | +| `ViewMap` / `OverheadRefresh` | `:669` / `:611` | overhead map view | +| `CountObjects` / `DebugMemory` | `:86` / `:54` | diagnostics | +| `PicturePause` / `ShapeTest` | `:137` / `:217` | asset viewers | diff --git a/docs/systems/actordata.md b/docs/systems/actordata.md new file mode 100644 index 0000000..b42c238 --- /dev/null +++ b/docs/systems/actordata.md @@ -0,0 +1,83 @@ +# Actor / AI subsystem + +Everything that moves, shoots, opens, or can be killed is an **actor**. Actors +are driven by data-defined **state machines**: each state names a sprite, a +duration, an optional per-tick "think" function, and the next state. This +subsystem owns the state engine, the enemy AI, doors and pushwalls, and +projectiles. + +Owning files: + +| File | Role | +|-------------|------------------------------------------------------------------| +| `WL_STATE.C`| State engine, movement, line-of-sight, damage, death | +| `WL_ACT1.C` | Static objects, doors, pushwalls, area connectivity | +| `WL_ACT2.C` | Enemy spawners and per-enemy think/attack functions, projectiles | +| `WL_AGENT.C`| The player actor (movement, weapons) — see [rendering](rendering.md) | +| `WL_DEF.H` | `objtype`, `statetype`, `activetype`, `enemy_t`, `stat_t` | + +## The actor and state structs (`WL_DEF.H`) + +* `objtype` — one live actor: tile and fixed position, angle, health, current + `statetype*`, flags, and links in the active-object list. There are at most + `MAXACTORS` (150) of these per level. +* `statetype` — one node in a state machine: sprite/rotate flags, tic count, + `think` and `action` function pointers, and the successor state. +* `stat_t` — the kind of a static object (`MAXSTATS` = 400 per level). + +## The state engine (`WL_STATE.C`) + +`SpawnNewObj()` (`WL_STATE.C:81`) creates an actor in a start state; +`NewState()` (`:113`) transitions an actor to a new state and resets its tic +counter. Movement and pathing: + +* `TryWalk()` (`:181`) — attempt a one-tile move, respecting doors and blocks. +* `SelectChaseDir()` (`:475`) / `SelectDodgeDir()` (`:359`) / + `SelectRunDir()` (`:585`) — pick a direction toward, around, or away from + the player. +* `MoveObj()` (`:659`) — advance an actor along its current direction by a + fixed-point amount. + +Combat and perception: + +* `CheckLine()` (`:1037`) / `CheckSight()` (`:1187`) / `SightPlayer()` (`:1404`) + — line-of-sight tests using the same tile-stepping idea as the raycaster. +* `FirstSighting()` (`:1253`) — wake an enemy when it first sees the player. +* `DamageActor()` (`:964`) — apply damage; `KillActor()` (`:815`) — run the + death transition and drop items via `DropItem()` (`:778`). + +## Doors, pushwalls, areas (`WL_ACT1.C`) + +`WL_ACT1.C` manages the static world. Doors are a fixed set of directional +variants: `SpawnDoor()` (`:350`) registers one, and the door state functions +`OpenDoor`/`CloseDoor`/`OperateDoor` (`:400`/`:417`/`:498`) plus +`DoorOpening`/`DoorClosing` (`:554`/`:617`) animate it. **Areas** — the +flood-fill sound-propagation regions — are connected through doors: +`ConnectAreas` (`:308`), `RecursiveConnect` (`:293`), `InitAreas` (`:316`), +so opening a door lets enemy-alert sound travel between rooms. Static items +are placed by `SpawnStatic` (`:139`) / `PlaceItemType` (`:199`). + +## Enemies and projectiles (`WL_ACT2.C`) + +`WL_ACT2.C` holds the enemy roster. Spawners such as `SpawnStand` (`:847`), +`SpawnPatrol` (`:983`), `SpawnBoss` (`:936`), `SpawnGretel` (`:959`), +`SpawnTrans` (`:1246`), `SpawnUber` (`:1327`), and `SpawnWill` (`:1450`) +create each enemy type in its initial state. Per-enemy think/attack functions +(e.g. `T_UShoot` `:1351`, `T_Will` `:1450`) run from the state table each tick. +Projectiles are actors too: `T_Projectile()` (`:302`) advances a fireball with +`ProjectileTryMove()` (`:266`), and effects like `A_Smoke` (`:233`) and +`A_DeathScream` (`:1061`) are action callbacks fired from specific states. + +## Tick flow + +``` +PlayLoop (WL_PLAY.C) + └─ for each active objtype: + state->think(ob) // AI decision (WL_STATE / WL_ACT2) + advance ticcount → NewState when it expires + state->action(ob) // one-shot effect (sound, damage, spawn) +``` + +See also: [`files/WL_STATE.md`](../files/WL_STATE.md), +[`files/WL_ACT1.md`](../files/WL_ACT1.md), [`files/WL_ACT2.md`](../files/WL_ACT2.md), +[`files/WL_AGENT.md`](../files/WL_AGENT.md). diff --git a/docs/systems/audio.md b/docs/systems/audio.md new file mode 100644 index 0000000..97b768d --- /dev/null +++ b/docs/systems/audio.md @@ -0,0 +1,59 @@ +# Audio subsystem + +The Sound Manager drives three independent output paths and mixes them under +interrupt: **AdLib** (Yamaha OPL2 FM) for music and FM sound effects, the +**Sound Blaster** DAC for digitized effects, and the **PC speaker** for +bleeper effects on machines with no sound card. + +Owning files: + +| File | Role | +|---------------|-------------------------------------------------------------| +| `ID_SD.C` | Sound Manager: device setup, playback, music sequencer | +| `ID_SD_A.ASM` | Time-critical asm: PC-speaker toggling, timer/IRQ handlers | +| `ID_SD.H` | `SDMode` / `SMMode` enums, `soundnames`, public API | +| `ID_SD.EQU` | Assembler equates for the sound asm | +| `AUDIO*.H` / `BUDIO*.H` | Per-product sound-chunk name tables | + +## Device model + +Sound and music are configured independently: + +* Effects device — `SD_SetSoundMode(SDMode)` (`ID_SD.C:1774`): off, PC + speaker, AdLib, or Sound Blaster. +* Music device — `SD_SetMusicMode(SMMode)` (`ID_SD.C:1830`): off or AdLib. +* Digitized device — `SD_SetDigiDevice(SDSMode)` (`ID_SD.C:1193`) selects the + DAC path for sampled sounds. + +`SD_Startup()` (`ID_SD.C:1868`) probes hardware — including parsing the +`BLASTER` environment variable for the Sound Blaster's port, IRQ, and DMA +channel (`ID_SD.C:1957-1979`) — and installs the timer interrupt. + +## Playing sounds and music + +* `SD_PlaySound(soundnames)` (`ID_SD.C:2123`) starts an effect. Sounds carry a + **priority**; a higher-priority sound preempts a lower one on the same + channel. The sound chunk must already be cached (it `Quit`s on an uncached + sound, `ID_SD.C:2142`). +* `SD_StartMusic(MusicGroup far *)` (`ID_SD.C:2309`) begins an IMF/AdLib music + track; `SD_MusicOn()` (`ID_SD.C:2275`) enables the sequencer. + +## Interrupt-driven mixing + +The Sound Manager installs a timer ISR that fires at the game tick rate. On +each fire it advances the AdLib music sequencer, steps the current FM/PC-speaker +effect envelope, and feeds the next byte of any playing digitized sample. The +PC-speaker toggling and the tight IRQ entry/exit paths are hand-written in +`ID_SD_A.ASM` because they must run with tightly bounded latency. This ISR is +the audio half of the engine's only concurrency — everything else is the +single-threaded main loop. + +## How audio data reaches the mixer + +Sound and music chunks are ordinary cached assets: `ID_CA.C` loads and +decompresses them from the AUDIO file into memory, and the per-product name +tables (`AUDIOWL6.H`, `AUDIOSOD.H`, …) map symbolic `soundnames` to chunk +indices. See [file-cache](file-cache.md) for how the chunks are fetched. + +See also: [`files/ID_SD.md`](../files/ID_SD.md), +[audio/sound constants](../reference/constants.md). diff --git a/docs/systems/file-cache.md b/docs/systems/file-cache.md new file mode 100644 index 0000000..3b87c10 --- /dev/null +++ b/docs/systems/file-cache.md @@ -0,0 +1,59 @@ +# File and asset cache subsystem + +All game assets — walls and sprites (VSWAP), UI graphics (VGAGRAPH), maps +(GAMEMAPS), and audio (AUDIOT/AUDIO) — are stored in compressed archive files +and pulled into memory on demand as numbered **chunks**. `ID_CA.C` is the +Content/Asset manager that owns this. + +Owning files: + +| File | Role | +|-------------|-----------------------------------------------------------------| +| `ID_CA.C` | Chunk cache: file open, chunk load, decompression | +| `ID_CA.H` | Chunk-count and cache-type constants, public API | +| `MUNGE.C` | Offline data-munging helper | +| `GFX*.H` / `MAPS*.H` / `AUDIO*.H` | Per-product chunk-name/offset tables | + +## Archive layout + +Each archive has a **header file** of chunk offsets/sizes plus a **data file** +of the chunks themselves (e.g. `VGAHEAD` + `VGAGRAPH`, `MAPHEAD` + `GAMEMAPS`, +`AUDIOHED` + `AUDIOT`). At startup `ID_CA.C` reads the header tables so any +chunk can be located by index. The per-product `GFX*_*.H`, `MAPS*.H`, and +`AUDIO*.H` headers give symbolic names to those indices. + +## Loading and caching + +`CA_LoadFile()` (`ID_CA.C:347`) reads a whole file into a memory block. +Higher-level entry points cache an individual chunk (graphics, map, or audio), +decompress it, and register the resulting block with the Memory Manager so it +can be purged when idle and reloaded later. Because assets are referenced by +chunk index, gameplay code never touches raw file offsets. + +## Decompression + +Two decompressors run over the raw chunk data, sometimes stacked: + +* **Carmack expansion** — `CAL_CarmackExpand()` (`ID_CA.C:609`). A run-length + scheme with two escape tags (near-pointer and far-pointer copies) that + back-reference earlier decoded words. Used on map planes. +* **Huffman expansion** — `CAL_HuffExpand()` (`ID_CA.C:418`). A canonical + Huffman decoder driven by a 256-node tree table loaded from the archive + (`grhuffman`, `audiohuffman`). Used for graphics (`ID_CA.C:927`) and audio + (`ID_CA.C:1188`). + +Map data is typically **Carmack-then-Huffman** compressed and is expanded in +that order on load. + +## Where the cached chunks go + +* Graphics chunks feed the [rendering](rendering.md) and [ui-menu](ui-menu.md) + layers. +* Audio chunks feed the [audio](audio.md) Sound Manager. +* Map chunks feed [level setup](game-logic.md) and the actor spawners. + +Large asset sets that exceed conventional memory are paged through the Page +Manager (`ID_PM.C`); see [game-logic](game-logic.md). + +See also: [`files/ID_CA.md`](../files/ID_CA.md), +[cache constants](../reference/constants.md). diff --git a/docs/systems/game-logic.md b/docs/systems/game-logic.md new file mode 100644 index 0000000..e900fcb --- /dev/null +++ b/docs/systems/game-logic.md @@ -0,0 +1,77 @@ +# Game-logic subsystem + +This subsystem is the spine of the program: process startup, the front-end +carousel, per-level setup, the per-tick play loop, save/load and +intermissions, plus the two memory subsystems everything else allocates +through. The debug overlay lives here too. + +Owning files: + +| File | Role | +|--------------|-------------------------------------------------------------| +| `WL_MAIN.C` | `main`, `InitGame`, `DemoLoop`, `BuildTables`, boot flags | +| `WL_GAME.C` | `GameLoop`, `SetupGameLevel`, `DrawPlayScreen`, save/load | +| `WL_PLAY.C` | `PlayLoop`, control polling, per-tick actor dispatch | +| `WL_INTER.C` | Level-complete intermission, high scores, victory/death | +| `ID_MM.C` | Memory Manager: near/far heap, purgeable/lockable blocks | +| `ID_PM.C` | Page Manager: EMS/XMS-backed paging of VSWAP pages | +| `WL_DEF.H` | Shared types, constants, `gamestate`, actor/state structs | +| `WL_DEBUG.C` | Debug keys and diagnostic overlays | + +## Boot and the front end (`WL_MAIN.C`) + +`main()` calls `InitGame()` (`WL_MAIN.C:1145`), which brings up memory, video, +audio, and input; builds the trig tables with `BuildTables()` +(`WL_MAIN.C:586`, invoked at `:1233`); loads graphics; and shows the signon +screen. Control then enters `DemoLoop()` (`WL_MAIN.C:1411`, called at `:1612`) +— the attract-mode carousel of title, credits, and recorded demos, which exits +into a game when the player starts one. Boot flags such as `IsA386` +(`WL_MAIN.C:47`), `tedlevel` (jump straight into the TED level editor), +and `nospr` (suppress sprites for debugging) are read here. + +## The game and play loops + +`GameLoop()` (`WL_GAME.C:1238`) is the per-game driver: it calls +`SetupGameLevel()` (`WL_GAME.C:625`) to populate the map, actors, doors, and +areas; `DrawPlayScreen()` (`WL_GAME.C:868`) to paint the HUD frame; then loops +on `PlayLoop()` (`WL_PLAY.C`). Each `PlayLoop` iteration is one tick: + +1. Poll controls (Input Manager) and apply player movement. +2. Advance every live actor's state machine (see [actordata](actordata.md)). +3. Call `ThreeDRefresh()` to render (see [rendering](rendering.md)). + +When the player finishes or dies, `WL_INTER.C` runs the intermission — +bonus tally, high-score table, and the between-level or victory screens. + +## Memory Manager (`ID_MM.C`) + +`ID_MM.C` is a hand-rolled heap over DOS conventional memory, optionally +extended with XMS (`MML_CheckForXMS` `:129`, `MML_SetupXMS` `:156`). It hands +out relocatable blocks through `MM_GetPtr()` (`ID_MM.C:435`) / +`MM_FreePtr()` (`:559`). Blocks can be marked **purgeable** +(`MM_SetPurge` `:594`) so the manager may reclaim them under pressure, and +**locked** (`MM_SetLock` `:630`) to pin them. `MM_SortMem()` (`:666`) +compacts the heap by relocating unlocked blocks; `MM_ShowMemory` (`:772`) and +`MM_DumpData` (`:822`) are diagnostics. + +## Page Manager (`ID_PM.C`) + +`ID_PM.C` pages the VSWAP graphics/sound pages through **EMS** or **XMS** +expanded memory, since the full asset set does not fit in the 640 KB +conventional region. `PML_MapEMS` (`ID_PM.C:58`) maps a logical page to a +physical EMS frame; `PML_StartupEMS` (`:82`) / `PML_StartupXMS` (`:197`) +initialize the backing store; `PML_XMSCopy` (`:244`) moves pages to and from +XMS. Callers request a page and get back a pointer valid until the next paging +operation. Carmack's retrospective (`README.rst`) calls this scheme +"unnecessarily complex." + +## Debug overlay (`WL_DEBUG.C`) + +`WL_DEBUG.C` implements the debug-key handler (map reveal, god mode, item +give, clipping toggle, etc.) and the on-screen diagnostic readouts. It is +compiled in when the debug build flag is set. + +See also: [`files/WL_MAIN.md`](../files/WL_MAIN.md), +[`files/WL_GAME.md`](../files/WL_GAME.md), [`files/WL_PLAY.md`](../files/WL_PLAY.md), +[`files/WL_INTER.md`](../files/WL_INTER.md), [`files/ID_MM.md`](../files/ID_MM.md), +[`files/ID_PM.md`](../files/ID_PM.md), [`files/WL_DEBUG.md`](../files/WL_DEBUG.md). diff --git a/docs/systems/input.md b/docs/systems/input.md new file mode 100644 index 0000000..678d018 --- /dev/null +++ b/docs/systems/input.md @@ -0,0 +1,52 @@ +# Input subsystem + +The Input Manager abstracts three devices — keyboard, mouse, joystick — behind +a common polling interface. The keyboard is serviced by the engine's own +interrupt handler; mouse and joystick are polled on demand. + +Owning files: + +| File | Role | +|-------------|---------------------------------------------------------------| +| `ID_IN.C` | Input Manager: keyboard ISR, mouse/joystick polling, ack loop | +| `ID_IN.H` | `ScanCode` (`sc_*`) constants, `ControlInfo`, public API | + +## Keyboard + +`ID_IN.C` installs its own keyboard ISR in place of the BIOS handler. On each +key event the ISR records the make/break in a scancode table, so the main loop +can ask "is this key down right now?" rather than draining the BIOS type-ahead +buffer. The `sc_*` scancode constants (Escape, arrows, letters, etc.) live in +`ID_IN.H` and are the vocabulary the rest of the engine uses for key bindings. + +## Mouse and joystick + +* Mouse buttons via `IN_MouseButtons()` (`ID_IN.C:959`); motion is read from + the mouse driver as a delta each poll. +* Joystick buttons via `IN_JoyButtons()` (`ID_IN.C:979`); axis motion via + `INL_GetJoyDelta()` (`ID_IN.C:324`), which reads the raw analog counts and + scales them against the calibrated min/max range. + +## The control abstraction + +The manager collapses whatever the active device reports into a single +direction + button set, so gameplay code (`WL_PLAY.C`) does not care which +device is bound. The acknowledgement helpers drive "press any key" prompts: + +* `IN_StartAck()` (`ID_IN.C:871`) begins waiting. +* `IN_CheckAck()` (`ID_IN.C:891`) polls without blocking. +* `IN_Ack()` (`ID_IN.C:918`) blocks until any input. +* `IN_UserInput(delay)` (`ID_IN.C:935`) waits up to a timeout, returning + whether the user acted — used by attract-mode screens to bail into the game + when the player presses something. + +## Relationship to the game loop + +`WL_PLAY.C::PollControls` (per tick) asks the Input Manager for the current +control state, turns it into player movement/turn/fire intents, and applies +them before the world is ticked. The keyboard ISR is the input half of the +engine's interrupt-driven concurrency; the mouse and joystick add no +interrupts and are simply sampled inside the poll. + +See also: [`files/ID_IN.md`](../files/ID_IN.md), +[scancode constants](../reference/constants.md). diff --git a/docs/systems/rendering.md b/docs/systems/rendering.md new file mode 100644 index 0000000..3a5d818 --- /dev/null +++ b/docs/systems/rendering.md @@ -0,0 +1,95 @@ +# Rendering subsystem + +The renderer turns the 64 × 64 tile map plus the actor list into a framebuffer +each tick. It has three layers: the **raycaster** (walls), the **scaled-shape +renderer** (sprites and the player weapon), and the **video drivers** that put +pixels on the VGA hardware. + +Owning files: + +| Files | Role | +|-----------------------------------------|---------------------------------------| +| `WL_DRAW.C`, `WL_DR_A.ASM` | Raycaster and per-column wall drawing | +| `WL_SCALE.C`, `OLDSCALE.C`, `CONTIGSC.C`| Compiled scalers for shapes | +| `WL_AGENT.C` | Player weapon draw, HUD faces | +| `ID_VL.C`, `ID_VL_A.ASM`, `ID_VL.EQU` | VGA Mode-X low-level driver | +| `ID_VH.C`, `ID_VH_A.ASM` | High-level view / masked-pic / font | + +## The raycaster (`WL_DRAW.C`) + +`ThreeDRefresh()` (`WL_DRAW.C:72`, the entry point called once per tick from +`PlayLoop`) is the top of the renderer. Per frame it: + +1. Latches the camera: `viewx`, `viewy`, `viewangle`, `viewsin`, `viewcos`. +2. Casts one ray per screen column across the field of view. The cast is a + **DDA** that steps through tile boundaries, testing horizontal and vertical + intercepts. A vertical grid crossing lands in `HitVertWall` + (`WL_DRAW.C:477`); a horizontal one in `HitHorizWall` (`WL_DRAW.C:550`). + Each computes the exact texture column and the projected wall height. +3. Because every wall is exactly one tile thick and axis-aligned, the cast + terminates the moment it enters a solid tile — this is the tile-aligned + simplification that gives Wolf3D its right-angle-only geometry. +4. `TransformActor()` (`WL_DRAW.C:210`) rotates each visible actor into view + space so it can be depth-sorted and scaled. +5. `DrawScaleds()` (`WL_DRAW.C:1054`) walks the transformed, depth-sorted list + and draws each sprite through the scaler. + +The heavy per-pixel wall inner loops live in `WL_DR_A.ASM` for speed. + +## The compiled scalers (`WL_SCALE.C`) + +Sprites are drawn by **run-time-compiled scaler routines**: for each possible +on-screen height the engine emits a straight-line unrolled copy loop instead +of running a general scaling loop. `SetupScaling()` (`WL_SCALE.C:60`) allocates +the scaler table; `BuildCompScale()` (`WL_SCALE.C:153`) compiles one height; +`ScaleShape()` (`WL_SCALE.C:421`) and `SimpleScaleShape()` (`:625`) invoke the +compiled code to draw a masked shape at a given center and height. `ScaleLine` +(`WL_SCALE.C:249`) is the per-column primitive. `OLDSCALE.C` and `CONTIGSC.C` +are earlier/alternate versions of the same idea kept in the tree. + +> Carmack's retrospective (`README.rst`) notes these compiled scalers are now +> counter-productive: on cached CPUs they thrash the instruction cache, and a +> plain texture-mapping loop would be faster on a 486+. + +## The player weapon and HUD (`WL_AGENT.C`) + +`WL_AGENT.C` owns the first-person weapon sprite and the status-bar face; it +draws the weapon frame each tick after the world is rendered and manages the +face animation and HUD numbers. + +## Video drivers + +### Low level — `ID_VL.C` / `ID_VL_A.ASM` + +`ID_VL.C` is the VGA register driver. It sets the planar **Mode-X** display +(`VL_SetVGAPlaneMode`, `ID_VL.C:115`), configures line width and split-screen +(`VL_SetLineWidth` `:246`, `VL_SetSplitScreen` `:277`), and owns the palette +(`VL_SetPalette` `:371`, `VL_SetColor` `:332`, `VL_FillPalette` `:309`). Plane +selection and fast fills drop into `ID_VL_A.ASM`; register addresses are the +`ID_*` equates in `ID_VL.EQU` / `ID_VL.H`. + +### High level — `ID_VH.C` / `ID_VH_A.ASM` + +`ID_VH.C` is the *view* layer above the raw driver: masked-pic and tile blits +(`VWB_DrawPic` `:304`, `VWB_DrawTile8`/`8M` `:291`/`:297`), primitives +(`VWB_Bar`, `VWB_Plot`, `VWB_Hlin`, `VWB_Vlin`), proportional-font string +drawing (`VW_DrawPropString` `:40`, `VW_MeasurePropString` `:214`), and the +dirty-rectangle bookkeeping (`VW_MarkUpdateBlock` `:246`). `VL_MungePic` +(`ID_VH.C:170`) rearranges a linear pic into the planar layout the VGA wants. + +## Data flow into the framebuffer + +``` +PlayLoop → ThreeDRefresh + ├─ cast rays → HitVertWall / HitHorizWall (walls) + ├─ TransformActor (per actor) (view space) + └─ DrawScaleds → ScaleShape (sprites) + │ + WL_AGENT weapon/HUD ───────┤ + ▼ + ID_VH view layer → ID_VL Mode-X page → VGA +``` + +See also: [rendering constants](../reference/constants.md), +[`files/WL_DRAW.md`](../files/WL_DRAW.md), [`files/WL_SCALE.md`](../files/WL_SCALE.md), +[`files/ID_VL.md`](../files/ID_VL.md), [`files/ID_VH.md`](../files/ID_VH.md). diff --git a/docs/systems/tools-build.md b/docs/systems/tools-build.md new file mode 100644 index 0000000..3d8d213 --- /dev/null +++ b/docs/systems/tools-build.md @@ -0,0 +1,57 @@ +# Tools and build subsystem + +This covers how the program is built and the small assembler/utility pieces +that support the build rather than the game at runtime. + +## Toolchain + +The target is **Borland C++ 3.0** (per `README.rst` and `GOODSTUF.TXT`) for +16-bit DOS. The project is described by, in decreasing age: + +| File | Role | +|--------------|--------------------------------------------------------------| +| `WOLF3D.PRJ` | Borland project — source list and per-file compiler options | +| `WOLF.IDE` | Older Borland IDE project form | +| `WOLF.DSW` | Turbo/Borland context (workspace) file | +| `WOLF.OBR` | Browser database emitted by the IDE | +| `GO.BAT` | Batch entry point that drives the build | +| `RULES.ASI` | Borland TASM rules/equates included by the assembler sources | + +The idiomatic build is `CD WOLFSRC` then `GO.BAT`. Pre-built outputs +(`WOLF3D.EXE`, `WOLF.EXE`, `SV.EXE`) and the link map (`WOLF3D.MAP`) are +checked in alongside the source, as is an `OBJ/` directory of object files. + +> The executable still needs game data (walls, sprites, sound, maps) from a +> retail or shareware release of Wolfenstein/Spear — that data is **not** in +> this repository. See `README.rst`. + +## Assembler support files + +* `C0.ASM` — the C startup module (program entry stub, segment setup) that + runs before `main`. +* `H_LDIV.ASM` — long-division helper used by the fixed-point math. +* `RULES.ASI` — shared TASM macros/equates pulled into the `.ASM` files. +* `*.EQU` tables (`ID_VL.EQU`, `ID_SD.EQU`, `GFX*_*.EQU`) — assembler equates + that mirror the C constants so hand-written asm and C agree on register + addresses, chunk ids, and offsets. + +## Utility / patch code + +* `JABHACK.ASM` + `WHACK_A.ASM` — the "JAB HACK" self-modifying/patch code + (initials of Jason A. Blochowiak) used to poke the running image. +* `MUNGE.C` — an offline data-munging helper for preparing asset data. +* `CONTIGSC.C` / `OLDSCALE.C` — alternate/earlier versions of the compiled + scaler kept in the tree for reference (the live scaler is `WL_SCALE.C`; see + [rendering](rendering.md)). +* `WOLFHACK.C` — an experimental variable-height-wall renderer, not part of the + shipping build. +* `DETECT.C` — hardware-detection helper. + +## Per-product configuration + +The many version headers (`WOLF1VER.H`, `SODVER.H`, `SDMVER.H`, `SPANVER.H`, +`WOLFJVER.H`, `WOLFGTV.H`, …) plus `FOREIGN.H` and `F_SPEAR.H` are compile-time +switches that select which product this build is. See +[ARCHITECTURE §1](../ARCHITECTURE.md) for the full table. + +See also: [constants reference](../reference/constants.md). diff --git a/docs/systems/ui-menu.md b/docs/systems/ui-menu.md new file mode 100644 index 0000000..6349dee --- /dev/null +++ b/docs/systems/ui-menu.md @@ -0,0 +1,54 @@ +# UI and menu subsystem + +This subsystem covers everything the player reads or clicks outside the 3-D +view: the front-end and in-game **control panel**, the scrolling **text +pager** used for the help/story articles and the end screens, and the +**proportional-font** string rendering underneath both. + +Owning files: + +| File | Role | +|-------------|-----------------------------------------------------------------| +| `WL_MENU.C` | Control panel: main menu, new game, sound/controls, load/save | +| `WL_MENU.H` | Menu item structs and layout constants | +| `WL_TEXT.C` | Text pager: command parsing, page layout, article display | +| `ID_US_1.C` | User Manager: window/print helpers, input fields, page setup | +| `ID_US.H` | User Manager public API | + +## Control panel (`WL_MENU.C`) + +`US_ControlPanel(scancode)` (`WL_MENU.C:332`) is the entry point — pressing +Escape in game, or reaching a menu from the front end, lands here. It draws +`DrawMainMenu()` (`WL_MENU.C:539`) and dispatches to the sub-panels: + +* New game / episode / difficulty — `CP_NewGame` (`:926`), + `DrawNewEpisode` (`:1050`), `DrawNewGameDiff` (`:1124`). +* Sound options — `CP_Sound` (`:1135`), `DrawSoundMenu` (`:1252`). +* End game — `CP_EndGame` (`:864`); view high scores — `CP_ViewScores` (`:893`). +* Read This (help/order screens) — `CP_ReadThis` (`:610`). +* Boss key — `BossKey` (`:627`), the instant "hide the game" panic screen. +* Quick-key handling — `CP_CheckQuick` (`:650`). + +Each menu is a table of items plus draw/handle callbacks; navigation and +highlight are shared logic in the User Manager. + +## Text pager (`WL_TEXT.C`) + +`WL_TEXT.C` renders the story/help "articles," which are text with embedded +layout commands. `ShowArticle()` (`WL_TEXT.C:599`/`:601`) displays one; the +parser handles commands (`HandleCommand` `:186`), embedded pictures +(`ParsePicCommand` `:125`, `TimedPicCommand` `:155`), and word wrapping +(`HandleWord` `:352`, `NewLine` `:289`). `PageLayout()` (`:412`) lays out a +page and `BackPage()` (`:518`) steps backward; `CacheLayoutGraphics()` (`:543`) +preloads the pics a page needs. + +## Font and window rendering (`ID_US_1.C`) + +The User Manager provides the primitives the menus and pager build on: +bordered windows, centered/left print helpers, blinking-cursor input fields, +and the proportional-font string draw path (which ultimately calls +`VW_DrawPropString` in `ID_VH.C` — see [rendering](rendering.md)). It also owns +the print cursor state (`PrintX`/`PrintY`) shared across UI code. + +See also: [`files/WL_MENU.md`](../files/WL_MENU.md), +[`files/WL_TEXT.md`](../files/WL_TEXT.md), [`files/ID_US_1.md`](../files/ID_US_1.md).