Map Data Structure - HoraceAndTheSpider/Bloodwych-68k GitHub Wiki

The main structure of the BW map data is contained within a 4096 byte ($1000 byte) block for each Tower or Dungeon. There are 6 map blocks in Bloodwych, and 4 map blocks in Extended Levels.

In the PC version of the game, this is found as a self contained .map file.

In the Amiga and ST versions, these blocks are found at various offsets in the BW game binary - see Data Locations below.

Data Locations: (Amiga - SPS 102)

[$0EC7C] [MapData1] The Keep [$1007E] [MapData2] Serpent Tower [$11480] [MapData3] Moon Tower [$12882] [MapData4] Dragon Tower [$13C84] [MapData5] Chaos Tower [$15086] [MapData6] Zendicks Tower

Data Definition:

Each block contains $38 bytes of 'header' data, giving information about the layout of the Tower / Dungeon, this is then followed by $FC8 bytes of data containing the map data itself.

$00-$07 : contain the Width values of the respective Floors of the the Tower, starting with the lowest floor. $08-$0F : contain the Height values of the respective Floors of the the Tower, starting with the lowest floor.

$10-$1F : are used to contain the offset values of the respective Floors of the the Tower, starting with the lowest floor - these are equivalent to the offset values used to assign the position of objects within a map (see: Object Data Structure). These offsets count bytes from the map data after the header, not from the start of the whole block.

$20-$27 : contain the X-alignment values in relation to each other floor, for each of the respective Floors of the the Tower, starting with the lowest floor. $28-$2F : contain the Y-alignment values in relation to each other floor, for each of the respective Floors of the the Tower, starting with the lowest floor.

$30-31 : contains the width of one specifically specified floor for each tower. $32-33 : contains the height of one specifically specified floor for each tower. (as above) $34-35 : contains the offset value of one specifically specified floor for each tower. (as above)

These three values are a copy of one floor's details, rather than an entrance or exit point. The game keeps track of each player's floor separately. The source details below explain how this copy is used.

$36-37 : contains the number of the top floor of the tower. Floors start at 0, so a top floor of 5 means there are six floors.

$38-$0FFF : contain the map data for the tower.

Example Data: The Keep

       0C 15 0F 1F 13 04 00 00  <<< Widths of Floors 0-7
       01 15 0F 1F 13 05 00 00  <<< Heights of Floors 0-7

       00 00  << offset at which floor 0 begins
       00 18  << offset at which floor 1 begins
       03 8A  << offset at which floor 2 begins
       05 4C  << offset at which floor 3 begins
       0C CE  << offset at which floor 4 begins
       0F A0  << offset at which floor 5 begins
       0F C8  << offset at which floor 6 begins (although there is no floor 6 in the Keep)
       00 00  << offset at which floor 7 begins

       08 05 08 00 06 0B 00 00 <<< X offsets of Floors 0-7
       05 05 08 00 06 0C 00 00 <<< Y offsets of Floors 0-7

       00 15 00 15                   <<<< Width / Height of "special" floor (floor 1 for the keep)
       00 18                             <<< offset at which "special" floor begins (floor 1 for the keep)

       00 05                             <<<  Number of Top Floor

Map Location Data Table

The data is stored as consecutive two bytes (word) per map-location; AB CD

The lowest three bits of D represent one of the following map types. Its remaining bit is a flag, so it must be ignored when reading the type:

      0 - Spaces
      1 - Stone Walls
      2 - Wooden Walls
      3 - Misc.
      4 - Stairs
      5 - Metal Doors
      6 - Pads, Triggers & Holes
      7 - Magic Locations

C is usually used for "generic" detail (such as N/E/S/W rotation) for wall-types which fill the space. For map-data which is not "occupied" (e.g wood walls, trigger pads etc) , a more generic system is used for C which defines any other occupancy in that location. This can be found in the sub-section regarding 'Spaces'.

AB are used to define the detail of the map-data. This would be such as, colours of gems used, types of doors and locks etc.

Further source details (SPS 439)

The notes below connect the layout and example above to the game's 68000 routines.

Data locations in SPS 439

These are file offsets in the original executable, not runtime addresses. Runtime addresses in the original disassembly are file offsets plus $384.

Tower Original source label Map file offset Extracted resource
The Keep MapData1 $EBBC maps/mod0.map
Serpent Tower MapData2 $FFBE maps/serp.map
Moon Tower MaoData3 $113C0 maps/moon.map
Dragon Tower MapData4 $127C2 maps/drag.map
Chaos Tower MapData5 $13BC4 maps/chaos.map
Zendik's Tower MapData6 $14FC6 maps/zendik.map

MaoData3 is the original source spelling; its existing relabel is MapData3. These offsets must not be reused for another executable profile.

Header sizes and coordinates

All words are big-endian. Floor indices are zero-based, from 0 to 7.

Offset Size Meaning
$00 8 bytes Width of each floor
$08 8 bytes Height of each floor
$10 8 words Floor cell-data byte offsets, relative to the payload at map + $38
$20 8 bytes Floor X alignments
$28 8 bytes Floor Y alignments
$30 1 word Legacy/bootstrap width
$32 1 word Legacy/bootstrap height
$34 1 word Legacy floor-data offset copied by the AMOS “special floor” command
$36 1 word Index of the top floor; a top index of 5 describes six floors
$38 $FC8 bytes Map-cell payload, including unused capacity

For local coordinates (x, y) on floor f:

cell byte offset = floor_data_offset[f] + 2 * (y * width[f] + x)
cell address     = map_base + $38 + cell byte offset

The floor-data offsets do not include the header. Each floor occupies 2 * width * height bytes. The stock allocation accommodates at most 2,020 cells across all floors.

Example: The Keep

The first six floor dimensions are 12×1, 21×21, 15×15, 31×31, 19×19 and 4×5. Their payload offsets are $0000, $0018, $038A, $054C, $0CCE, $0FA0. They consume exactly $FC8 bytes. The header top-floor index is 5.

The bootstrap triplet is width 21, height 21, offset $0018: it duplicates floor 1. This duplication does not designate an entrance or a particular player.

How floors line up

Each floor has its own X/Y coordinates. Adding its alignment tells us where that cell sits relative to the other floors:

world_x = local_x + alignment_x[f]
world_y = local_y + alignment_y[f]

new_local_x = old_local_x + alignment_x[old_floor] - alignment_x[new_floor]
new_local_y = old_local_y + alignment_y[old_floor] - alignment_y[new_floor]

The coordinate-rebasing routine at original adrCd0084BA implements these differences. Its coordinate-component operations are byte-sized; valid map design must keep destinations in range rather than relying on arithmetic wraparound.

A pit fall preserves world X/Y and decreases the floor by one. A stair transition rebases the coordinates and then adds the travel vector twice. See Stairs and Pads, Triggers & Holes for the distinction between ordinary alignment conventions and checks actually enforced by the game.

The working header and the “special floor”

The tower selector at original adrCd000B68 copies fourteen longwords of header data, not fourteen map pointers, to the working cache at adrEA00EE40. It stores the resulting cell-data pointer at adrL_00EE78.

The copied width and height at header +$30 and +$32 initially occupy the active geometry words. Normal floor selection at adrCd0084D6 / adrCd0084DA subsequently loads width, height and data offset from the eight-floor arrays. The active data-offset word at adrW_00EE76 overwrites the cached copy of the header's top-floor word.

The AMOS _EDITKEYS5 “special floor” action copies the viewed floor's width, height and offset into header +$30, +$32, and +$34. No direct SPS 439 instruction reader of the copied +$34 offset was identified. The complete header is still copied, including those bytes; their later purpose has not been established.

This triplet is not the active player's floor and is not an entrance/exit coordinate. Each player has a separate floor word at player-record +$58. The action loop loads the first player's geometry and then the second player's geometry separately. The players can therefore be on different floors while sharing one working geometry cache. See Player Location Data.

Block sizes and source addresses

Two original object-access paths use the displacement $0FCA from the cell-data base:

$0FCA = $1000 map allocation - $38 header + $02 object-length word

Thus the map allocation is encoded in instructions even though those instructions do not spell it as $1000. Changing it requires both access paths to agree.

LevelDataOffsets contains six assembled word differences from MapData1. Because these differences are assembled from labels, they follow changes to the source layout. The selector adds a signed word displacement; the stock last-tower displacement is $640A. An enlarged layout must still fit that addressing mode or deliberately replace it.

The initial cell-data pointer at adrL_00EE78 is MapData1 + $38 (original value $0000EF78). Symbolic assembly of that expression preserves the stock bytes while allowing the pointer to follow relocated map data.

Making a map or object block larger moves the data that follows it; it cannot simply overwrite the next block in the original executable. A source rebuild must also account for the 14-bit object-location byte offset, word-sized map offsets, coordinate limits, other signed/short addressing, save-data layout, and any dependent working buffers. Replacing one size constant does not establish that all these constraints have been satisfied. See Object Location Data Structure.

Movement lookup

The 16 bytes at original adrEA005794 (existing relabel MovementOffsetTable) contain eight signed X deltas followed by eight signed Y deltas:

Direction index Direction X Y
0 North 0 -1
1 East 1 0
2 South 0 1
3 West -1 0
4 North-east 1 -1
5 South-east 1 1
6 South-west -1 1
7 North-west -1 -1

The coordinate-step and stair routines use the cardinal entries. Stair travel applies the chosen X and Y delta twice.

Source evidence

The SPS 439 definitions above are grounded in the original disassembly: tower/header selection at adrCd000B68, coordinate conversion and floor selection at CoordToMap and adrCd0084BA–adrCd00850C, stairs at adrCd006ED0–adrCd006F38, pits at adrCd004CB2, and object access at adrCd000960 and adrCd005F5C. The AMOS source documents the original Layout controls but does not override the game instruction behaviour.