Data Structures¶
This chapter details the various data structures used by the game.
Events¶
Caution
This section is a draft based on current reverse engineering work. Some of the information may be inaccurate or incomplete.
The game uses a simple virtual machine for scripting event sequences. The data discovered for this so far exists in bank 12 ($12:XXXX on the system bus). There are 256 event numbers. $12:8000 to $12:8200 is a series of 256 2-byte indexes. These indexes subsequently indicate the beginning of each event’s script in the data array extending from $12:8200 to $12:EFFF. The event commands have variable lengths depending on the nature of the command.
$FA: Play Song¶
Bytes: | 2 |
---|---|
Parameter 1: | Desired track number. |
This instruction causes to play the song identified by the given track number.
$FE: Warp to Map¶
Bytes: | ? |
---|---|
Parameter 1: | Target map ID. |
Parameter 2: | %DDXXXXXX where DD is the target direction and XXXXXX is the target X coordinate. |
Parameter 3: | Target Y coordinate. |
Parameter 4: | %P??????? where P is the target map plane. |
Map IDs from $00 to $FA act as expected and transport the player to that map. Maps $FB to $FF have special behavior:
$FF: End Event¶
Bytes: | 1 |
---|
This instruction ends the event. It automatically makes sure the visible player field sprite corresponds with the active character.
Character Records¶
Character records are stored in two separate areas in RAM. The first set is made up of five 64-byte records located at $7E1000. The second set of is five 128-byte records located at $7E2000. The second set of records is used in-battle and is a strict superset of the first set. The individual bytes can be interpreted as follows:
$00: Character Byte 1¶
Bitmask: | %RL?CCCCC |
---|---|
R: | Right-hand weapons |
L: | Left-hand weapons |
C: | Character ID |
The lowest five bits define the character ID. Therefore, the game supports up to \(2^5\) or 32 unique character IDs. One of these is reserved for indicating an empty slot. Each instance of a character has its own ID. (For example, Kain leaves the party and rejoins twice. All three instances of Kain have a separate ID. The values used in game are as follows:
ID | Character |
---|---|
$00 | <empty slot> |
$01 | Cecil (dark knight) |
$02 | Kain (until Mist) |
$03 | Rydia (child) |
$04 | Tellah (until Damcyan) |
$05 | Edward (until Leviatan) |
$06 | Rosa (until Fabul) |
$07 | Yang (until Leviatan) |
$08 | Palom |
$09 | Porom |
$0A | Tellah (until Cecil becomes a Paladin) |
$0B | Cecil (paladin) |
$0C | Tellah (until Tower of Zot cutscene) |
$0D | Yang (until Super Cannon) |
$0E | Cid |
$0F | Kain (until Sealed Cave) |
$10 | Rosa (final) |
$11 | Rydia (adult) |
$12 | Edge |
$13 | FuSoYa |
$14 | Kain (final) |
$15 | Golbez |
$16 | Anna |
The two highest bits determine the character’s handedness. If the uppermost bit is set, the character can equip weapons on their right hand, and if the second highest bit is set, they can equip weapons on their left hand.
The remaining bit’s function is currently unknown.
$01: Character Byte 2¶
Bitmask: | %B?L?SSSS |
---|---|
B: | Back row |
L: | Long range |
S: | Sprite/Class |
The lowest four bits determine the sprite and class of the character. These two properties are inextricably tied together and are defined using one value. The known values are as follows:
ID | Sprite | Class |
---|---|---|
$00 | Cecil (dark knight) | DKnight |
$01 | Kain | Dragoon |
$02 | Rydia (child) | Caller |
$03 | Tellah | Sage |
$04 | Edward | Bard |
$05 | Rosa | Wh.Wiz |
$06 | Yang | Karate |
$07 | Palom | Bl.Wiz |
$08 | Porom | Wh.Wiz |
$09 | Cecil (paladin) | Paladin |
$0A | Cid | Chief |
$0B | Rydia (adult) | Caller |
$0C | Edge | Ninja |
$0D | FuSoYa | Lunar |
$0E | *Various | <garbage> |
$0F | *Golbez | <garbage> |
$0E appears to be garbage data and is not actually used in the game. The portrait is that of a solid black pig, the overworld sprite is a mini and the in-battle sprite is a green pig. $0F provides an in-battle sprite of Golbez, but the rest of the data is not usefully defined.
Bit 5 is the so-called long range bit, which determines whether or not the character is capable of performing long range attacks without having their accuracy reduced. This bit is especially notable because the game has a bug that results in this bit never being reset. In other words, once a character equips a back row weapon (and the game is given an opportunity to recalculate stats), the character will retain that status forever.
Bit 7 is the back row bit and is set when the character is in the back row and unset when the character is in the front row. This does not affect the display of party members and appears to be used only for calculations.
The functions of the remaining two bits are currently unknown.
$03-$06: Status¶
Bitmask: | %SSSSSSSS %SSSSSSSS %SSSSSSSS %SSSSSSSS |
---|---|
S: | Status effects |
These bytes encode the following status effects:
Byte | Bit | Hex | Binary | In-Game | Description |
---|---|---|---|---|---|
$03 | 7 | $80 | %10000000 | Swoon | Swoon/Death |
$03 | 6 | $40 | %01000000 | Stone | Stone |
$03 | 5 | $20 | %00100000 | Toad | Toad |
$03 | 4 | $10 | %00010000 | Small | Small/Mini |
$03 | 3 | $08 | %00001000 | Pig | Pig |
$03 | 2 | $04 | %00000100 | Mute | Mute/Silence |
$03 | 1 | $02 | %00000010 | Darkness | Darkness/Blindness |
$03 | 0 | $01 | %00000001 | Poison | Poison |
Byte | Bit | Hex | Binary | In-Game | Description |
---|---|---|---|---|---|
$04 | 7 | $80 | %10000000 | Curse | Curse |
$04 | 6 | $40 | %01000000 | Float | Float |
$04 | 5 | $20 | %00100000 | Paralyze | Paralysis |
$04 | 4 | $10 | %00010000 | Sleep | Sleep |
$04 | 3 | $08 | %00001000 | Charm | Charm/Confuse |
$04 | 2 | $04 | %00000100 | Berserk | Berserk |
$04 | 1 | $02 | %00000010 | Petrify | Gradual petrification (2/3) |
$04 | 0 | $01 | %00000001 | D | Gradual petrification (1/3) |
Byte | Bit | Hex | Binary | In-Game | Description |
---|---|---|---|---|---|
$05 | 7 | $80 | %10000000 | D | Magnetized |
$05 | 6 | $40 | %01000000 | Stop | Stop |
$05 | 5 | $20 | %00100000 | <unknown> | |
$05 | 4 | $10 | %00010000 | <unknown> | |
$05 | 3 | $08 | %00001000 | <unknown> | |
$05 | 2 | $04 | %00000100 | <unknown> | |
$05 | 1 | $02 | %00000010 | <unknown> | |
$05 | 0 | $01 | %00000001 | Count | Count/Doom |
Byte | Bit | Hex | Binary | In-Game | Description |
---|---|---|---|---|---|
$06 | 7 | $80 | %10000000 | <unknown> | |
$06 | 6 | $40 | %01000000 | <unknown> | |
$06 | 5 | $20 | %00100000 | Wall | Wall/Reflect |
$06 | 4 | $10 | %00010000 | Barrier | Barrier |
$06 | 3 | $08 | %00001000 | Image (two hits) | |
$06 | 2 | $04 | %00000100 | Image (one hit) | |
$06 | 1 | $02 | %00000010 | <unknown> | |
$06 | 0 | $01 | %00000001 | HP Critical |
$07-$08: Current HP¶
These two bytes contain the character’s current HP, encoded in low-endian format. (In other words, the first byte is the low byte and the second byte is the high byte.
$09-$0A: Maximum HP¶
These two bytes contain the chracter’s maximum HP, encoded in low-endian format.
$0B-$0C: Current MP¶
These two bytes contain the character’s current MP, encoded in low-endian format.
$0D-$0E: Maximum MP¶
These two bytes contain the character’s maximum MP, encoded in low-endian format.
$0F: Base Strength¶
The character’s base strength.
$10: Base Agility¶
The character’s base agility.
$11: Base Vitality¶
The character’s base vitality.
$12: Base Wisdom¶
The character’s base wisdom.
$13: Base Will¶
The character’s base will.
$14: Strength¶
The character’s base strength plus any bonuses from their equipment. If the value is $B6 or greater (essentially -74 to -1, as this is signed), the value is replaced with 1. This almost sets up a lower bound for the stat at 1, but 0 itself is allowed to pass through.
The upper bound is set to 99.
$15: Agility¶
(calculated the same as strength)
$16: Vitality¶
(calculated the same as strength)
$17: Wisdom¶
(calculated the same as strength)
$18: Will¶
(calculated the same as strength)
$19: Elemental Attack¶
These are the elements associated with the character’s physical attack. It is set as the union of the attack elements of their weapons.
$1A: Racial Attack¶
The character’s physical attack will do extra damage against the races indicated in this variable. It is determined by the union of the race property of each of their weapons.
$1B: Physical Attack Multiplier¶
This is calculated with the following formula:
strength // 8 + agility // 16 + 1
$1C: Physical Attack Accuracy¶
There are a number of ways this might be calculated, depending on what the character has equipped:
- No Weapons
- 50 + level // 4
- One Weapon or Bow+Arrow
- (weapon or bow accuracy) + level // 4
- Two Weapons
- (level // 4 + level // 4 + sum(weapon accuracies)) // 2
If only a bow or only an arrow is equipped, they are ignored.
This value is capped at 99.
$1D: Physical Attack Base¶
Like with accuracy, there are a few possibilities:
- Yang (specifically class is Karate)
- level * 2 + strength // 4 + 2
- Bow+Arrow
- bow_power // 2 + arrow_power + strength // 4
- No Weapon
- level // 4 + strength // 4
- One Weapon
- level // 4 + strength // 4 + weapon_power
- Two Weapons
- (level // 4 + strength // 4) * 2 + sum(weapon powers)
If the player has a bow and arrow equipped and the bow is in the primary hand, then the value is modified as follows:
value = value - (value // 5)
If only a bow or only an arrow is equipped, it is treated as a single weapon with a power of 1.
This value is capped at 255.
$1E-$1F: Physical Attack Status¶
This determines the status that physical attacks potentially carry. It is set to the union of the status property of the character’s weapons.
$20: Elemental Weakness¶
This byte controls the elements the character is weak to. This is set by taking the opposite of the elements they are resistant to. The only pairs of opposite elements are fire/ice and holy/darkness. Lightning and the pseudo-elements do not have opposites.
For example, if the character resists fire and holy, they will have a weakness to ice and darkness.
$21: Elemental Strong Weakness¶
This byte determines which elements the character is strongly weak too. This results in double the damage, versus a regular weakness. This value is set as the opposite of any elemental immunities the character has.
This property is bugged. Once it is set, it will never be unset as the game has no code to do so. Immunities and resistances take precedence over weaknesses in general, so as long as that armor is equipped, there will be no problem. However, once the character removes the equipment that gave the immunity, they will retain a permanent strong weakness unless they re-equip the armor.
$22: Magical Defense Multiplier¶
This is calculated using the following formula:
(wisdom + will) // 32 + (agility // 32)
$23: Magical Defense Evasion¶
This is calculated using the following formula:
(wisdom + will) // 8 + sum(equipment magic evasion)
This is capped at 99.
$24: Magical Defense Base¶
This is calculated as the sum of the magical defense of the character’s equipment.
It is capped at 255.
$25: Elemental Resistance¶
The character resists these elements and takes reduced damage. This is calculated by combining any resistances from all equipped equipment.
$26: Elemental Immunity¶
The character is completely immune to these elements. This is calculated by combining any immunities from all equipped equipment.
$27: Race Resistance¶
The character will take reduced damage from attacks by the races encoded in this byte. Again, this is calculated by combining any race resistance properties from the character’s equipment.
$28: Physical Defense Multiplier¶
This is calculated according to the following formula:
\(\left \lfloor \frac{level}{16} \right \rfloor \cdot shields + \left \lfloor \frac{agility}{8} \right \rfloor\)
$29: Physical Defense Evasion¶
This is set to the sum of the physical evade values of all the character’s equipment. An empty armor slot is considered to have an evade of 10. An empty hand does not confer this bonus.
This value is capped at 99.
$2A: Physical Defense Base¶
This is calculated as the sum of the physical defense of all the character’s equipment, plus half their vitality.
This value is capped at 255.
$2B-$2C: Status Immunity¶
These two bytes determine which statuses the character is immune to. It is determined by combining the status immunities of all the character’s equipment.
$2D: Critical Rate¶
This is the rate at which a character does critical hits. It is derived from the character’s base critical rate as follows:
- No Weapon or Two Weapons
- Set to base_critical_rate
- One Weapon
- base_critical_rate * 2
- Bow+Arrow
- base_critical_rate * 3
This value is mostly capped at 99. Technically, in the single weapon case, it is only capped if the base value is 128 or greater.
This value and the next value are an exception to the records at $2000 being a strict superset of the records at $1000. In particular, these values in the records at $1000 are always the base values. The derived values are only stored in the in-battle records at $2000.
$2E: Critical Bonus¶
This is a bonus applied to critical hits. It is derived from the character’s base critical bonus as follows:
- Bow+Arrow
- base_critical_bonus + arrow_power
- One Weapon
- base_critical_bonus + weapon_power // 2
- Two Weapons or No Weapon
- base_critical_bonus
This value is capped at 255.