Ultima II Map Format
Format type | Map/level |
---|---|
Map type | 2D cell-based |
Layer count | 1 |
Tile size (pixels) | 16×16 |
Viewport (pixels) | 320×160 |
Cell dimensions | 64×66 (for planets and towns) |
Games |
Ultima II: Revenge of the Enchantress uses dozens of maps for planet overworlds and towns. Those maps with numbers ending in 0 are planets, those ending in 1, 2, or 3 are towns, those ending in 4 or 5 are dungeons. Planet and town maps are 64×66 cells, which yields 1,024×1,056 pixels. Dialogue from NPCs is stored in separate files in the Ultima II Talk Format.
The tiles used to draw the map cells are stored in the ULTIMAII.EXE file. Interestingly, the game rewrites the map files as the game runs, saving the location of NPCs and monsters directly in the map files.
File Format
For planets and towns, the map data is a string of 4,224 bytes where each byte represents a tile id. The values in the map match up with the tiles in the ULTIMAII.EXE, only they're all multiplied by 4, so, the tile id from the map file must be divided by 4 to get the correct tile in the tile lookup.
Data type | Name | Description |
---|---|---|
UINT8[4,224] | Tile Id | The tile id for each tile in the map. |
Tile Data
Remember that the maps store the tile id ×4. So, a mountain, which is tile id 4, will be stored as 16 (0x10).
Tile Id | Tile Description |
---|---|
0 | Water |
1 | Swamp |
2 | Grass |
3 | Forest |
4 | Mountain |
5 | ? |
6 | Town |
7 | Tower |
8 | Castle |
9 | Dungeon Entrance |
10 | Signpost |
11 | Sea Monster |
12 | Orc |
13 | Daemon |
14 | Devil |
15 | Balron |
16 | Minax |
17 | Horse |
18 | Ship |
19 | Airplane |
20 | Rocket |
21 | Shield |
22 | Sword |
23 | Forcefield |
24 | Guard |
25 | Jester |
26 | Shopkeep |
27 | ? |
28 | Road |
29 | Empty |
30 | Wall |
31 | Empty Counter / Space |
32-39 | A-H |
40 | I / Door |
41-47 | J-P |
48 | Moongate |
49-57 | R-Z |
58 | Counter End, Right |
59 | Counter End, Left |
60 | Fighter |
61 | Cleric |
62 | Mage |
63 | Thief |
The map file does not store which specific NPC, castle, town, dungeon, or signpost is at which location.
Source Code
Map Viewer
This FreeBASIC program displays game maps with the proper tile set.
' Renders the specified Ultima II map.
#include once "fbgfx.bi"
' Change to your Ultima II path, and set the map file you want to view.
Dim As String DataPath = "H:\DOS\Ultima2"
Dim As String MapFile = "mapx20"
Dim As fb.Image Ptr TileCGA(0 To 63)
Dim As UByte X
Dim As UByte Y
Dim As Integer Pixel
Dim As UByte Pixels
Dim As UByte TileNumber
Dim As UShort OffsetX = 0
Dim As UShort OffsetY = 0
Dim As UByte Offset
Dim As UByte ColorBlock
' Graphic tile data is stored in the EXE.
Open DataPath + Chr(92) + "ultimaii.exe" For Binary As #1
Seek 1, 31811 ' Jump to the start of the over world tiles.
ScreenRes 1024, 1056, 32
' Load all of the tiles into memory.
For TileNumber = 0 To 63
For Y = 0 To 15
For X = 0 To 3
' CGA Linear stores 4 pixels per byte. Bits 7 and 6 determine the
' first color, bits 5 and 4 determined the next color, and so on.
Get #1, , Pixels
' Determine the four pixels from this one byte.
ColorBlock = 7
For Offset = 0 To 3
If Bit(Pixels, ColorBlock) = -1 Then
If Bit(Pixels, ColorBlock - 1) = -1 Then
Pixel = RGB(170, 170, 170)
Else
Pixel = RGB(170, 0, 170)
End If
Else
If Bit(Pixels, ColorBlock - 1) = -1 Then
Pixel = RGB(0, 170, 170)
Else
Pixel = RGB(0, 0, 0)
End If
End If
' Plot the pixel.
PSet((X * 4) + Offset, Y), Pixel
ColorBlock = ColorBlock - 2
Next Offset
Next X
Next Y
Get #1, , Pixels
Get #1, , Pixels
' Load the tile into an image buffer.
TileCGA(TileNumber) = ImageCreate(16, 16)
Get (0, 0)-(15, 15), TileCGA(TileNumber)
Next TileNumber
Close #1
' Open the map file.
CLS
Open DataPath + Chr(92) + MapFile For Binary As #1
' Load the entire map, and draw the appropriate tiles.
For Y = 0 To 65
For X = 0 To 63
Get #1, , TileNumber
TileNumber = TileNumber / 4
Put (X * 16, Y * 16), TileCGA(TileNumber)
Next X
Next Y
Close #1
Sleep
Credits
This map format was reverse engineered by TheAlmightyGuru. If you find this information helpful in a project you're working on, please give credit where credit is due. (A link back to this wiki would be nice too!)