| 
  • If you are citizen of an European Union member nation, you may not use this service unless you are at least 16 years old.

View
 

The Adventure Map Data Structure

Page history last edited by Kert 5 years, 1 month ago Saved with comment

 

 

 

This wiki article was created largely as an extended answer to someone who asked about the format. It is still very incomplete, with no imminent plans to expand it. Talking to senior Ironfist team members remains the best way to learn more.

 

Overview

 

The main data structures in question are fullMap, SMapHeader, mapCell, and mapCellExtra.  The raw reverse-engineered versions of the data structures are available in HEROES2W.h and EDITOR2W.h, but prettier versions are available in map.h . Some data structures are slightly different between the editor and the game; the editor's versions have extra data to support deleting.

 

Key concepts

 

  • You know how in the map editor you can select a whole town or mountain object and place it as one? Well, in the game, there's no such concept of a larger object. All there is a bunch of tiles, each of which act independently. You can take the top-left corner of the Necromancer castle and put it in the middle of the ocean next to the bottom-right corner of a sulfur mine, and you can make it so that the ocean square next to it acts like a Faerie Ring.
    • However, the concept of a larger object does exist in the editor, called an "overlay".  In the editor, each mapCell and mapCellExtra has extra fields so that, when you delete one tile, it also deletes all other tiles placed at the same time.
  • The data structures make heavy use of bitfields. E.g.: there is a 1-bit field in mapCell declared  "unsigned int isShadow 1;". However, in the raw reverse-engineered data structure, it appears as part of a larger 

    field_4_1_1_isShadow_1_13_extraInfo field  ("1 bit unknown, 1 bit isShadow, 1 bit unknown, 13 bits extraInfo"). All decompiled code that accesses the isShadow field will do so using bitops.

  • The mapCell field contains places to store the info of one object on that cell. However, cells may contain many overlapping objects. A single cell may contain a sulfur mine, the mountain it sits on, a visiting hero, the shadow of an object next to it, and the top part of a tall building on a cell beneath it. To support this, each cell also contains an embedded linked list of "cell extras," which store this information for other objects. 

  • Some cells have extra gameplay information associated with them, e.g.: the cell with the entrance to a town is linked to the town object, a sign has text, etc. These are stored in an additional object, confusingly also called an "extra." (Yes, these names were used by the original game developers.)  These are stored in the field extraInfo as an offset into the global array ppMapExtra, which contains these Extra objects. You can browse map.h to see a list of the various Extra objects (e.g.: TownExtra, SignExtra).

 

 

Information about specific fields 

 

  • mapCell->objType contains 8 bits: 7 bits are the location type (enum ADVENTURE_MAP_LOCATION); the 8th bit is "TILE_HAS_EVENT", and is only marked on the interactive tile  (e.g.: all squares of a mine may have LOCATION_MINE, but only the flaggable square has LOCATION_MINE | TILE_HAS_EVENT). Note that, since Price of Loyalty features more than 126 distinct location types, several of them are actually stored under "LOCATION_ALCHEMIST_TOWER," with extraInfo used to disambiguate. (These are: Arena, Mermainds, Seer's Hut / Eyes, Sirens, and Stable.) See advManager::GenericSiteEvent for details. 
  • mapCell->objTileset is a value of enum OBJ_TILESET, which is an index into the gTilesetFiles array. 

 

mapCell/mapCellExtra fields conversion tables

 

mapCell

 

mapCell decompiled   mapCell ironfist
Name Size (bits) Bit indexes   Name Size (bits) Bit indexes
groundIndex 16 1-16   groundIndex 16 1-16
objTileset 8 17-24   hasObject 1 17
objectIndex 8 25-32   isRoad 1 18
extraInfo 16 33-48   objTileset 6 19-24
overlayTileset 8 49-56   objectIndex 8 25-32
overlayIndex 8 57-64   field_4_1 1 33
displayFlags 8 65-72   isShadow 1 34
objType 8 73-80   field_4_3 1 35
extraIdx 16 81-96   extraInfo 13 36-48
        hasOverlay 1 49
        hasLateOverlay 1 50
        overlayTileset 6 51-56
        overlayIndex 8 57-64
        flags 8 65-72
        objType 8 73-80
        extraIdx 16 81-96

 

Conversion table

mapCell decompiled mapCell ironfist
extraInfo & 1 field_4_1
(extraInfo >> 1) & 1 isShadow
(extraInfo >> 2) & 1 field_4_3
(extraInfo >> 8) >> -5 extraInfo
objTileset & 1 hasObject
(objTileset >> 2) & 0x3F objTileset
overlayTileset & 1 hasOverlay
(overlayTileset >> 1) & 1 hasLateOverlay
(overlayTileset >> 2) & 0x3F overlayTileset

 

It's also very important to use casts for these fields

(unsigned char)overlayIndex

(unsigned char)objectIndex

 

mapCellExtra

 

mapCellExtra decompiled   mapCellExtra ironfist
Name Size (bits) Bit indexes   Name Size (bits) Bit indexes
nextIdx 16 1-16   nextIdx 16 1-16
_1_q_7_objTileset 8 17-24   animatedObject 1 17
objectIndex 8 25-32   objTileset 7 18-24
field_4_1_1_1_isShadow_5 8 33-40   objectIndex 8 25-32
_1_q_1_hasLateOverlay_6_q 8 41-48   field_4_1 1 33
field_6 8 49-56   field_4_2 1 34
        field_4_3 1 35
        field_4_4 5 36-40
        animatedLateOverlay 1 41
        hasLateOverlay 1 42
        tileset 6 43-48
        overlayIndex 8 49-56

 

Conversion table

mapCellExtra decompiled mapCellExtra ironfist
_1_q_7_objTileset & 1 animatedObject
(_1_q_7_objTileset >> 1) & 0x7F objTileset
_1_q_1_hasLateOverlay_6_q & 1 animatedLateOverlay
(_1_q_1_hasLateOverlay_6_q >> 1) & 1 hasLateOverlay
(_1_q_1_hasLateOverlay_6_q >> 2) & 0x3F tileset
field_4_1_1_1_isShadow_5 & 1 field_4_1
(field_4_1_1_1_isShadow_5 >> 1) & 1 field_4_2
(field_4_1_1_1_isShadow_5 >> 2) & 1 field_4_3

 

 

Comments (0)

You don't have permission to comment on this page.