
I couldn't miss the chance to practice my drawing!
There exists a bug in the damage formulas of certain weapons in Final Fantasy V.
The agility stat is meant to play a major role in the damage formula of most weapons belonging to certain classes (bells, boomerangs, bows, knives, short swords, throwables, and whips). However, only the damage formulas of bells, throwables, and the Chicken Knife implement that properly.
The damage formula[]
The general damage formula for weapons in Final Fantasy V is:
where:
- Damage is the damage output of a regular attack performed with the equipped weapon.
- A is a non-negative integer value that mainly depends on the nominal attack (Atk) value of the given weapon.
- D is a non-negative integer value that mainly depends on either the physical defense (Def) or magic defense (MDef) stat of the target.
- M (also known as the multiplier) is a strictly positive integer value that mainly depends on the attacker's level and some combination of the attacker's strength, magic, and agility stats. The attacker's level is capped at 99 (i.e., levels 100~255, achievable with Drink/Mix still count as 99).
- the min() and max() functions enforce the damage ceiling (9999), and the damage floor (0).
With limited exceptions, boomerangs, bows, knives, short swords, and whips share the same sub-formulas for A, D, and M:
where // represents the integer division operation (i.e., taking the quotient ignoring the remainder), and % represents the modulo operation (i.e., taking the remainder ignoring the quotient).
The formula for M is bifurcated: the first one represents the original intention of the developers, and the second one represents the actual implementation, including the bug. In that regard, the % 256 reduction is purely a mathematical tool to represent the bug. The likely reason for the existence of the bug can be found here.
The bug[]
As Level and Agility are capped at 99 for the purpose of the damage formula, the upper limit of their product is 9801, which is in the [256, 65535] range (both inclusive). Every unsigned number in that range:
- Cannot be represented in binary using fewer than 2 bytes.
- Can always be represented in binary using 2 bytes.
Therefore, to consistently be able to represent in binary all possible (Level * Agility) products, 2 bytes (16 bits) are needed.
The bug consists in only the value encoded by the least significant byte of (Level * Agility) being used in the damage formula, while the most significant byte is ignored. Mathematically, this is equivalent to reducing (Level * Agility) modulo 256.
Since, by definition, any value modulo 256 must be in the [0, 255] range (both inclusive), when dividing it by 128, the only two possible integer quotients are 0 and 1. Therefore, the impact of the agility stat on the multiplier formula (and, thus, on the overall damage formula) is very limited.
Consequences[]
The primary consequence of the bug is that the agility stat can only add 0 or 1 to the damage formula multiplier. Therefore, the contribution of the agility stat to the final damage becomes almost negligible. If the bug was not there, the maximum additive impact on the multiplier would be (99 * 99) // 128 = 76, which makes the actual impact of the agility stat even more underwhelming.
Additionally, it is possible for a character attacking with an affected weapon to deal less damage after a level up, everything else being equal. That is the case whenever M decreases after a level up (i.e., from Level to Level + 1):
The easiest scenario in which the above inequality is satisfied (due to the agility bug) is when:
In other words, the additive contribution of the Strength stat to the multipler does not increase, and the bugged contribution of the Agility stat decreases from 1 to 0. For instance, considering a character with 55 strength and 50 agility before and after leveling up from 10 to 11:
Exceptions[]
Certain bells and the shuriken-type throwables also feature the agility stat in the multiplier M of their damage formula. However, in those cases, both bytes of Level * Agility are correctly used. Additionally, within the weapon classes that are affected by the bug, there are some exceptions:
- The Twin Lance and the Man-Eater use the spears' damage formula, which does not feature agility.
- The Magic Bow deals no damage by design. As such, it does not use a damage formula.
- The Chicken Knife uses a unique damage formula, which does not feature the bug in the multiplier. As such, it is considerably stronger than all other knives, and deals more damage than any other weapon in the game in most practical scenarios.
Programming explanation[]
Many programming languages allow or force programmers to select an explicit type when declaring variables. In the case of unsigned integer variables (i.e., variables that are supposed to hold unsigned integer values) the type is used by the compiler/interpreter software and the Operating System to determine how many bytes to allocate for such variables. For instance:
uint32_t a_var; // The value of a_var is treated as an unsigned 4-byte integer.
uint16_t b_var; // The value of b_var is treated as an unsigned 2-byte integer.
uint8_t c_var; // The value of c_var is treated as an unsigned 1-byte integer.
If the variable that is supposed to hold the result of the (Level * Agility) expression is explicitly declared as uint8_t rather than uint16_t, only 1 byte is available to store such result. Therefore, only the least significant byte is stored, and the rest is lost.