Level maps and object lists
Level map information is stored in the file "lev.ark". A default map is in the "data" folder and is loaded after character creation. During gameplay, the map is stored in the folder "Save0".
File format
The file is a container for several differently-sized blocks that contain different infos of the level maps. Some blocks may be unused, e.g. automap blocks.
The file header looks like this:
0000 Int16 number of blocks in file
0002 Int32 file offset to block 0
0006 Int32 file offset to block 1
... etc.
File offsets are absolute offsets into the file. When an offset is 0, the block is not available.
Ultima Underworld 1 has 135 (0x0087) entries (9 levels x 15 blocks). The block layout is as following:
<9 blocks level tilemap/master object list>
<9 blocks object animation overlay info>
<9 blocks texture mapping>
<9 blocks automap infos>
<9 blocks map notes>
The remaining 9 x 10 blocks are unused.
The Ultima Underworld Demo uses three separate files to store the map.
Here's a list of the files and what blocks they contain:
| File | Description |
|---|---|
| level13.st | level tilemap/master object list block |
| level13.txm | texture mapping |
| level13.anx | object animation overlay info |
Ultima Underworld 2 has 320 (0x0140) entries (80 levels x 4 blocks).
These can be split into 4 sets of 80 entries each:
0.. 79 level maps
80..159 texture mappings
160..239 automap infos
240..319 map notes
Data blocks for Ultima Underworld 2 are compressed using the uw2 compression scheme described in chapter 9.1.
The "level tilemap/master object list" for each level contains infos about the level architecture (tilemap) and the objects which live in it:
| offset | size | description |
|---|---|---|
| 0000 | 4000 | tilemap (64 x 64 x 4 bytes) |
| 4000 | 1b00 | mobile object information (objects 0000-00ff, 256 x 27 bytes) |
| 5b00 | 1800 | static object information (objects 0100-03ff, 768 x 8 bytes) |
| 7300 | 01fc | free list for mobile objects (objects 0002-00ff, 254 x 2 bytes) |
| 74fc | 0600 | free list for static objects (objects 0100-03ff, 768 x 2 bytes) |
| 7afc | 0104 | unknown (260 bytes) |
| 7c00 | 0002 | |
| 7c02 | 0002 | no. entries in mobile free list minus 1 |
| 7c04 | 0002 | no. entries in static free list minus 1 |
| 7c06 | 0002 | 0x7775 ('uw') |
Level tilemap
Each underworld level consists of a 64x64 tile map (just like on a chess board). A tile can be of different types and can have various floor heights. The ceiling height is fixed. A tile can have an index into the master object list that is the start of an object chain with objects in this tile.
The first 0x4000 bytes of each "level tilemap/master object list" contain the tilemap info bytes. For each tile there are two Int16 that describe a tile's properties. The map's origin is at the lower left tile, going to the right, each line in turn.
The two Int16 values can be split into bits:
0000 tile properties / flags:
| bits | len | description |
|---|---|---|
| 0- 3 | 4 | tile type (0-9, see below) |
| 4- 7 | 4 | floor height |
| 8 | 1 | unknown (?? special light feature ??) always 0 in uw1 |
| 9 | 1 | 0, never used in uw1 |
| 10-13 | 4 | floor texture index (into texture mapping) |
| 14 | 1 | when set, no magic is allowed to cast/to be casted upon |
| 15 | 1 | door bit (when 1, a door is present) |
0002 tile properties 2 / object list link
| bits | len | description |
|---|---|---|
| 0- 5 | 6 | wall texture index (into texture mapping) |
| 6-15 | 10 | first object in tile (index into master object list) |
about word 0000, bit 8:
For UW2 Bit 8 is set pretty often, and if set the light level changes. Ironically 1 sometimes means daylight (in Lord British Castle Lv 1) but sometimes 0 means daylight (LBC Lv 5). But the areas are exactly right.
Underworld tile types
| Nr | Type |
|---|---|
| 00 | Solid (wall tile) |
| 01 | Open (square tile of empty space) |
| 02 | Diagonal, open SE |
| 03 | Diagonal, open SW |
| 04 | Diagonal, open NE |
| 05 | Diagonal, open NW |
| 06 | Sloping up to the north |
| 07 | Sloping up to the south |
| 08 | Sloping up to the east |
| 09 | Sloping up to the west |
Master object list
The master object list is stored after the tilemap data. There are 1024 (0x0400) list positions. The first 256 are reserved for "mobile objects" that have extra NPC info. The rest of the list is used for "static objects". The list entries are allocated from the end to the beginning of the lists. Item positions that are free are stored in the "free lists", described in chapter 4.3.
Entry 0 is never allocated and is used to test against item links of value 0. Entry 1 is partly used to store the player's informations, e.g. direction or in-tile x/y positions.
Each object entry has a "general object info" block consisting of 4 Int16 words. The 256 "mobile objects" are followed by 19 bytes "mobile object extra info", resulting in entries of 27 bytes length. The remaining 768 entries only have 8 bytes each. The "mobile object extra info" is described in chapter 4.2.3.
The "general object info" block looks as following:
bits size field description
0000 objid / flags
0- 8 9 "item_id" Object ID (see below)
9-12 4 "flags" Flags
12 1 "enchant" Enchantment flag (enchantable objects only)
13 1 "doordir" Direction flag (doors)
14 1 "invis" Invisible flag (don't draw this object)
15 1 "is_quant" Quantity flag (link field is quantity/special)
0002 position
0- 6 7 "zpos" Object Z position (0-127)
7- 9 3 "heading" Heading (*45 deg)
10-12 3 "ypos" Object Y position (0-7)
13-15 3 "xpos" Object X position (0-7)
0004 quality / chain
0- 5 6 "quality" Quality
6-15 10 "next" Index of next object in chain
0006 link / special
0- 5 6 "owner" Owner / special
6-15 10 (*) Quantity / special link / special property
All field names listed are used later to refer to these fields in the object's information words.
Object IDs can be split up for classification purposes. Read more in chapter 6 about it.
Objects in a tile are stored as a linked list, where the "Index of next object in chain" points to the next object in list, or contains 0 for the end of the linked list. The first object in the list is determined by the tile's object index value (see above, at "Tile map").
(*) The "Quantity" field in word 0006 can have several meanings. If the "is_quant" field is 0 (unset), it contains the index of an associated object. The exact meaning varies, but is generally a "has-a" type relationship (contents, trap to set off, spell). The field name "sp_link" is used in this document if that type of field is meant.
If the "is_quant" flag is set, the field is a quantity or a special property. If the value is < 512 or 0x0200 it gives the number of stacked items present. Identical objects may be stacked up to 256 objects at a time. The field name "quantity" is used for this.
If the value is > 512, the value minus 512 is a special property; the object type defines the further meaning of this value (see chapter 6.1 for all special objects in Ultima Underworld). The field name "property" is used for this type of value.
Note that the term "object" and "item" are used concurrently in the document and always mean the same thing.
Enchantments
If the enchantment flag is set and the object is enchantable, then the link field (less 512) determines the enchantment. Enchantment names are stored in strings chunk 5. The way in which the link value maps onto spells in this chunk depends on the object type.
Most objects seem to use spells 256-320 (add 256) if the enchantment number is in the range 0-63, otherwise they add 144 to use spells 208 and up. Healing fountains, however, don't use a correction at all.
Weapons and armour have a more complex mapping. Most enchanted weapons and pieces of armour have an enhancement for Accuracy, Damage, Protection or Toughness, which are spells 448-479 in the main spell list. These map to special property values 192-207. Yes, there are only 16 values for 32 spells; enchanted armour adds another 16 to the spell index to bring it into the armour enchantment range. However, these items may also carry generic enchantments, in which case the special properties map to spells 0-255 (and armour doesn't apply a special correction).
Wands don't hold their enchantments directly in the quantity field, since they also need to store the number of charges remaining. Instead, they link to a spell object which holds the enchantment; it seems here that the "quality" field of the spell object determines the number of charges. Other objects may also carry spells in this way.
Item Owner
Some items have a "... belongs to " description. The common object properties (see chapter 6.2) determine if an object can have an owner. The string printed is stored in the "owner" field and is an index into string block 1; the string printed for the critter type is "owner" - 1 + 370. When the field is 0, the object doesn't belong to anyone.
Mobile object extra info
The values stored in the NPC info area (19 bytes) contain infos for critters unique to each object.
offsets type bits meaning
0008 0000 Int8 0-7 npc_hp
0009 0001
000a 0002 Int8 7
000b 0003 Int16 0-3 npc_goal
4-11 npc_gtarg
000d 0005 Int16 0-3 npc_level
4-11
8
13 npc_talkedto
14-15 npc_attitude
000f 0007 Int16 6- 12 npc height?
0011 0009
0012 000a
0013 000b Int8 7 single bit, unknown
0014 000c
0015 000d
0016 000e Int16 0-3 unknown
4-9 npc_yhome
10-15 npc_xhome
0018 0010 Int8 0-4: npc_heading?
5-7:
0019 0011 Int8 0-6: npc_hunger (?)
001a 0012 Int8 npc_whoami
The values are used for combat, AI and conversations.
Free lists
The free lists generally contain infos about the master object list usage and are used for object slot allocation/deallocation.
Free list, mobile objects
This consists of an Int16 for each mobile object (critter) slot which is not in use, giving the slot position in the master object list. Note that there are only 254 entries in this table because object 0 is always the null object (and hence is never allocated) and object 1 is always the avatar (and can never be free - that's probably a metaphor for life, or something). Of course, only the first (no. free mobile objects) entries are valid.
Free list, static objects
This consists of an Int16 for each static object which is not in use, as above. This table is 768 entries long (room for all possible static objects).
Texture mappings
The texture mapping table are used to map tile texture indices to the actual texture used, since wall and floor textures are only encoded with 6 and 4 bits. The block of size 0x007a always look like this:
0000 48 x Int16 wall texture number (from w64.tr)
0060 10 x Int16 floor texture number (from f32.tr)
0074 6 x Int8 door texture number (from doors.gr)
007a
The last value from the floor texture number array is used as ceiling texture.
"Look" descriptions come from block 000a, where wall textures use strings 0 to 255 and floor textures are described by strings 256 to 510, in reverse order. Ceiling always uses string 511.
In uw2 the texture mapping is 134 (0x0086) bytes long and contains 64 Int16 entries that are indices into t64.tr. The first 16 entries are shared by the floor and wall indices. Entries above 16 are wall-only textures. Ceiling seems to be textured by entry 0x20. The last 6 bytes is the door texture mapping, see above.
"Look" descriptions are almost the same as in uw1, but as textures can be shared between walls and floors, there are two descriptions for every entry in t64.tr. Floor descriptions start at string 255 without reversing order.
Animation infos
This block contains entries with length of 6 bytes with infos about objects with animation overlay images from "animo.gr". It always is 0x0180 bytes long which leads to 64 entries.
0000 Int16 link1
0002 Int16 unk2
0004 Int8 tile x coordinate
0005 Int8 tile y coordinate
link1's most significant 10 bits contain a link into the master object list, to the object that should get an animation overlay.
Automap infos
Each block contains the "visited" bytes for each level. Each byte describes a tile on the main map. The block size always is 0x1000.
rest not decoded yet
Terrain texture properties
The file "terrain.dat" in the data directory contains information on the terrain types represented by the various wall and floor textures. There is a 16-bit word per texture, up to a maximum of 256 walls and 256 floors. Floor data therefore starts at file offset 0x200. Terrain types are:
0000 Normal (solid) wall or floor
0002 Ankh mural (shrines)
0003 Stairs up
0004 Stairs down
0005 Pipe
0006 Grating
0007 Drain
0008 Chained-up princess
0009 Window
000a Tapestry
000b Textured door (used for the lock to the Key of Infinity)
0010 Water (not waterfall)
0020 Lava (not lavafall)
0040 Waterfall - UW2
00C0 Ice wall - UW2
00D8
00E8 Ice walls (crumbling?)
0080 Lavafall - UW2
00F8 Ice - UW2