The Magic User in Dungeons & Dragons: Shadow Over Mystara is a popular character choice with some unusual traits. For one thing, instead of performing a smash attack with F, attack, he produces a black dagger (stowing his main weapon somewhere) and thrusts it out there enthusiastically. It looks like it hurts, but besides a large amount of stun, it usually does only a token point of damage. However, there's a random, enticing chance for a "critical hit" and massive damage.
Let's have a closer look at that.
Ouch.
Summary: In the first phase of a critical hit attempt, a random value is looked up from a table. That value either indicates failure or points to one of three second-phase tables, each one awarding higher damage multipliers but a lower success rate than the last. Then another random value is looked up from the second table to determine success or failure.
Here's what happens in a successful attempt:
003E98: moveq | #$0, D4 |
... | |
003EB4: jsr | $19a88.l |
003EBA: andi.w | #$f, D0 |
003EBE: add.b | D0, D4 |
003EC0: move.b | (-$74,PC,D4.w), D4 |
003EC4: bmi | $3ef2 |
003EC8: jsr | $19a88.l |
003ECE: andi.w | #$f, D0 |
003ED2: add.w | D0, D4 |
003ED4: move.b | ($22,PC,D4.w), D4 |
003ED8: bmi | $3ef2 |
003EDC: move.b | #$1, ($77,A0) |
003EE2: move.b | #$3, ($18e,A0) |
003EE8: move.w | ($60,A1), D0 |
003EEC: lsr.w | D4, D0 |
003EEE: bra | $3f28 |
... | |
02648A: cmp.b | ($16e,A0), D0 |
02648E: ble | $26494 |
026490: move.b | ($16e,A0), D0 |
026494: sub.w | D0, ($62,A1) |
Register A0
is the player's base (0xFF85DE for P1) and A1
is the victim's base (0xFFBA1E for bosses). The $19a88.l
subroutine plants a two-byte random number in D0, but only the smallest half-byte is used, resulting in a number from 0 to 15.
The table lookups occur at $3ec0
and $3ed4
. Negative results in either case branch to $3ef2
and result in failure. If the second value drawn is non-negative, the enemy's max life (offset 0x60) is loaded into D0
and shifted right (divided by two) that number of times. This is the amount to be deducted from current life (offset 0x62).
Possible values are 2, 1 and 0, meaning the hit can take off a quarter, half, or all of the enemy's max life.
Here are all the possible results of the table lookups:
random number D0 at $3EBE | D4 at $3EC4 | random number D0 at $3ED2 | D4 at $3ED8 |
---|---|---|---|
0x0 - 0xA | 0xFF | n/a | |
0xB - 0xC | 0x00 | 0x0 - 0x5 | 0x02 |
0x6 - 0xF | 0xFF | ||
0xD - 0xE | 0x10 | 0x0 - 0x3 | 0x01 |
0x4 - 0xF | 0xFF | ||
0xF | 0x20 | 0x0 - 0x1 | 0x00 |
0x2 - 0xF | 0xFF |
So the chance of getting each result:
full damage | 1/16 x 2/16 | 1/128 | 0.78% |
---|---|---|---|
1/2 damage | 2/16 x 4/16 | 4/128 | 3.13% |
1/4 damage | 2/16 x 6/16 | 6/128 | 4.69% |
1 point damage | 128/128 - 1/128 - 4/128 - 6/128 | 117/128 | 91.5% |
It looks like the instructions at $02648A
- $026490
are supposed to limit how much damage can be done. The byte at offset 0x16E from the player's base is compared the pending damage in D0
. The value of that offset depends on the right-hand equipment, but the M.User can't equip anything in his right hand and it's always 0x63 for him. D0
is supposed to be capped to that value, implying that the most damage a stab can do is 99 points. (Boss HP can exceed 1000 points.)
But the reduction to 99 damage only happens when the pending damage is from 0x64 to 0x7F, or 100 to 127. Any higher and it's left alone. Maybe the D0
number is interpreted as negative and they meant cmp.w
instead of cmp.b
? It's not hard to see in real games that stab damage can be a lot higher than a hundred points.
On the other hand, there's no minimum. A critical hit can do less damage than a normal hit on weak enemies.
Here is the RNG subroutine at $19a88.l
:
019A88: move.w | ($1a,A5), D0 |
019A8C: add.w | D0, D0 |
019A8E: add.w | ($1a,A5), D0 |
019A92: lsr.w | #8, D0 |
019A94: add.b | D0, ($1b,A5) |
019A98: move.b | D0, ($1a,A5) |
019A9C: move.w | ($1a,A5), D0 |
019AA0: rts |
Register A5
is 0xFF8000, and the pseudorandom number ends up in D0
.
The algorithm is just a few instructions and is pretty simple: Without exceeding two bytes of space, triple the seed, shift right eight times (divide by 28, rounding down), add the reduced value to the second byte of the seed, then discard the first byte and replace with the reduced value. There's probably a name for it in computer science textbooks or something.
This gets called many times when an enemy takes some action or gets hit, and affects other random occurences as well. But if there are no enemies present or they aren't doing anything (e.g., knocked down or dizzy) the value at 0xFF801A won't change. So the player's character cannot manipulate the seed directly. Performing random various actions before the stab attempt isn't going to improve the chance of success unless they alter the enemy's AI decisions.
Update: You can affect the RNG with a slide (qcf+jump), dash attack (qcf+attack for those who have it) or skidding to a stop from a run. I think this is because the dust cloud sprites are randomized.
Too many times, I've been grinding through a JRPG trying to get a stupid random occurrence to happen, like a rare item drop, and having no idea if what I'm trying has any chance of working. Even knowing the odds only takes the edge off the aggravation a little bit, but it's still better than nothing.
I was wondering how Magic User's critical hits were working.
Thanks for sharing this, really.
Posted by: Rakhasta | 07/10/2012 at 06:31 AM