A Doom map is two-dimensional: a floor plan of lines, with a floor and ceiling height for each area. A map in a WAD is an empty marker lump with the map’s name and then these lumps, always in this order. Each of the first eight is an array of fixed-size records with nothing before the first or after the last. Numbers are 16-bit and little-endian, signed (i16) unless marked u16; positions are in map units, x growing east and y north. Records refer to one another by their number in their lump, counting from 0.
E1M1Marker the map’s name, no data0 bytesTHINGSThings monsters, items, player starts10 bytes eachLINEDEFSLines the walls and edges of the plan14 bytes eachSIDEDEFSSides the textures on each side of a line30 bytes eachVERTEXESVertices the points lines join4 bytes eachSEGSSegs lines cut up by the node builder12 bytes eachSSECTORSSubsectors convex areas, each a run of segs4 bytes eachNODESNodes the BSP tree28 bytes eachSECTORSSectors areas with floor and ceiling26 bytes eachREJECTReject table sector-to-sector sightvariesBLOCKMAPBlockmap lines by 128-unit blockvaries
A map editor writes the first four lumps and SECTORS. A node builder works
out SEGS, SSECTORS, NODES, REJECT and
BLOCKMAP from them, for the engine’s drawing, collisions and sight checks.
THINGS
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
| 0 | 2 | i16 | x | Position east–west. |
| 2 | 2 | i16 | y | Position north–south. |
| 4 | 2 | i16 | angle | The way it faces in degrees: 0 east, 90 north, 180 west, 270 south. |
| 6 | 2 | i16 | type | What it is: the thing’s number in the game’s table, such as 1 for player 1’s start or 3001 for an imp. |
| 8 | 2 | i16 | options | Flags, below. |
| Bit | Value | Meaning |
|---|---|---|
| 0 | 1 | Present on skills 1 and 2 |
| 1 | 2 | Present on skill 3 |
| 2 | 4 | Present on skills 4 and 5 |
| 3 | 8 | Ambush: the monster stays put until it sees a player, whatever it hears |
| 4 | 16 | Only in multiplayer games |
Doom ignores the other bits. Boom later used bit 5 (not in deathmatch) and bit 6 (not in co-op), and MBF bit 7 (friendly).
0x40 00(64)2 bytes2yC0 FF(−64)2 bytes4angle5A 00(90, north)2 bytes6type01 00(player 1 start)2 bytes8options07 00(every skill)2 bytes
LINEDEFS
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
| 0 | 2 | i16 | v1 | The vertex the line starts at. |
| 2 | 2 | i16 | v2 | The vertex it ends at. Looking from v1 to v2, the right side is the front. |
| 4 | 2 | i16 | flags | Flags, below. |
| 6 | 2 | i16 | special | What happens when the line is crossed, pressed or shot: a door, a lift, a teleporter, the exit. 0 for none. |
| 8 | 2 | i16 | tag | The special acts on the sectors with this tag. |
| 10 | 2 | i16 | sidenum[0] | The right sidedef. |
| 12 | 2 | i16 | sidenum[1] | The left sidedef, or −1 (FF FF) for a one-sided line. |
| Bit | Value | Name in the source | Meaning |
|---|---|---|---|
| 0 | 1 | ML_BLOCKING | Blocks players and monsters |
| 1 | 2 | ML_BLOCKMONSTERS | Blocks monsters only |
| 2 | 4 | ML_TWOSIDED | Two-sided: there is a sector on both sides |
| 3 | 8 | ML_DONTPEGTOP | Upper texture unpegged: drawn from the top down |
| 4 | 16 | ML_DONTPEGBOTTOM | Lower texture unpegged |
| 5 | 32 | ML_SECRET | Shown on the automap as a one-sided wall |
| 6 | 64 | ML_SOUNDBLOCK | Sound passing two of these stops |
| 7 | 128 | ML_DONTDRAW | Never shown on the automap |
| 8 | 256 | ML_MAPPED | Shown on the automap from the start |
Boom later used bit 9 to let a pressed line pass the use on to lines behind it.
SIDEDEFS
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
| 0 | 2 | i16 | textureoffset | How far the textures are moved along the wall. |
| 2 | 2 | i16 | rowoffset | How far they are moved up or down. |
| 4 | 8 | ASCII | toptexture | The upper texture, drawn above an opening where the ceiling beyond is lower. - for none. |
| 12 | 8 | ASCII | bottomtexture | The lower texture, drawn below an opening where the floor beyond is higher. |
| 20 | 8 | ASCII | midtexture | The wall itself on a one-sided line; on a two-sided line usually -, or a see-through texture. |
| 28 | 2 | i16 | sector | The sector this side faces into. |
VERTEXES
Two 16-bit numbers each: x at offset 0 and y at offset 2. The node builder adds vertices of its own at the end, where it splits lines into segs.
SEGS
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
| 0 | 2 | i16 | v1 | Start vertex. |
| 2 | 2 | i16 | v2 | End vertex. |
| 4 | 2 | i16 | angle | Direction, in units of 1/65536 of a turn: 0 east, 16384 north, −32768 west, −16384 south. |
| 6 | 2 | i16 | linedef | The line the seg is part of. |
| 8 | 2 | i16 | side | 0 for the line’s right side, 1 for its left. |
| 10 | 2 | i16 | offset | How far along the line the seg starts. |
SSECTORS
Each subsector is a convex area of one sector, bounded by a run of segs: the number of segs (offset 0) and the first of them (offset 2), 4 bytes in all.
NODES
The binary space partition tree the engine draws with. Each node splits the area below it in two along a line. The last node is the root.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
| 0 | 2 | i16 | x | A point on the partition line. |
| 2 | 2 | i16 | y | |
| 4 | 2 | i16 | dx | The line’s direction. |
| 6 | 2 | i16 | dy | |
| 8 | 8 | i16 × 4 | bbox[0] | The right child’s bounding box: top, bottom, left, right. |
| 16 | 8 | i16 × 4 | bbox[1] | The left child’s bounding box. |
| 24 | 2 | u16 | children[0] | The right child: a node number, or with bit 15 (8000, NF_SUBSECTOR) set a subsector number. |
| 26 | 2 | u16 | children[1] | The left child, the same way. |
SECTORS
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
| 0 | 2 | i16 | floorheight | The height of the floor. |
| 2 | 2 | i16 | ceilingheight | The height of the ceiling. |
| 4 | 8 | ASCII | floorpic | The flat on the floor. |
| 12 | 8 | ASCII | ceilingpic | The flat on the ceiling; F_SKY1 shows the sky. |
| 20 | 2 | i16 | lightlevel | Brightness, 0 to 255. |
| 22 | 2 | i16 | special | An effect: flickering light, damage, a secret. 0 for none. |
| 24 | 2 | i16 | tag | Line specials with the same tag act on this sector. |
REJECT
A table of one bit for each pair of sectors, which lets the engine skip line-of-sight checks. Bit number a × sectors + b is set when a monster in sector a can never see a player in sector b. Bits are taken from the lowest bit of each byte first. The lump is (sectors² + 7) ÷ 8 bytes, rounded down; many node builders write it all zeros, which makes the engine check every time.
BLOCKMAP
A grid of 128 × 128 unit blocks over the map, and for each block the lines that touch it, for collision checks.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
| 0 | 2 | i16 | x origin | The west edge of the grid. |
| 2 | 2 | i16 | y origin | The south edge of the grid. |
| 4 | 2 | i16 | columns | Blocks across. |
| 6 | 2 | i16 | rows | Blocks up. |
| 8 | 2 each | u16 | offsets | For each block, row by row from the south-west, eastwards first: where its list starts, counted in 2-byte words from the start of the lump. |
| … | varies | u16 | lists | Each list is a 0, the numbers of the lines in the block, and −1 (FF FF). Blocks with the same lines can share a list. |
0Header00 00 00 00 01 00 01 00: one block from 0, 08 bytes8Offset05 00: the list starts at word 5, byte 102 bytes10List00 00, lines00 00 01 00, thenFF FF8 bytes
Hexen’s format
Hexen’s maps have an eleventh lump, BEHAVIOR, holding compiled ACS
scripts (it starts ACS and a zero byte), and give things and lines more
fields. A map with a BEHAVIOR lump is in Hexen’s format; ZDoom and later
ports also use it for Doom maps. The other lumps are as in Doom.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
| 0 | 2 | i16 | tid | A thing ID that scripts and specials refer to; 0 for none. |
| 2 | 2 | i16 | x | Position east–west. |
| 4 | 2 | i16 | y | Position north–south. |
| 6 | 2 | i16 | height | Height above the floor. |
| 8 | 2 | i16 | angle | Facing, in degrees. |
| 10 | 2 | i16 | type | What it is. |
| 12 | 2 | i16 | flags | 1, 2, 4 skills; 8 ambush; 16 dormant; 32 fighter, 64 cleric, 128 mage; 256 single player, 512 co-op, 1024 deathmatch. |
| 14 | 1 | u8 | special | An action run when the thing dies or is picked up. |
| 15 | 5 | u8 × 5 | args | The action’s five arguments. |
A thing in Hexen’s format: 20 bytes.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
| 0 | 2 | i16 | v1 | Start vertex. |
| 2 | 2 | i16 | v2 | End vertex. |
| 4 | 2 | i16 | flags | Doom’s bits 0 to 8; bit 9 (512) repeatable; bits 10 to 12 how the special is set off: 0 player crosses, 1 player uses, 2 monster crosses, 3 projectile hits, 4 player bumps, 5 projectile crosses. |
| 6 | 1 | u8 | special | The action number. |
| 7 | 5 | u8 × 5 | args | Its arguments: a tag, a speed, a script number and so on. |
| 12 | 2 | i16 | sidenum[0] | The right sidedef. |
| 14 | 2 | i16 | sidenum[1] | The left sidedef, or −1. |
A line in Hexen’s format: 16 bytes. There is no tag field: the specials take their tag as an argument.
Later map formats
Source ports added other ways to store a map. UDMF keeps the whole map as text in a
TEXTMAP lump after the marker, up to an ENDMAP marker. GL node
builders add lumps such as GL_VERT and GL_SEGS after a second
marker named GL_ and the map’s name, and ZDoom can keep compressed nodes in
ZNODES. The explorer names these lumps and shows TEXTMAP as
text, but draws only maps in Doom’s and Hexen’s formats.