Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Random numbers in Dragon Warrior III
- ====================================
- How the random number generator is seeded
- -----------------------------------------
- The random number generator is seeded from the save file checksum
- whenever a save file is saved (or checked, on reset -- note that while
- the files are checked in slot order, DW3 swaps the positions of save
- files to put the current file in the first slot, so the seed on reset is
- not necessarily from file #3). See the bottom of this file for details
- of how this works and how it can be abused to break the RNG. On reset
- with a fresh cart (backup RAM not initialized) or in which the file in
- the third slot is save file #3 and is empty, the initial seed when the
- main menu opens is $3D27.
- Random numbers in character creation
- ------------------------------------
- When creating characters in a new file:
- - If the character is not the hero, choose the name by taking the low 2
- bits of a random number as an index into a table of 4 predefined names
- (each character has a different list of 4 possible names).
- - Take the predefined base values for the character's class and add the
- low bit of a random number to each (in the order Luck, Intelligence,
- Vitality, Agility, Strength).
- - The file is then checksummed (after other non-random initialization
- like inventory setup), resetting the RNG seed.
- When creating a character at Luisa's Place 2F:
- - Store the predefined base stats for the selected character class.
- - Take a multi-random number in the range 0 through 4 (as in battles;
- see below) and add 2; this is the number of stats to increment.
- - For that number of times, take a multi-random number in the range 0
- through 4 and increment the stat selected by that index (in the order
- Strength, Agility, Vitality, Intelligence, Luck).
- - The file is checksummed after other character setup is complete.
- Random numbers on the field
- ---------------------------
- - When entering a new map, a random number is taken and discarded.
- - When an NPC is about to move, a random number is taken and the low 2
- bits are used to select the direction the NPC moves in. (NPCs move
- based on the global frame timer: NPCs 0 and 1 move on frames $00 and
- $80, NPCs 2 and 3 move on frames $10 and $90, etc. Movement is
- suppressed if the NPC is offscreen or in a different map room.)
- - Numbness healing while moving: For each numb character, take a random
- number at each step; numbness heals if the low 4 bits of the random
- number are zero.
- - While on the non-Alefgard overworld and neither the current tile nor
- the last 3 tiles are map entrances, every 64 frames (when the low 6
- bits of the $90 frame counter are zero):
- - Take a random number. If the low 3 bits are zero, take another
- random number.
- - Take another random number.
- - On the overworld, on each step (before checking for encounters), add
- ((seed & 31) + 223) / 256 to the time-of-day timer. The timer is an
- 8.8 fixed-point value with a period of 204; "night" is any value 120
- or greater. Note that this does not take a new random number from the
- RNG, but just uses the current seed value.
- - If in a zone (overworld) or map (town/dungeon) with enemies,
- encounters are checked for on each step (see $8222 in bank 0):
- - If in a low encounter rate area (16/256, 64/256, 128/256), take a
- random number and skip the encounter check if the number is greater
- than or equal to the /256 chance.
- - Multiply the encounter rate (1 for low encounter rate areas) by:
- - On the overworld:
- - Water: 4 during the day, 5 at night
- - Grass tiles: 10 during the day, 13 at night
- - Woods, desert, ice tiles: 15 during the day, 19 at night
- - Heavy forest, swamp tiles: 18 during the day, 22 at night
- - Hills: 25 during the day, 31 at night
- - Other tiles: encounter not possible, abort
- - In dungeons: 84 after a room transition, 10 elsewhere
- But set the encounter rate to 100 if any party member has the
- Golden Claw (equipped or not), or if the product is greater than
- 100. Then take a random number, and an encounter occurs if the
- random number is less than the encounter rate.
- - If an encounter occurs, use a random number to choose one of the 14
- encounters in the group (encounters are weighted based on tables,
- see 0/$90F3). If on the overworld, and:
- - during daytime, the chosen encounter is 4, 10, or 13; or
- - during nighttime, the chosen encounter is 0 or 6,
- or if the chosen encounter is $FF, repeat up to 98 more times, then
- abort the encounter.
- - Choose enemy groups:
- - If the encounter is index $00 through $04, then:
- - Add a group of 1 of the base enemy to the encounter.
- - For up to 19 tries, choose a random number from 0 through 5
- (using $C3F1, which repeatedly takes random numbers and masks
- off high bits [$F8, in this case] until the masked value is
- in range). If the number is not the same as the encounter
- index, the number is valid for the current day/night state
- (if on the overworld), the enemy ID of that encounter is not
- $FF, and that enemy has not been seen yet:
- - Add a group of that enemy (count 1) and mark it as seen.
- - Compare a random number against the chance of adding
- another group (this chance is part of the encounter data);
- if greater or equal, stop adding enemies.
- - Advance the additional-group-chance index, and if it
- reaches 2, stop adding enemies.
- - Choose a random number from 0 through 2. If it is zero,
- stop. Otherwise, choose another random number from 0 through
- the highest defined enemy group index (or 4, if both
- additional-group chances were consumed); if that group is not
- the encounter $05 enemy, add 1 to the group count. Then
- repeat this step.
- - If the encounter is index $05, add a group of 1 of the base
- enemy to the encounter; then choose a random number from 0
- through 4, and add 8 of that enemy to the encounter. If on the
- overworld and the selected encounter is invalid for the current
- day/night state, or if the selected encounter ID is $FF, repeat
- 18 more times, then skip adding a second group.
- - If the encounter is index $06 through $0A, choose a random
- number of enemies from the random range selected for the
- encounter and set that as the solitary enemy group's count.
- Note that if the random range is a power of 2 (e.g. 4-7), $C3F1
- masks off one bit too few, so the random number range check will
- fail half the time.
- - If the encounter is index $0B, there is no randomness (one fixed
- enemy in the encounter).
- - If the encounter is index $0C or $0D, then for each of 4 enemy
- groups, choose a random count from the random range defined for
- the group. This is performed even if the random range is 1
- (constant count) and even if the group does not exist in the
- encounter (enemy ID is $FF).
- Random numbers in battle (and damage calculations, etc.)
- ------------------------
- Aside from the regular RNG function ($C3B8, below just "random number"),
- the battle system uses several non-straightforward methods to get random
- numbers. I refer to them with the following terms (see the noted
- routine addresses in bank 4 for implementations).
- - "multi-random number" ($ACBE): a random number taken after discarding
- a count of numbers from the stream equal to the value of $6A68 plus 1;
- see also the note below about "why battle outcomes can differ even
- with the same RNG seed".
- - "multi-random number in a range" ($ACA3): a multi-random number
- multiplied by a range and divided by 256. For a range R, this gives
- values (approximately) evenly distributed from 0 through R-1 without
- needing to repeatedly take random numbers as with $C3F1.
- - "random-16 number" ($BA5C): a random number computed by summing the
- low 5 bits of 16 consecutive random seeds (each taken after discarding
- a random number from the stream) and subtracting 120, then repeating
- until the result is in the range [0,255]. This gives a result
- weighted toward 128, similar to how rolling two fair six-sided dice
- will result in a sum of 7 more often than 2 or 12.
- - "random-32 number" ($BA64): like random-16, but computed by summing
- the low 4 bits of 32 consecutive random seeds and subtracting 112.
- The last two methods have a theoretical risk of infinite-looping if the
- RNG gets stuck in a hole, since the counter ($00A4) which is normally
- added to the RNG seed when returning a random number is ignored.
- Fortunately, the two holes in the actual function used by the RNG land
- within the [0,255] range, so they're safe; seed $5FEA gives results of
- 40 for random-16 and 208 for random-32, while $AFF5 gives results of 216
- for random-16 and 48 for random-32.
- Battles proceed as follows (omitting some non-random logic for brevity):
- - At startup:
- - For each Shadow is in the party:
- - Take a random-16 number.
- - Let N be that number times the lead party member's level,
- divided by 100, plus one; if N is greater than 130, set it to
- 130.
- - Take a multi-random number in the range 0 through N-1, and set
- the effective enemy ID to that value.
- - Set HP for each enemy by subtracting a multi-random number in the
- range [0,(floor(base_HP/4)+1)%256) from the enemy's base HP. (The
- mod-256 is relevant only for Zoma, whose base HP is 1023 so HP/4+1
- overflows to zero.)
- - Choose a random party member as the focus target for each enemy
- group (regardless of whether the enemy's data has the focus-attacks
- flag set).
- - If the battle is not a non-interactive or preset battle:
- - If the preemptive/back type (copied to $49 from table at $8ADB
- in bank 0) is 1 and a random number is less than 32, the
- encounter is a back attack (1 free turn for enemies).
- - Otherwise, if the preemptive/back type is not 3 and a random
- number is less than 8, the encounter is a back attack.
- - Otherwise, if the preemptive/back type is 2 and a random number
- is less than 32, the encounter is a preemptive attack (1 free
- turn for the party).
- - Otherwise, if the preemptive/back type is not 3 and a random
- number is less than 8, the encounter is a preemptive attack.
- - At the beginning of each turn, for each of the 12 battle character
- slots (4 party member slots followed by 8 enemy slots), take a
- multi-random number in the range [agility/4,agility) to determine
- action order for that turn; higher values go sooner, and later slots
- win ties over earlier slots. All slots are processed, even if the
- slot is empty or the associated character or enemy is dead.
- - If not a non-interactive battle, then for each party member:
- - If the party is ironized, attempting to run, or in a back attack,
- skip.
- - If the character is asleep, dead, or numb, skip.
- - If the character is in BeDragon status, take a random number. If
- the number is 0, the character attacks the first enemy group; if
- not, the character chooses action 4 if the low bit of the number is
- 0, otherwise action 5. (Both of these actions are the breath
- attack; it may be that BeDragoned characters were originally
- intended to have two actions, but that was changed to just the
- breath attack during development.)
- - Otherwise, show the character's battle command menu and let the
- player enter a command.
- - If the player chooses Run on the first character's menu:
- - If the battle is a preset encounter, the attempt fails.
- - Otherwise, if in the first turn of a preemptive attack or if the
- lead party member's level is at least 10 levels higher than the
- highest enemy level in the encounter, the attempt succeeds.
- - Otherwise, take a random number; the attempt fails if the number
- is less than the chance selected by the number of previous
- failed run attempts (128, 128, 64, 0 -- so the fourth run
- attempt always succeeds).
- - If an interactive battle, then unless Run was chosen, take a random
- number and store the low 4 bits in $6A68 (the multi-random discard
- counter).
- - Choose actions for each enemy:
- - If the enemy is dead, do nothing.
- - If the AI type (see notes below about enemy data) is 2, do nothing
- (actions will be chosen when the enemy actually takes its turn).
- - Choose an action and target:
- - Choose an action:
- - If the action chance selector is 3 (preset action order),
- choose the next action in sequence.
- - Otherwise, take a multi-random number, and choose an action
- based on that number and the chance table for the action
- chance selector.
- - Choose a target for the action: (note that in non-interactive
- battles, party targets are still selected here but will be
- changed by the confusion handler at turn resolution)
- - If the AI type is 0:
- - If the action targets a party member or is "assess the
- situation", parry, flee, or "call for help (same enemy)",
- choose a random target from all living party members.
- - If the action is Vivify or Revive, choose a random target
- from all dead enemies.
- - Otherwise, choose an appropriate enemy target (logic
- omitted for brevity).
- - Otherwise:
- - If the action is a spell, then:
- - If the enemy does not have enough MP for the spell:
- - If the AI type is 2, choose no target (try another
- command).
- - Otherwise, if the enemy has already used the action
- in the current battle, choose no target.
- - Otherwise, mark the action as having been used and
- continue.
- - If the enemy is in StopSpell status and the AI type is
- 2, choose no target.
- - Choose a target based on the action:
- - If "assessing the situation", parrying, or using a
- damage breath attack, choose a dummy target to cause
- the command to be accepted.
- - If attacking:
- - If the enemy focuses attacks on a single character:
- - If the character is alive, choose that target.
- - Otherwise, if at least one other party member is
- alive, choose a random living party member.
- - Otherwise, choose the existing target.
- - Otherwise, for each living party member, take a
- multi-random number; if it is less than or equal to
- the cumulative probability at that party slot, that
- character is chosen as the target. (This is the
- only case in which party order makes a difference,
- aside from a very slightly greater probability to
- choose the last party member due to off-by-one and
- rounding errors in unweighted random selection.)
- - If fleeing, choose a dummy target if the enemy's level
- plus 5 is less than the lead party member's level;
- otherwise, choose no target. (Thus, enemies who have
- the Flee action will not use it unless their level is
- at least 6 lower than the lead party member's level.)
- - If casting a damage spell:
- - If the AI type is 2, choose a random living party
- member who is not in Bounce status.
- - Otherwise, choose a random living party member.
- - If casting Increase:
- - If the AI type is 1-3, choose a random enemy target
- whose defense is at least 768. (This appears to be
- a bug in the code; the branch targets after the
- defense check should probably have been switched.
- As a result, "smart AI" enemies will never use
- Increase unless a Metal Slime or Metal Babble is in
- the encounter.)
- - Otherwise (AI type 0), choose a random enemy target.
- - (Logic for other actions omitted for brevity.)
- - If no target could be chosen, retry the action choice, but for
- chance selectors other than 3, exclude the previously chosen
- action from the set of possible actions and spread its chance
- evenly among the chances for all remaining actions in the action
- list (rounding down, so that the last action gets all of the
- remainder).
- - If no target could be chosen for any action, set the action to
- a normal attack.
- - If the action count selector is 3, choose a second action:
- - If the action chance selector is 3, choose the next action in
- sequence, just like the first action.
- - Otherwise, take a multi-random number and choose an action as for
- the first one.
- - Choose a target for the action as for the first action.
- - If the action count selector is 1, take a random number, and if the
- low bit is 1, choose a second action.
- - If the action count selector is 2, take a random number. If the
- low 2 bits are 0, do nothing (only one action). Otherwise choose a
- second action, and if the low 2 bits of the random number were 1 or
- 2, choose a third action in the same manner as the second.
- - Resolve the turn. For each character (party or enemy) in turn order:
- - If a party member:
- - Skip the turn if the party is ironized or fleeing.
- - Skip the turn if the character is not present or dead.
- - If numb, print a numbness message and skip the turn.
- - If asleep, take a multi-random number and compare against the
- chance of waking for the number of sleep turns remaining (255,
- 128, 85, 32). If less than or equal to that chance, wake up
- the character; in any case, print an appropriate message and
- skip the turn.
- - If the character has the Noh Mask equipped or has Confused
- status, select a random action for confusion (logic omitted here
- for brevity).
- - If the character is a Goof-off, take a random number; if less
- than 65, select a random Goof-off action (logic omitted here for
- brevity).
- - Resolve the character's action.
- - If an enemy:
- - If in the first turn of a preemptive attack, skip the turn.
- - If the party attempted and failed to run, take a multi-random
- number; if at least 192 (1/4 chance), skip the turn.
- - If no action was chosen, skip the turn.
- - If the Chance time-stop effect is active, skip the turn.
- - If the enemy's AI type is 2:
- - If the action count selector is 1 or 2, take a multi-random
- number in the range [0,2], replace it with 2 if it is 0, and
- use that as the number of actions.
- - For each action, choose an action as described above, then
- evaluate the action as described below; treat the enemy's AI
- type as 1 for this purpose.
- - Otherwise (the enemy's AI type is not 2), evaluate the action:
- - If asleep, take a multi-random number and compare against the
- chance of waking for the number of sleep turns remaining
- (255, 192, 128, 64). If less than or equal to that chance,
- wake up the enemy; in any case, print an appropriate message
- and skip the action.
- - If the enemy is confused or the battle is non-interactive,
- choose a new target (and possibly change the action):
- - If the action is any of:
- - 0 (assess the situation)
- - 1 (parry)
- - 4-6 (attack + status effect)
- - 7 (flee)
- - 8, 59-63 (call for help)
- - 16-18 (status effect breath)
- - 40 (Chaos)
- - 43 (freezing wave)
- - 44 (Bounce)
- - 47 (Vivify)
- - 48 (Revive)
- then change it to 2 (normal attack) and continue with the
- next step.
- - If the action is a single-target action (2, 3, 9, 19-22,
- 31, 33, 36, 39, 42, 49, 50, 51, 54, 55, 56):
- - If the action targets the party:
- - If there is at least one enemy group other than the
- attacker's group living enemies, randomly choose one
- such group, then randomly choose the target from
- that enemy group.
- - Otherwise, if there are living enemies other than
- the attacker, choose a random enemy from the
- attacker's group.
- - Otherwise, leave the original target alone.
- - Otherwise, if the battle is an interactive battle,
- choose a random living party member as the target.
- - Otherwise (the battle is a non-interactive battle),
- choose the attacker as the target.
- - Otherwise (the action is a multi-target action):
- - If the action targets the party:
- - If there is at least one enemy group other than the
- attacker's group living enemies, randomly choose one
- such group as the target.
- - Otherwise, if there are living enemies other than
- the attacker, choose the attacker's group as the
- target.
- - Otherwise, leave the original target alone.
- - Otherwise, if the battle is an interactive battle,
- select the party member with the same index as the
- index of the originally targeted enemy group.
- - Otherwise (the battle is a non-interactive battle),
- select the attacker's group as the target.
- - Resolve the action.
- - If the enemy is not asleep and has a second action:
- - Evaluate the second action.
- - If the enemy has a third action, evaluate the third
- action.
- - At the end of each turn:
- - For each enemy with a nonzero HP regeneration selector, add a
- multi-random number in the associated range to the enemy's HP,
- capping at the enemy type's base HP (not the initial HP selected
- when battle started).
- - At the end of battle:
- - Multiply base experience by 1/3, rounding any fraction up, and add
- that amount to the base experience to get the total experience
- earned from the battle. Note that the code truncates the addend to
- the low 16 bits, so (in theory) if you killed 5 or more Metal
- Babbles in one battle, you would lose out on 65536 experience.
- - Divide the total experience by the number of living party members,
- rounding any fraction up, and add it to each living party member.
- - If the enemy can drop an item, then:
- - If the drop rate index is 0, the item is always dropped.
- - Otherwise, if the drop rate index is not 0, calculate the drop
- rate as 1 << (6 - index). The item is dropped if a random
- number is less than the drop rate.
- - Otherwise, take a random number. If it is 0, then the item is
- dropped if a second random number is less than 32.
- - Multiply base gold by 1/5, rounding any fraction up, and add that
- value to the base gold to get the total gold for the battle.
- - Add the total gold to the party's gold.
- - If the low 2 bits of a random number are 0 and a living merchant is
- in the party, add 1/8 of the total gold + 1 to the party's gold.
- (Note that the random number check is performed whether or not a
- merchant is in the party.)
- - Check each party member for leveling up. If a level up occurs:
- - Check the current value of each stat (in the order Strength,
- Agility, Vitality, Luck, Intelligence, maximum HP, maximum MP)
- against the upper limit for the character's class and current
- level. If the current value is greater than (or equal to, for
- maximum HP and MP) the limit, take the low bit of a random
- number as the level-up bonus. Otherwise:
- - For base stats, take a random-16 number, multiply it by the
- base increment for the class and current level, and divide by
- 110; if the result is zero, take the low bit of a random
- number instead.
- - For maximum HP and MP, take the bonus applied to Vitality or
- Intelligence respectively (but zero if the "low bit of a
- random number" method was used). If that value is zero, the
- HP or MP bonus is also zero; otherwise, double the value, add
- a multi-random number in the range [-2,+2], and use the
- result as the bonus.
- - For each spell learnable by the character's class:
- - Check whether the spell is learned this level (this check is
- done whether or not the spell is already known):
- - If the spell's learn chance type (*) is 1 and the
- character's new level is at least the spell's level, take
- a random number and learn the spell if bit 3 ($08) of the
- number is set.
- - If the spell's learn chance type is 0 and the character's
- new level is in the range [L,L+2] (where L is the spell
- level), compare the character's new Intelligence against
- the Intelligence target for the character's old level and
- obtain the learn chance:
- - If current < target - 15, the chance is $00 at level L,
- $80 at level L+1, and $FF at level L+2.
- - Else if current < target + 10, the chance is $80 at
- level L and $FF at level L+1 and L+2.
- - Else the chance is $FF at all levels.
- If the chance is not zero, take a random number; if the
- number is less than or equal to the chance, the spell is
- learned.
- - If the spell is learned this level, iterate over the class's
- spell list; if the character's spell bits corresponding to
- the spell ID are 0, set them to 1 and display the "learned a
- new spell" message. (If any bit is 1, the check stops
- immediately, which is why the Luisa registration bug -- a
- 1-byte overrun on a new character's inventory which overwrites
- the hero's first spell byte when the 12th stored character is
- created -- prevents the hero from learning Heal on the field
- spell list.)
- - Note that the Goof-off's possible random actions are handled
- as a set of 36 "spells", each of which is learned with chance
- type 1 and a base level equal to the ability index. The
- "learned a new spell" message is suppressed for Goof-offs.
- (*) Each spell has, in addition to its base learning level, a bit
- indicating which of two algorithms is used to check whether the
- spell has been learned. The following spells are type 1:
- Firebal (7), Infermost (36), Beat (22), Sacrifice (41),
- Limbo (20), RobMagic (15), SpeedUP (5), Awake (16), Surround (7),
- Chaos (27), Transform (37), BeDragon (34), Sap (8), Upper (4),
- Increase (9), Bounce (24), Bikill (21), Chance (40)
- All others are type 0.
- Other general notes:
- - When the battle system needs to choose a random target, it takes a
- multi-random number and divides that by (255 / number of possible
- targets). If the result is out of range, the last possible target is
- chosen.
- - The standard formula for computing base damage for a physical attack
- is as follows:
- - Compute the attacker's attack power minus half of the target's
- defense power.
- - If at least 2, multiply by a random-32 number bounded to [102,153]
- and divided by 4, then divide by 64 to get the base damage value.
- (This results in an average damage of attack/2 - defense/4, varying
- by up to 20% on either side of the average.)
- - Otherwise, use the low bit of a multi-random number as the base
- damage value.
- - When a party member attacks an enemy group:
- - If the attacker has the Armor of Hades equipped, take a random
- number and abort the attack ("Can't move because of a curse!") if
- the number is at least 170 (slightly more than 1/3 chance).
- - If the attacker has the Sword of Destruction equipped, take a
- random number and abort the attack if the number is at least 192
- (1/4 chance).
- - If the attacker has the Zombie Slasher equipped, perform a
- resistance check against holy damage for the targeted enemy.
- (There is not an explicit list of zombie enemies in the code;
- instead, the Zombie Slasher deals extra damage to any enemy if the
- enemy fails a resistance check against the damage.)
- - For each non-dead enemy in the selected enemy group:
- - Compute the base damage using the standard formula. Then take a
- multi-random number in the range 0 through 255 minus the
- attacker's intelligence, divide by 4, multiply that by the base
- damage, and add the high byte of the product to the base damage
- to get the final damage.
- - If any enemies took damage greater than their current HP, choose
- the enemy with the greatest remaining HP as the target (the enemy
- with a greater index wins ties). Otherwise, choose the enemy which
- would have the greatest remaining floor(HP/4) after the attack as
- the target (again, the greater index wins ties).
- - If the attacker is in Surround status, take a multi-random number;
- the attack misses if the number is less than 160.
- - If the attacker has the Demon Axe equipped, take a multi-random
- number; the attack misses if the number is less than 32.
- - If the attacker is in Bikill status, double the damage value.
- - If the attacker does not have the Poison Needle equipped:
- - Check for evasion:
- - If on the first turn of a preemptive attack, if the target is
- asleep, or if the target's evade rate is zero, the attack
- always hits.
- - Otherwise, take a multi-random number; the attack misses if
- the number is less than the evade rate.
- - Check for a critical hit:
- - If the attacker is a Fighter, take a multi-random number and
- make the attack a critical hit if the number is less than the
- attacker's level.
- - Otherwise, if the attacker has the Sword of Destruction or
- the Demon Axe equipped, the critical rate is 32, otherwise 4.
- Take a multi-random number and make the attack a critical hit
- if the number is less than the critical rate.
- - If the attack is a critical hit, recompute the damage by taking a
- multi-random number in the range [54,64], multiplying that value by
- the attacker's attack power, dividing by 64, and adding 1.
- - If the weapon does bonus damage to the enemy (Zombie Slasher or
- Dragon Killer against relevant enemies), take a multi-random number
- and add that number plus 16 to the damage.
- - If the weapon is the Poison Needle, take a multi-random number; if
- less than 32 and the encounter is not a preset encounter, the enemy
- is killed, otherwise damage is set to 1. (Note that this implies
- the Poison Needle can never do 0 damage -- it will always either
- hit for 1 damage or kill the enemy.)
- - If the enemy is parrying, halve the damage value and add 1.
- - Apply the damage (reduce the target's HP).
- - If the Multi-Edge Sword is equipped, apply 1/4 damage + 1 to the
- attacker.
- - When an enemy attacks a party member:
- - Compute the base damage using the standard formula, except that if
- the value of (attack power - half of defense power) satisfies all
- of the following conditions:
- - less than 256,
- - no greater than 1/8 of the attacker's attack power, and
- - greater than 2,
- then instead compute base damage as a multi-random number in the
- range 0 through (attack power - half of defense power - 1).
- - If the target has the Cloak of Evasion equipped, take a random
- number; if the number is at least 204 (~20% chance), the attack
- misses.
- - Take a multi-random number; if less than 4, the attack misses.
- - If the attacker is in Surround status, take a multi-random number;
- the attack misses if the number is less than 160.
- - If the target has the Shield of Sorrow equipped (and does not have
- the Cloak of Evasion or Swordedge Armor equipped), reduce the
- damage by half.
- - If the attacker is in Bikill status, double the damage value.
- - Apply the damage (reduce the target's HP).
- - If the party member dies, numb and poison statuses are cleared
- (confuse and bounce are not cleared).
- - Otherwise, if the target has the Swordedge Armor equipped, apply
- half the final damage plus 1 to the enemy.
- - Otherwise, if the target has the Shield of Sorrow equipped (and
- does not have the Cloak of Evasion equipped), choose a random
- living party member other than the target and apply the same damage
- to that character.
- - Apply any special effect of the attack (sleep, confusion, numbness)
- if the target is still alive.
- - When an enemy attacks another enemy:
- - Compute the base damage as if the enemy was attacking the party
- member whose index is the low 2 bits of the target enemy's index.
- - Discard that value and recompute damage using the same algorithm
- as for attacking a party member, but using the target enemy's
- defense power instead.
- - Handle Surround and Bikill status as when attacking a party member.
- - If the target is parrying, halve the damage value and add 1.
- - Apply damage to the target.
- - When checking a party member's resistance to a status effect which has
- a chance to miss:
- - Let the effect's base hit rate (out of 256) be H.
- - The chance C (out of 256) that the effect hits the party member is
- C = (((384 - luck) / 2) * H) / 128. (Conceptually, the base hit
- rate is multiplied by a factor from 0.5 to 1.5 based on the target
- character's Luck stat.)
- - If C >= 256, the effect hits.
- - Otherwise, take a multi-random number; if it is less than C, the
- effect hits.
- Base hit rates for standard effects are:
- - Beat: 32 (16 if Angel's Robe equipped)
- - Chaos: 64
- - Limbo: 50
- - Numb: 32
- - Poison: 96
- - Sap/Defence: 190
- - Sleep: 96
- - Slow: 192
- - StopSpell: 96
- - Surround: 160
- - Vivify: 128
- - When checking an enemy's resistance to a damage spell or status
- effect, if the enemy is partially resistant (resistance index 1 or 2),
- the enemy resists the damage or effect if a multi-random number is
- less than the resistance chance (77 or ~30% for index 1, 179 or ~70%
- for index 2).
- - When any character uses a damage spell on an enemy, damage for each
- target is computed as a multi-random number in the appropriate range:
- - Blaze: [8,14)
- - Blazemore: [70,90)
- - Blazemost: [160,200)
- - Firebal: [16,24)
- - Firebane: [30,42)
- - Firevolt: [88,112)
- - Bang: [16,24)
- - Boom: [52,68)
- - Explodet: [120,160)
- - IceBolt: [25,35)
- - Snowblast: [42,58)
- - Snowstorm: [88,112)
- - IceSpears: [60,80)
- - Infernos: [8,24)
- - Infermore: [25,55)
- - Infermost: [60,120)
- - Zap: [70,90)
- - Lightning: [175,225)
- Spell resistance is checked after damage is computed.
- - When an enemy uses a damage spell or breath attack on a party member,
- damage for each target is computed as a multi-random number in the
- appropriate range:
- - Blaze: [7,12)
- - Blazemore: [52,62)
- - Blazemost: [92,128)
- - IceBolt: [16,24)
- - Firebal: [10,18)
- - Firebane: [22,34)
- - Explodet: [60,80)
- - Snowblast: [32,42)
- - Snowstorm: [55,67)
- - Infernos: [6,18)
- - Infermore: [14,34)
- - Infermost: [30,62)
- - Flaming breath (weak): [6,10)
- - Flaming breath (medium): [30,40)
- - Flaming breath (strong): [80,100)
- - Blizzard breath (weak): [9,21)
- - Blizzard breath (medium): [40,60)
- - Blizzard breath (strong): [100,140)
- Damage is reduce to 1/4 + 1 if the attack is a spell and the target
- has the Armor of Hades equipped, or to 2/3 if any of the below apply:
- - the target has the Armor of Radiance or Water Flying Cloth equipped
- - the attack is a breath attack and:
- - the target has Barrier status, or
- - the target has the Shield of Heroes equipped
- - the attack is a fire breath attack and the target has the Dragon
- Mail equipped
- - the attack is a spell attack and the target has the Magic Armor or
- Sacred Robe equipped
- - When an enemy uses a breath attack on another enemy, damage for each
- target is computed as a multi-random number in the appropriate range:
- - Flaming breath (weak): [4,10)
- - Flaming breath (medium): [10,40)
- - Flaming breath (strong): [20,100)
- - Blizzard breath (weak): [12,21)
- - Blizzard breath (medium): [20,60)
- - Blizzard breath (strong): [40,140)
- The large damage ranges here appear to be due to a bug: the code that
- reads the base and range values ($8DDF in bank 4) gets them backwards,
- storing the base damage in the zero-page address used to hold the
- random range and vice versa.
- - When a party member uses a Medical Herb:
- - Take a multi-random number in the range [35,50).
- - If the target is not Zoma, heal the target by that amount.
- - Otherwise, take a multi-random number in the range [0,219), add
- 229, and damage Zoma by the low 8 bits of that amount. (This is a
- bug in the code to assign damage from healing spells, which is also
- used for herbs: it doesn't check for the herb effect code ($41), so
- it reads past the end of the damage range table and takes bytes
- from the following routine's code as the damage range.)
- - When a party member casts Beat on an enemy, if the enemy makes a
- successful resistance check against death, a second check is
- performed. This has the effect of squaring (and thus lowering) the
- enemy's resistance rate -- 30% death resistance becomes 9% resistance
- to Beat, and 70% death resistance becomes 49% resistance to Beat.
- Defeat does not make this second check, so the normal death resistance
- rate applies.
- - When a party member or enemy casts Beat or Defeat on a party member,
- if the party member has the Sacred Amulet equipped, the effect always
- misses (thus skipping the resistance check).
- - When using the Wizard's Ring:
- - Add a multi-random number in the range [10,25) to the user's MP.
- - Take a random number; if it is less than 25 (~10% chance), the
- ring breaks.
- - When using the Chance spell:
- - If the encounter is not a preset encounter, take a multi-random
- number in the range [0,16] and execute that effect.
- - Otherwise (the encounter is a preset encounter), take the low 4
- bits of a multi-random number; if that value is at least 12,
- execute effect 1, otherwise execute effect 16.
- The possible effects are:
- - 0: Party sleep / enemy flee ("something unbelievably frightening")
- - 1: Healusall
- - 2: Revive on all dead party members if any, else Healusall
- - 3: Heal all party members for [41,48] HP
- - 4: Confuse all party members and enemies
- - 5: Kill all enemies ("<enemy> shatters into pieces")
- - 6: Kill all enemies (same as effect 5)
- - 7: Nullify spells ("fierce darkness")
- - 8: All party attacks become criticals ("additional Attack Power")
- - 9: Put all party members and enemies to sleep
- - 10: Caster gets 3 free turns ("time ceases")
- - 11: Randomize party formation ("party changes its formation")
- - 12: Enemies "depart"
- - 13: Party gets a free turn ("the foe is taken off guard")
- - 14: Steal all MP from all enemies
- - 15: Cure numbness on all party members
- - 16: No effect ("Chance...chance...chance...")
- - Enemy data can be found at address $B2D3 in bank 0 (also entry $6A in
- the BRK #$07 array). The data is a list of 23-byte entries, one per
- enemy; corresponding enemy names are stored as two sequences of
- strings in bank 2, the first line's text at $B3BC and the second
- line's text at $B8BF. The data structure is as follows:
- - Byte 0, bits 7-6: bits 4-3 of evade rate
- - Byte 0, bits 5-0: level
- - Bytes 1-2: experience
- - Byte 3: speed
- - Byte 4: gold (low 8 bits)
- - Byte 5: attack power (low 8 bits)
- - Byte 6: defense power (low 8 bits)
- - Byte 7: base HP (low 8 bits)
- - Byte 8: MP
- - Byte 9, bit 7: bit 2 of evade rate (evade rate is a multiple of 4)
- - Byte 9, bits 6-0: item dropped ($7F = none)
- - Bytes 10-17, bits 5-0: possible actions
- - Bytes 10-11, bit 7: AI type selector (byte 11 has the high bit)
- - Type 0: random target choice
- - Type 1: smart target choice
- - Type 2: smart target choice, and action choice is delayed until
- enemy acts
- - Bytes 12-13, bit 7: action chance selector
- - Type 0: equal chance for all 8 actions
- - Type 1: $12, $16, $1A, $1E, $22, $26, $2A, $2E (/256)
- - Type 2: $02, $04, $06, $08, $0A, $0C, $0E, $C8 (/256)
- - Type 3: fixed action sequence
- - Bytes 14-15, bit 7: number of actions per turn
- - For AI type 2: 1, 1-2, 1-2, 2
- - For other AI types: 1, 1-2, 2, 1-3
- - Bytes 16-17, bit 7: HP regeneration selector
- - Type 0: no regeneration
- - Type 1: 16-23 HP/turn
- - Type 2: 44-55 HP/turn
- - Type 3: 90-109 HP/turn
- - Byte 18, bits 7-6: fire damage resistance
- - Byte 18, bits 5-4: ice damage resistance
- - Byte 18, bits 3-2: wind damage resistance
- - Byte 18, bits 1-0: gold (high 2 bits)
- - Byte 19, bits 7-6: lightning damage resistance
- - Byte 19, bits 5-4: instant death resistance
- - Byte 19, bits 3-2: Sacrifice resistance
- - Byte 19, bits 1-0: attack power (high 2 bits)
- - Byte 20, bits 7-6: Sleep resistance
- - Byte 20, bits 5-4: StopSpell resistance
- - Byte 20, bits 3-2: Sap resistance
- - Byte 20, bits 1-0: defense power (high 2 bits)
- - Byte 21, bits 7-6: Surround resistance
- - Byte 21, bits 5-4: RobMagic resistance
- - Byte 21, bits 3-2: Chaos resistance
- - Byte 21, bits 1-0: base HP (high 2 bits)
- - Byte 22, bits 7-6: Slow / Limbo resistance
- - Byte 22, bits 5-4: Expel / Fairy Water / Zombie Slasher resistance
- - Byte 22, bit 3: focus-fire flag (if set, the enemy continually
- attacks the same character until they die)
- - Byte 22, bits 2-0: item drop chance (100%, 1/8, 1/16, 1/32, 1/64,
- 1/128, 1/256, 1/2048)
- Why battle outcomes can differ even with the same RNG seed
- ----------------------------------------------------------
- Battles use the same RNG as the rest of the game, but with a twist:
- Many routines that use randomness discard a certain number of random
- numbers from the RNG sequence before obtaining the actual random number
- to be used. The "certain number" is stored at address $6A68, which is
- updated with a random number in the range [0,15] during each turn of
- battle, after all characters' commands have been entered. (Attempting
- to run does not change this value.)
- The catch is that $6A68 is _not_ cleared or initialized on reset. Since
- that address is within the backup RAM area, the value will be retained
- across reset and power-off. This is why, for example, battle outcomes
- when battling Metal Slimes near Dhama in a speedrun will differ even
- when resetting the RNG with a fixed seed from a separate file on each
- reset. $6A68 is not even cleared when initializing backup RAM on a
- fresh cart, so the value in that case is unpredictable (unless running
- on an emulator, in which case the emulator normally clears backup RAM to
- zero when the game is first run).
- Details of the random number generator (and how to break it)
- ------------------------------------------------------------
- Dragon Warrior III has a very nearly high-quality random number
- generator for the NES era, using a 16-bit LFSR (linear feedback shift
- register) with a period of 65534 and adding the low 8 bits of the LFSR
- to an 8-bit counter to produce the final 8-bit random number. This
- gives an RNG sequence length of LCM(65534,256) = 8,388,352 values.
- The random number generator algorithm is:
- for (16 iterations) {
- seed = LFSR(seed, 1)
- }
- counter += 1;
- return (byte)(seed + counter);
- where
- LFSR(seed, input) = (seed << 1) ^ ((seed>>15 ^ input) ? $1021 : 0)
- I say "very nearly" high-quality because there are two initial seeds
- which give an LFSR sequence length of just 2: $5FEA and $AFF5. This
- property itself is unavoidable in any LFSR equation, but the problem is
- that the game does not attempt to avoid these seeds. Since the RNG
- algorithm clocks the LFSR 16 times per random number, this effectively
- means that if the seed is ever set to one of these two values, the RNG
- will simply return the counter plus a constant value (either $EA or
- $F5).
- Now, since those two seeds are not part of the 65534-long sequence, it
- should normally be impossible to encounter them in the first place. But
- it turns out there's one place in the code which calls the LFSR shift
- function with non-constant input bits: the save file checksum routine.
- The game calculates a 16-bit checksum for each save file (787 bytes of
- data) by setting an initial seed of $3A3A, passing each bit of the data
- as the input bit to the LFSR function, and taking the resulting LFSR
- value as the checksum (the routine can be found at $ACD0 in bank 7).
- The game doesn't check whether that checksum is one of the two values
- above that generate a short sequence, so if we can generate a save file
- with one of those values as the checksum, we can make the RNG misbehave.
- When creating a new file, we directly control a total of 10 bytes:
- - The hero's name (8 bytes at offset $114)
- - The hero's sex (bit 3 of the byte at offset $48)
- - The message speed (1 byte at offset $311)
- We also indirectly control the names and status of the autogenerated
- party characters; the names are picked randomly from a pool of 4 names
- for each character, and each stat randomly (50% chance) has 1 added to
- the base value. The save file is checksummed after generating each of
- the four initial characters; naturally, we need the seed after the
- final checksum to be one of the two short-sequence values.
- There are many name/sex/speed combinations which produce the desired
- result in a new save file, which can then be used to reset the RNG seed
- on the initial menu screen by changing the message speed for the file
- (to the same speed it's already set at). Some short names include:
- File: 1 Name: pP Sex: F Speed: 7 Checksum: $5FEA
- File: 1 Name: iGa Sex: M Speed: 7 Checksum: $5FEA
- File: 2 Name: am Sex: F Speed: 6 Checksum: $AFF5
- File: 2 Name: E? Sex: F Speed: 5 Checksum: $5FEA
- File: 2 Name: DOg Sex: M Speed: 7 Checksum: $5FEA
- File: 2 Name: Ndt Sex: M Speed: 6 Checksum: $AFF5
- File: 3 Name: -a Sex: M Speed: 6 Checksum: $AFF5
- File: 3 Name: Qg Sex: M Speed: 7 Checksum: $5FEA
- File: 3 Name: mop Sex: F Speed: 7 Checksum: $5FEA
- For reference, the mapping from text characters to name data is:
- a...z = $0B...$24
- A...Z = $25...$3E
- Space = $50
- ' = $68
- , = $6A
- - = $6B
- . = $6C
- ( = $6D
- ) = $6E
- ? = $6F
- ! = $70
- Names are padded with $00 to a length of 8 bytes.
- One caveat with "breaking" the RNG in this fashion is that it can cause
- the game to lock up when starting an encounter. This happens due to
- poor design of the core "ranged random number" function ($C3F1) combined
- with the lack of a safety valve in one part of the encounter generation
- logic. Specifically, in the "Choose a random number from 0 through 2"
- item described earlier (at $8573 in bank 0), if the encounter has two
- groups and the next RNG output on entry to that step is 1 or 2 (mod 4),
- the following will occur:
- - The low 2 bits of the first random number returned are nonzero,
- so the loop is not terminated.
- - The next ranged random number call (to get a group index) will see
- a random number outside the requested range of 0 through 1, so it
- will consume random numbers until it reaches one whose low 2 bits
- are zero, giving group index 0.
- - At the next iteration, the low 2 bits of the first random number
- will equal 1, so the loop is not terminated.
- - The next ranged random call will get group index 0, and so on
- ad infinitum.
- The code places no limit on the number of loop iterations, so if the
- loop enters this state, the game will halt.
- An interesting consequence of using one of these two seeds is that
- physical attacks in battles which use the standard damage formula will
- always give either the maximum ($5FEA) or minimum ($AFF5) possible
- damage, and level-up bonuses for the five basic stats will be
- consistently low ($5FEA) or high ($AFF5).
- Random numbers in other NES Dragon Warrior games
- ------------------------------------------------
- The LFSR function used in Dragon Warrior III's random number generator
- dates all the way back to the original Dragon Warrior. There, the
- function was used only to compute save file checksums (see $FC2A); the
- RNG was seeded from the 16-bit checksum and subsequently updated with
- the formula seed <- seed * 3 + 129 (see $C55B), taking the low byte of
- the seed as the RNG output. This function produces two independent
- sequences of 32768 values depending on the starting seed, and does not
- have any "holes" with extremely short sequences. The RNG seed is stored
- at $94.
- Dragon Warrior II was the first game in the series to use the LFSR for
- random number generation as well. It uses essentially the same logic
- as DW3, except that it does not keep a counter to add to the seed when
- generating RNG output, so if the seed falls into one of the holes, the
- RNG's output will become constant. The RNG seed is stored at $32.
- Dragon Warrior IV uses the same RNG as DW3. The RNG seed is stored at
- $12, and the output counter is stored at $050D.
Add Comment
Please, Sign In to add comment