Super Turbo HUD

Haha ouch, but thanks for all of the hard work you’ve put in yourself, paps. At least with this amazing new throw information, we finally have a definitive source for how the game calculates the ranges and values. EVERYONE who is serious about ST should be using this and learning from it.

To summarize, each character has a static “throwable box” that also displays throwable states (by way of color changes during emulation). In addition, throw moves toss out their own “active throw attempt” box that activates the throw when it intersects the throwable box. All of the standard throw ranges for a character seem to be the same, though they, like the static throwable box, differ per character. It explains things like how Honda is relatively more difficult to tick throw, for example. Characters may have varying sizes of active throw boxes if they also possess command throws.

Again, this is EPIC news, and is readily verifiable by using the scripts in the emulator. MAJOR THANKS to dammit for discovering this.

Updated. New hotkey for toggling the throw hit boxes.

Additions:
[LIST]
[]Added the throw hitboxes
[
]Dizzy bar is shown when the char is dizzied.
[]Removed the ability to move the dummies by 1 pixel since throw boxes are now visible.
[/LIST]
Fixes:
[LIST]
[
]Fixed the stun counter, it’s actually a word, not a byte, so it’s displayed correctly (0-65535)
[*]Fixed the grab break, ken seems to have a maximum value of 63, far beyond what I thought the threshold was (52). I believe sim can get 57, didn’t test this much. So I left it at 63 until I reverse what determines that value and find the table and…the max value :p.
[/LIST]

Let me know if I broke anything, don’t think I did.

I’ll add an accurate stun gauge (although I don’t see the practical uses) that keeps the stun counter in mind (the one Cauldrath mentioned increments after each stun) just didn’t feel like doing it tonight.

how to control p2 to do reversal? answer me fagsky!

Updated the video on SRK thread and the google code, shit was out dated lol.

can you make a meter to figure out how long of a grace period you have to complete a command move? for example, if it do a hadoken motion :qcf: how many secs grace period do I get to push the punch button and still get a fire ball to come out using ryu?

There’s enough time to walk forward just a smidgeon.

Sent by that tapatalk thing

This I already know, but the HUD with that meter really big on the screen would help to practice for advanced tricks.

I planned to do that by changed those numerical values for the moves into visual aids, I just never got around to it. I dunno if I ever will.

You guys have truly turned this thing into a complete training app!

Found the damage formula for damage before damage scaling occurs (Does not include throws):

The game first loads a base value depending on which move you used to damage your opponent into register D6, this example will use Guiles Close St.HK:



0BEDA4: move.b  (A1,D0.w), D6 // A1 is the base pointer it changes depending on the character used, D0.w is the offset, D0.w changes based on which move was used (in this case close st.HK)

// This loads 0x1A (the value at A1 + D0.w) as the base value for guiles close st.HK
// the next change occurs at:

0BEE6A: add.w  D0, D6 // Add register D0 to Register D6 (Remember D6 is holding our base damage value)

//D0: is modified here:

0BEE64: move.b  (A1,D2.w), D0 // This grabs a memory address that has a random value written to it constantly, D0 later has an AND operation performed to it

// After the AND operation the value appears to be anywhere from -3 (negative because 1A + FF = 19, 1A+ FE = 18 etc...) to +4

0BEE6A: add.w  D0, D6

//A final value is subtracted from this new value:

0BEE78: sub.w  D5, D6 // Subtract D5 from D6

//D5 has modifications at the following locations (This is the characters armor value, Character Dependant)

0BEE74: mulu.w  D6, D5 // Multiply D5 by D6
0BEE76: lsr.w  #5, D5  // Logical bit shift right 5 times

// Damage = D6 + D0 - D5
// In this example, lets say D0 ends up being 0x02

// D6 = 0x1A    D0 = 0x02
// 0x1A (base value of close st.hk) + 0x02 (D0) = 0x1C
// now D6 =0x1C
// D5 = 0x05
// 0x05 * 0x1C = 0x8C
// Shift 0x87 5 times to the right:
// Original:  00000000000000000000000010001100
// Shifted right 5 times:  00000000000000000000000000000001
// 00000000000000000000000000000001 = 0x04
// D6 - D5 = 0x1C - 0x04
// D6 = 0x18
// Guiles St.HK ends up doing 24 damage


Layman’s terms:
[LIST]
[]A base value for the attack is grabbed from a table, Guile’s ST HK’s base value is 26
[
]A value from -3 to +4 (Credit to dammit for the table) is added to this, modifying the base value to 24 through 29
[]This value is multiplied by 5 (for ken, character dependant), and the result in binary is shifted to the right 5 times
[
]This is where it gets tricky, because the amount subtracted depends on the move and the character, if its a high damaging move obviously the multiplication result will be higher (such as a close ST.HK), also gief has a much higher multiplier. This range could be larger in say giefs SPD or hawks SPD or any move that is 1 hit and does a large amount of damage (high base value).
[/LIST]
[LIST]
[*]So in closing **
[SIZE=7]New Base Value = Base Value + (-3 to +4) (JUST READ DAMMITS LOG AND LOOKED AT THE TABLE, I DIDNT BOTHER TO DISCOVER THE TABLE LENGTH)
Reduction = New Base Value * [Character Armor Multiplier] -> Bitshifted right 5 times
Damage = New Base Value - Reduction
**[/SIZE]
[/LIST]
And that’s how damage is calculated in ST. Basically the more that gets added from the first modification of the base value, the more will be subtracted from the 2nd modification and vice versa.

This was fun to reverse. Basically the randomness of the damage comes from that pointer address for D2 at:
0BEE64: move.b (A1,D2.w), D0

That value at A1 + D2.w is always being written to constantly, it probably has a range as well, (such as when the other players life is low) to account for the damage scaling but I’m not going to bother to figure that out, Mame’s debug tools aren’t as great as cheat engines.

Knowing the base value you can get the maximum range for damages, assuming my tests with the -2 to +3 are correct (I tested this quite a bit, since the table it gets it from seems rather spread out, so I couldn’t pin point the exact table).

So if guiles Close ST.HK has a base value of 0x1A (26) we can determine maximum and minimum values:
1A + 4 = 1E

1E * 5 = 0x91

0x91 in binary is:
0000000000011110

Bit shift this to the right 5 times

0000000000000100 = 4

0x1E - 4 = 26 maximum damage

0x1A + -3 = 0x17

0x17 * 5 = 0x78

0x73 in binary is:

0000000001110011

Bit shift this to the right 5 times:
0000000000000010 = 2

0x17 - 2 = 21 minimum damage (VS KEN)

Guiles damage with Close ST.HK Vs. KEN (UNTIL DAMAGE SCALING OCCURS) is 21-26

Yoga book hyper couldn’t be any more wrong, lol.

Cool stuff.

EDIT:

Armor rating multipliers by char:

Gief = 0xA (10)
Rog, Sagat, Dictator, T.Hawk, Honda, Blanka = 7
Everyone else = 5

I found the subroutine that checks to scale the damage further. If I have time, I’ll reverse this.

Damage scaling occurs if the players life is equal to or less than 30.



0BEE80: cmpi.w  #$1f, ($2a,A2)  // This checks if the characters life is equal to or below 30
0BEE86: bcc    $beea2  // Branch if the characters life is equal to or below 30
0BEE88: movea.l #$e7d96, A1
0BEE8E: move.w  ($2a,A2), D1  // This is where the damage scale formula begins
0BEE92: move.b  (A1,D1.w), D1 // Further modified, have to look into this
0BEE96: ext.w  D1
0BEE98: mulu.w  D6, D1  // Multiply the damage scale by the damage
0BEE9A: lsr.w  #5, D1  // Bitshift the damage scale to the right 5 times
0BEE9C: sub.w  D1, D6  // Subtract the damage scale from the damage
0BEE9E: bne    $beea2 // Jump back to the original damage formula


I’m pretty fucken awesome.

hawk needs to be in the top group. thats fucked up capcom.

P.S. Sirlin said that Hawk’s SPD is the only move in the game that has ignores the armor.

Just tested this.

Technically this isn’t correct, but at the same time it is…let me explain.

I found the throw damage calculator, but haven’t explored it, i basically put a watch point on the 2P’s life and set it to break when it’s written to and find out what was writing to it, that’s how I found it.

Anyways, when hawk does an SPD, he gets a base value:

LP SPD: 0x26 (38)
MP SPD: 0x2A (42)
HP SPD: 0x2E (46)

I didn’t bother to find out how this value changes for other chars, because gief seems to use the same formula, but I think there is a jump somewhere that hawk skips. Anyways, hawks SPD doesn’t have any sort of randomness to it…UNTIL damage scaling…however remember that damage scaling doesn’t come into effect until the character has 30 life or less. So when damage scaling occurs, hawks SPD damage does indeed decrease. Just doing some testing without looking at the code, the lowest value I’ve seen it go to when scaled (when geif is at 30 life) for HP SPD is 0x29 (41). LP SPD at 30 life gets scaled to 0x22 (34).

If you set giefs health lower, to say 17, the LP spd gets scaled to 0x18 (24). So yes and no. Hawk’s SPD is not scaled by armor, UNTIL the opponent is at or below 30 life.

However, it appears the damage scaling on Hawk’s SPD is never enough to save an opponent. So even though they are at 30 life, and LP SPD is going to do 38 damage, it only gets scaled to 34, so they always get killed even with the scaling.

I haven’t determined if scaling increases as life goes below 30, but hawks SPD seems to do this, more damage is scaled the lower the life of the opponent, but the damage of the SPD is always more than their life remaining, his scaling seems to be static as well, at 30 life, LP will always scale it 0x22 and HP will always scale to 0x29.

If I get some time I’ll do normal damage scaling and reverse that, and then I’ll check out throws and grabs and I’ll find out what part of hawks spd formula is bypassing what everyone else is doing.

All normal (non-command) throws ignore the armor as well. I was doing some testing on individual character damage scaling that I’ve been posting in the HDR thread (found out that non-teched throws in HDR have a random chance to do more damage than they do in regular ST for some reason, while in ST they are constant, not random, and same for every character).

The elusive hyper grabs.

Ok, whoever said you can get a hyper grab 100% by doing a certain button combination before the grab…is an idiot. This is false. They are random.

Grab address given in the explanation are for player 1. If you want player 2’s grab address just add 0x0400 (e.g. Grab for player 1 is 0xFF84CD + 0x0400 = 0xFF88CD) Type BYTE.

Working with ken’s knee bash I reversed the hyper grabs:



08616E: move.w  #$c, D0  //  Put the initial grab value of the constant 0x0C into register D0
086172: jsr    $528c.w
00528C: move.b  D1, ($150,A6)
005290: lea    ($54,PC), A3; ($52e6)
005294: movea.l A6, A4
005296: bsr    $52ac
0052AC: ext.w  D0
0052AE: move.w  D0, ($7c,A4)  //  Write D0 to FF88CA  (initial value of 0x0C)
0052B2: jsr    $2e96.w
002E96: move.w  ($2d0,A5), D0
002E9A: andi.w  #$202, D0
002E9E: move.w  D0, D1
002EA0: lsr.w  #8, D0
002EA2: eor.b  D0, D1
002EA4: andi    #$ee, CCR
002EA8: beq    $2eae
002EAA: ori    #$11, CCR
002EAE: roxr.w  ($2d0,A5)
002EB2: moveq  #$0, D0
002EB4: move.b  ($2d1,A5), D0 // D0 gets a value from a memory address that contains a random value
002EB8: rts
0052B6: andi.w  #$1f, D0
0052BA: move.b  (A3,D0.w), D0  //  This is the random portion of the grab, this reads a value from a table located at A3 + D0's offset
0052BE: ext.w  D0  // Sign extend register D0 to a word....e.g. if D0 contains 00FE it will sign extend to FFFE
0052C0: add.w  D0, ($7c,A4)  // Add D0 to grab address 1
0052C4: rts
005298: move.w  ($7c,A6), ($7e,A6)  // Takes the value at grab address 1 and writes it to the actual grab address not sure why there is a temporary address used.
00529E: rts


So, what is basically going on is grabs have a starting base value of 0x0C (12). This is stored into register D0, and then written to a temporary address at 0xFF88CA. The game then grabs a value from a memory address with a pseudo random value (I say pseudo because it actually cycles through the same numbers but which number gets written is random) at 0xFF82D1. D0 then gets AND’d against 0x1F and then reads from a table located at 0x52E6 + the offset of whatever D0 was from the random value and AND operation. This value then gets sign extended (type word), so if the value was say 0x00FE, it would become 0xFFFE, if it was 0x0012, it becomes 0x0000. This is then added to the base value at the temporary address at 0xFF88CA. Then the game copies the value at the temporary address to the actual grab value address at 0x0xFF84CC.

The lower the grab value, the faster you can get hits off on your opponent.

The attainable values for grabs from fastest to slowest are, 0x03, 0x06, 0x09, 0x0C, 0x0F, 0x12, 0x15

Meaning there are 7 different speeds for grabs in the game.

Example of the calculations:

Base value 0x0C
Base value is written to the temporary address
D0 receives a random number
D0 then receives a number dependent upon the random number given to it in the previous step, for this example it is 0x000000FE
This number is then AND’d against 0x1F, 0x000000FE becomes 0x0000001E
The game then grabs a value located at 0x52E6 + 0000001E. This value is 0x00000000 and writes it to D0
D0 gets sign extended (type word) from 0x00000000 to 0x00000000.
D0 is then added to the temporary grab address which contains, 0x0000000C + 0x00000000 = 0x0000000C
The temporary address is copied to the grab address.
The grab value is 0x0C.

To demonstrate how the value can become lower, assume the value looked up at the table was 0x00FA, when this is sign extended it becomes FFFA.
0xFFFA+ 0x0C = (it will rollover since we’re only working with a value type of word) 0x0006. The grab value is then 0x06.

Layman’s explanation:
[LIST]
[]Grabs have an initial value of 12
[
]There is a random number that is checked against that determines what number will be used from a table of numbers
[]This number has some math operations performed to it (To somewhat randomize)
[
]After the mathematical operations, it is added to the base value of 12
[*]There are only 7 speeds possible for grabs: 3, 6, 9, 12, 15, 18, 21 (Lower is faster)
[/LIST]
I’ve tried doing different button presses to manipulate the random number at 0xFF82D1, it is not manipulated by button presses. Anyone saying they can get a hypergrab 100% is lying and just merely rolling the same numbers in a row, it’s merely coincidence that this occurs. Which is probably why if they don’t get it they’ll say “hold on let me try again” and probably just had another successful roll attempt.

Being grabbed out of a special does not trigger a hyper grab, the same check gets performed. Any hyper grabs that came from being thrown from a special are just coincidental.

So the mystery of the hyper grab is now solved.

http://img843.imageshack.us/img843/2540/mame2011081515023237.jpg

Script is updated.

Added the grab speed bar (It’s on the left P1 Side, Speed 5 was attained, value of 9).

Speed:
21 = 1
18 = 2
15 = 3
12 = 4
9 = 5
6 = 6
3 = 7

Fixed the dizzy meter sticking around if you hit them out of dizzy. Also fixed the grab meter showing up when using regular throws. Also changed the life addresses to type word, for some reason I had them as type byte.

It’s updated on google code.

http://code.google.com/p/ssf2thud/downloads/list

Just found this.

Great work Pasky + everyone else involved. This will help my game greatly :D.


I am here: http://maps.google.com/maps?ll=35.012712,-119.335763

I checked out the psuedo random number generator at 0xFF82D1. I noticed when the game is reset, it always starts with a value of 0x2472 and after this initial value is written, that address just constantly gets bit rotated (type word) to the right. So eventually it will just rollover and the same numbers are generated.

Did some further testing on the grabs, I wrote a lua script to record the maximum and minimum values obtained at 0xFF82D1 when AND’d, I let the script run for about 10 minutes and let the number roll over a few times.

The max value is 31 and the minimum value is 0, after being AND’d.

This means there are 32 possible values to get from the table (0-31)

The table starts at 0x52E6, 0 = 0x52E6, 31 = 0x5305, the format below is the table offset, the value located at the offset , that value sign extended and that value added to 0x0C (the base value).

Example: assume we rolled a 10 (0x0A) on the random number generator, this brings us to:****

0x52E6 + 0A = 0x52F0
Value located at 0x52F0 = 0xF7
0xF7 sign extended = FFF7
FFF7 + 0x0C = 0003



0:  09 = 0009 + 0x0C = 0x15 (Speed 1)
1:  FA = FFFA + 0x0C = 0x06 (Speed 6)
2:  FD = FFFD + 0x0C = 0x09 (Speed 5)
3:  00 = 0000 + 0x0C = 0x0C (Speed 4)
4:  00 = 0000 + 0x0C = 0x0C (Speed 4)
5:  03 = 0003 + 0x0C = 0x0F (Speed 3)
6:  06 = 0006 + 0x0C = 0x12 (Speed 2)
7:  FD = FFFD + 0x0C = 0x09 (Speed 5)
8:  03 = 0003 + 0x0C = 0x0F (Speed 3)
9:  00 = 0000 + 0x0C = 0x0C (Speed 4)
10: F7 = FFF7 + 0x0C = 0x03 (speed 7)
11: 06 = 0006 + 0x0C = 0x12 (Speed 2)
12: 00 = 0000 + 0x0C = 0x0C (Speed 4)
13: 00 = 0000 + 0x0C = 0x0C (Speed 4)
14: FA = FFFA + 0x0C = 0x06 (Speed 6)
15: F7 = FFF7 + 0x0C = 0x03 (speed 7)
16: 00 = 0000 + 0x0C = 0x0C (Speed 4)
17: 06 = 0006 + 0x0C = 0x12 (Speed 2)
18: 00 = 0000 + 0x0C = 0x0C (Speed 4)
19: 00 = 0000 + 0x0C = 0x0C (Speed 4)
20: 00 = 0000 + 0x0C = 0x0C (Speed 4)
21: FD = FFFD + 0x0C = 0x09 (Speed 5)
22: 00 = 0000 + 0x0C = 0x0C (Speed 4)
23: 03 = 0003 + 0x0C = 0x0F (Speed 3)
24: FD = FFFD + 0x0C = 0x09 (Speed 5)
25: 00 = 0000 + 0x0C = 0x0C (Speed 4)
26: FA = FFFA + 0x0C = 0x06 (Speed 6)
27: 03 = 0003 + 0x0C = 0x0F (Speed 3)
28: 00 = 0000 + 0x0C = 0x0C (Speed 4)
29: 00 = 0000 + 0x0C = 0x0C (Speed 4)
30: 00 = 0000 + 0x0C = 0x0C (Speed 4)
31: 09 = 0009 + 0x0C = 0x15 (Speed 1)


So given the table you can work out the percentage chance of rolling a particular speed:

Speed 1: 2/32 = 6.25% (Slowest)
Speed 2: 3/32 = 9.37%
Speed 3: 4/32 = 12.50%
Speed 4: 14/32 = 43.75% (Default Speed)
Speed 5: 4/32 = 12.50%
Speed 6: 3/32 = 9.37%
Speed 7: 2/32 = 6.25% (Fastest)

And there you have it.

Great work pasky.
I think all that stuff should be put in a special area of the ST wiki, so all these discoveries will be documented and not forgotten.

Kinda funny how the game is using this pseudo random number generator for a lot of things. Remember, the game always resets the number to 0x2472 on boot up, and then just constantly bit rotates it to the right. It uses it twice in the dizzy calculations.

Lets check out the dizzy code:



002EB4: move.b  ($2d1,A5), D0  // Grab a value from the random number generator
002EB8: rts
07A636: andi.w  #$1f, D0  // AND the value against 0x1F (Gives a result of 0-31)
07A63A: move.b  (A0,D0.w), D0  // This gets a number from another table to randomize the dizzy @ A0+D0 and loads it into D0
07A63E: move.w  D0, D6
07A640: lsl.w  #6, D0  // Bit Shift D0 to the left 6 times
07A642: lea    ($5a,PC), A0; ($7a69e)  // Load effective address of  0x7A69E into register A0
07A646: lea    (A0,D0.w), A0  //Load the sum of registers A0 + D0 into A0, A0 then becomes the base pointer
07A64A: jsr    $2e96.w
002E96: move.w  ($2d0,A5), D0
002E9A: andi.w  #$202, D0
002E9E: move.w  D0, D1
002EA0: lsr.w  #8, D0
002EA2: eor.b  D0, D1
002EA4: andi    #$ee, CCR
002EA8: beq    $2eae
002EAE: roxr.w  ($2d0,A5)  // Rotate the value at the random number generator, so the game won't get the same number twice
002EB2: moveq  #$0, D0  // Reset D0 to zero
002EB4: move.b  ($2d1,A5), D0  //  Load the value at the random number generator into D0
002EB8: rts
07A64E: andi.w  #$3e, D0 //  Do a AND operation of D0 against 0x3E (Always results in 0--62, even numbers only)
07A652: move.w  (A0,D0.w), ($60,A6)  // Store the value located at A0 + D0 into the players dizzy value


Ok, now to break this down further.

So the first thing going on is the game grabs a value from the random number generator. This number is AND’d against and a result of 0-31 occurs. The game then takes this result of 0-31 and loads a value from a table at 0x7A65E. This table has the following values:



01:  01
02:  03
03:  02
04:  02
05:  02
06:  02
07:  01
08:  02
09:  02
10:  01
11:  02
12:  00
13:  02
14:  01
15:  02
16:  01
17:  02
18:  02
19:  01
20:  02
21:  01
22:  02
23:  01
24:  02
25:  02
26:  02
27:  02
28:  01
29:  02
30:  03
31:  02


0x00 = 01/32 = 3.125%
0x01 = 09/32 = 28.125%
0x02 = 19/32 = 59.375%
0x03 = 02/32 = 6.25%

Now then. Once the game grabs the value from this table, it takes that value and bit shifts it to the left by 6. This is the same thing as multiplying the value by 64. So now we have:

00 = 00 (Angel table)
01 = 0x40 (64) (Star table)
02 = 0x80 (128) (Bird table)
03 = 0xC0 (192) (Reaper table)

The game then takes this value and adds it to register A0. This gives us our base pointer where we shall start at the dizzy table.

So we know where the dizzy pointer could POSSIBLY begin at (0x7A69E + 0x00 = 0x7A69E) and we know where the dizzy could POSSIBLY end (0x7A69E + 0xC0 = 0x7A75E)

The final piece of the puzzle is the next random value it obtains from the random number generator after it rotates it again. This value is AND’d against 0x3E and gives us a result of 0-62 (even numbers only). The reason they are even, is because the dizzy values are type word, so they contain 2 bytes.

So with this information, we can pinpoint the beginning and the end of the dizzy table!

0x7A69E <— We know this address is the lowest possible base pointer, so this is the beginning of the dizzy table.
0x7A75E <— We know this address is the highest possible base pointer. So now we add the maximum offset possible from the random number generator, 62.

0x7A75E+ 0x3E = 0x7A79C

So the games dizzy table is located at:

Start: 0x7A69E
End: 0x7A79C

And here it is:

http://img839.imageshack.us/img839/3201/dizzytable.png

Ok so now, the possiblities:

We can have a possibility of 4 base pointers.

0x7A69E + 0x00 = 0x7A69E (Angels)
0x7A69E + 0x40 = 0x7A6DE (Stars)
0x7A69E + 0x80 = 0x7A71E (Birds)
0x7A69E + 0xC0 = 0x7A75E (Reapers)

We have 32 possible offsets. 0-62, even numbers only (e.g. 0,2,4,6,8 etc…)

Format: Offset: Dizzy value

Base pointer 1 0x7A69E:



00:  003C
02:  003C
04:  003C
06:  003C
08:  003C
10:  003C
12:  003C
14:  003C
16:  005A
18:  005A
20:  005A
22:  005A
24:  005A
26:  005A
28:  005A
30:  003C
32:  005A
34:  005A
36:  005A
38:  005A
40:  005A
42:  005A
44:  005A
46:  005A
48:  003C
50:  003C
52:  003C
54:  003C
56:  003C
58:  003C
60:  003C
62:  003C


Angels:
0x003C = 17/32 = 53.125%
0x005A = 15/32 = 46.875%

Base Pointer 2 0x7A6DE:



00:  0078
02:  0078
04:  0078
06:  0078
08:  0078
10:  0078
12:  0078
14:  0078
16:  0078
18:  0078
20:  005A
22:  005A
24:  005A
26:  0078
28:  0078
30:  0078
32:  0078
34:  0078
36:  005A
38:  005A
40:  005A
42:  0078
44:  0078
46:  0078
48:  0078
50:  0078
52:  0078
54:  0078
56:  0078
58:  0078
60:  0078
62:  0078


Stars:
0x005A = 06/32 = 18.75%
0x0078 = 26/32 = 81.25%

Base Pointer 3 0x7A71E:



00:  0096
02:  0096
04:  0096
06:  0096
08:  0096
10:  0096
12:  0096
14:  0096
16:  0096
18:  0096
20:  0096
22:  00B4
24:  00B4
26:  00B4
28:  0096
30:  0096
32:  0096
34:  0096
36:  0096
38:  00B4
40:  00B4
42:  00B4
44:  0096
46:  0096
48:  0096
50:  0096
52:  0096
54:  0096
56:  0096
58:  0096
60:  0096
62:  0096


Birds:
0x0096 = 26/32 = 8******1.25%
***0x00B4 = 06/32 = ***18.75%

Base Pointer 4 0x7A81E:



00:  0096
02:  0096
04:  0096
06:  0096
08:  0096
10:  0096
12:  0096
14:  00D2
16:  00D2
18:  00D2
20:  00D2
22:  00D2
24:  00D2
26:  00D2
28:  00D2
30:  00D2
32:  00D2
34:  00D2
36:  00D2
38:  00D2
40:  00D2
42:  00D2
44:  00D2
46:  00D2
48:  0096
50:  0096
52:  0096
54:  0096
56:  0096
58:  0096
60:  0096
62:  0096


Reapers:
0x0096 = 15/32 = 46.875%
0x00D2 = 17/32 = 53.125%

So, now we have all of our numbers in order lets make an example, assume the first random number is 22 (0x16) after the AND and 2nd roll is 44 (0x2C) after the AND.

Example:
0x7A65E + 0x16 (1st random number) = 0x7A674

The value located at 0x7A674 is 0x01.

0x01 * 0x40 = 0x40 (Bit shift it left 6 times)

0x7A69E** + 0x40 = 0x7A6DE (Stars table)**

**0x7A6DE **+ 0x2C (2nd random number) = 0x7A70A

The value located at 0x7A70A = 0x0078 (120)

We rolled a stars dizzy with a value of 120.

So there we have it. Given the numbers we can determine the percentage chance of each dizzy:



Angels: 3.125%
Stars: 28.125%
Birds: 59.375%
Reapers: 6.25%


You can then further break this down on what value you will get based on the type of dizzy you rolled:



Angels:
Dizzy value 60: 53.125%
Dizzy value 90: 46.875%

Stars:
Dizzy value 90: 18.75%
Dizzy value 120: 81.25%

Birds:
Dizzy value 150: 81.25%
Dizzy value 180: 18.75%

Reapers:
Dizzy value 150: 46.875%
Dizzy value 210: 53.125%


I haven’t taken statistics in ages, so if anyone wants to calculate actual roll percentages to get a reaper of 150 or 210, etc… feel free to post them.

I think it’s kinda funny how you can get birds and be worse off than if you got reapers since some of the birds dizzy value is 180, and reapers can be 150.

Another mystery solved.

I’m in yo ST, reversing yo shit!

Great post, dude.

It is not really statistics, just probability. The one thing you need is conditional probability.

P(AB) = P(B|A)P(A)

Ai = chance of a type of dizzy (birds, reapers, etc).
Bi|Ai = chance of a value under that type.
AB = chance of both occurring.

Well, this gives


Angels (60): 1.660%
Angels (90): 1.465%
Stars (90): 5.273%
Stars (120): 22.852%
Birds (150): 48.242%
Birds (180): 11.133%
Reapers (150): 2.93%
Reapers (210): 3.32%


Edit: what does

**0x01 * 0x40 = 0x40 **

mean? I suppose those are hex, but what is *? I guess AND would yield **0x00.
**