King's Bounty Index Tables
As indicated in King's Bounty Saved game Format, some of the values that define King's Bounty's world and mission are hardcoded within the main executable (KB.EXE). Specifically, assuming
constexpr int kAlphabet = 26; // 'Z' - 'A' + 1 constexpr int kAlphaExt = kAlphabet + 1; // extra element at the end is the king's castle
the following index tables exist (sample locations provided):
Offset 1990 | Data Type | Description |
---|---|---|
0x183f8 | BYTE x[kAlphaExt], y[kAlphaExt] | Castle gate locations (27 X values, then 27 Y values) |
0x1867d | BYTE c[kAlphabet] | Continent of the castle (0-3).[1] 26 values (the king's continent is always 0) |
0x165cb | BYTE c[kAlphabet] | An identical copy of the above, with unclear purpose.[2] |
0x169d6 | BYTE c[kAlphabet] | A THIRD identical copy of the above, used e.g. for villain contracts. |
0x18481 | BYTE x[kAlphabet], y[kAlphabet] | Town square coordinates (26 X values, then 26 Y values), *in corresponding castle order*.[3] |
0x1852d | BYTE x[kAlphabet], y[kAlphabet] | Harbor coordinates (where the boat appears); 26 X values, then 26 Y values |
0x18649 | BYTE x[kAlphabet], y[kAlphabet] | "Airport" coordinates (where Town Gate lands the hero); 26 X values, then 26 Y values |
0x18697 | BYTE m[kAlphabet] | town letter -> castle letter mapping for Town Gate (26 indices). E.g. m[0] (town of A-Anomaly) is 0x3 (castle of D-Duvock). |
Notes
- ↑ Used for Castle Gate and for castle entry detection.
- ↑ Possibly a rudiment of an early design allowing town placement on a different continent from its castle?
- ↑ The game treats castle-town pairs as, essentially, castle objects with town properties. All town data, except (obviously) for the Town Gate lookup table, are arranged in castle order, i.e. Riverton is #0 because of Azram, Underfoot is #1 because of Basefit, etc. Even the town name strings appear in the file in corresponding castle alphabetical order (Riverton, Underfoot...) instead of their own alphabetical order (Anomaly, Bayview...)
Obviously, all of the offsets apply to the uncompressed file.
The Castle Gate spell always places the hero south of the actual castle gate (the Y coordinate is less by 1).
Stepping on a Castle Gate tile (0x85) that does not correspond to a recognized castle freezes the game.
Stepping on a Town tile (0x8a) that does not correspond to a recognized town immediately returns the hero one step back.
Stepping onto a registered map location (one that's present in the tables) that's not marked by a corresponding interactive tile (town walls or castle gate) on the map does not by itself result in an interaction. In other words, coordinate matching only occurs after tile code matching.
Original geography
The offsets to index tables vary between released game builds. It is, however, possible to locate those tables in the game file by value by using the original castle and town coordinates to build canonical representations of the original index tables and then searching for representative byte sequences.
Modena (the KB modding tool) uses the following castle/town coordinates (the first pair being the castle entry, the second the town square, the first directional vector offset to the "Rent Boat" harbor and the second offset to the Town Gate "airfield"):
{{30, 27}, {{29, 12}, se, ss}}, {{47, 6}, {{58, 4}, ne, ww}}, {{36, 49}, {{38, 50}, ee, ss}}, {{30, 18}, {{34, 23}, ss, ee}}, {{11, 46}, {{5, 50}, sw, ss}}, {{22, 49}, {{17, 44}, ee, ww}}, {{41, 36}, {{13, 60}, ee, ww}}, {{43, 27}, {{9, 39}, se, ss}}, {{11, 30}, {{14, 27}, ss, ww}}, {{41, 34}, {{58, 33}, ss, ww}}, {{57, 58}, {{51, 28}, ss, nn}}, {{52, 57}, {{57, 57}, se, ss}}, {{25, 39}, {{3, 37}, sw, ss}}, {{22, 24}, {{17, 21}, ee, ww}}, {{6, 57}, {{41, 58}, ss, ww}}, {{58, 23}, {{50, 13}, sw, nn}}, {{42, 56}, {{58, 60}, ee, ss}}, {{54, 6}, {{57, 5}, sw, ww}}, {{17, 39}, {{9, 60}, ee, ss}}, {{9, 18}, {{13, 7}, ww, nn}}, {{41, 12}, {{7, 3}, ee, ww}}, {{40, 5}, {{12, 3}, ww, nn}}, {{40, 41}, {{46, 35}, ne, ee}}, {{45, 6}, {{49, 8}, ne, ee}}, {{19, 19}, {{3, 8}, nw, nn}}, {{46, 43}, {{58, 48}, ee, ss}}, {{11, 7}, {}} // Washington, D.C.
...and the following castle/continent breakdown:
C-index | Continent | Castles | Castle initials (for Info) |
---|---|---|---|
0 | Continentia | 11 | ACFIKNOPRVW |
1 | Forestria | 6 | BDJMQY |
2 | Archipelia | 6 | EGHLTX |
3 | Saharia | 3 | SUZ |
= | (total) | 26 |
You can use the ham (name shortened from "Hamming") tool from the Royal Reward project to analyze a binary file. Sample output:
conts: 0x165cb # (hamming=0 confidence=100.00%) conts: 0x169d6 # (hamming=0 confidence=100.00%) conts: 0x1867d # (hamming=0 confidence=100.00%) forts[X]: 0x183f8 # (hamming=0 confidence=100.00%) forts[Y]: 0x18413 # (hamming=0 confidence=100.00%) ports[X]: 0x18481 # (hamming=0 confidence=100.00%) ports[Y]: 0x1849b # (hamming=0 confidence=100.00%) p_bay[X]: 0x1852d # (hamming=0 confidence=100.00%) p_bay[Y]: 0x18547 # (hamming=0 confidence=100.00%) p_air[X]: 0x18649 # (hamming=0 confidence=100.00%) p_air[Y]: 0x18663 # (hamming=0 confidence=100.00%) ptofs: 0x18697 # (hamming=0 confidence=100.00%)
The tool has been tested on three different versions of KB.EXE and successfully located all the 6 kinds of lookup tables (4 of them broken down by X and Y) in each.
By editing all the correlated tables simultaneously it is then possible to move the castles and towns both within and across continents. However, placement of a castle and its corresponding town on two different continents has not yet been achieved and is most probably impossible.