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)
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