Pinged you on AIM but no response. I guess I smell
So basically the way the stat growths work is like this:
Every stat, for every character, has an expected "start" value (SV), "end" value (EV), and "growth type" (GT)
"None" is a special growth type that always returns 0, so it doesn't have a table associated with it... so lets ignore that one.
All other GTs have 29 entries associated with them (because the expected EV is for what we expect the stat to be at for level 30.) Each entry in a GT has two parts, which are basically expressions of percent growth (PG). The first value in the table is a representation of how much "Percent Total Growth" the character should have by this level. The second value is a representation how much "Percent incremental growth" the characters should gain for this specific level.
The crux here is that the first value is used more as a way to "boost" a stat if it falls behind due to bad randomization. The second value is the primary determinant of how much the character gains on a given level up.
So, the game basically takes the difference (EV - SV) and multiplies that times the incremental growth value for that level's entry in the GT table. Then it compares that to the expected total growth value and adds an extra 1 to whatever it determined you should gain based on the (EV - SV) * incremental growth value.
So for example, Bowie SDMN has a start HP value of 12, end value of 60, growth of linear.
When going from level 1 to 2, it will look at the 1st entry in the Linear GT... which turns out is in decimal "8" and "8"
The game will take 60 - 12 = 48 and multiply that times the second entry in the table so... 48 * 8 = 384...
Then generate a random number between 0 and 128, (lets say 108) add that to your value so ... 384 + 108 = 492
Then generate a random number between 0 and 128, (lets say 20) subtract that from your value so ... 492 - 20 = 472
Then add 128 so ... 472 + 80 = 552
Then divide by 256 so ... 552 / 256 = 2 (okay it actually equals 2.15625 but there are no floating points here, so no decimals they just fall off!)
(Call this the incremental growth = 2)
Then we take 60 - 12 = 48, and multiply it times the first entry in the table so... 48 * 8 = 384
Then add 128... 384 + 128 = 512
Then divide by 256... 512 / 256 = 2
(Call this the long term growth = 2)
Then add your base value 12 back into your incremental and long term results
(incremental result) so ... 2 + 12 = 14
(long term result) so ... 2 + 12 = 14
If the long term result is >= the incremental result, add 1 to your incremental growth. Here they are equal (2 and 2) so we add 1... so
2 + 1 = 3
Thus Bowie will gain 3 HP this level up.
Here is the entire "Linear" table for your reference:
8 - 8
17 - 9
26 - 9
35 - 9
44 - 9
52 - 8
61 - 9
70 - 9
79 - 9
88 - 9
97 - 9
105 - 8
114 - 9
123 - 9
132 - 9
141 - 9
150 - 9
158 - 8
167 - 9
176 - 9
185 - 9
194 - 9
203 - 9
211 - 8
220 - 9
229 - 9
238 - 9
247 - 9
256 - 9
Note that in the table the difference between the "total growth" value for one level and its previous level is generally equal to the "incremental growth" value for the level.
In other words, 211 - 203 = 8 for the values...
203 - 9
211 - 8
This isn't always the case though!
Here's the actual code:
ROM:000096DC loc_96DC: ; CODE XREF: sub_96BA+16j
ROM:000096DC and.w #7,d2
ROM:000096E0 subq.w #1,d2
ROM:000096E2 muls.w #$74,d2 ; 't'
ROM:000096E6 movea.l (p_19toFigureOut).l,a0 ; related to level up stat gains ?
ROM:000096EC add.w d2,a0
ROM:000096EE move.w d5,d2
ROM:000096F0 subq.w #1,d2
ROM:000096F2 lsl.w #2,d2
ROM:000096F4 add.w d2,a0
ROM:000096F6 move.w (a0)+,d0
ROM:000096F8 move.w (a0)+,d7
ROM:000096FA sub.w d3,d4
ROM:000096FC mulu.w d7,d4
ROM:000096FE
ROM:000096FE loc_96FE: ; CODE XREF: sub_96BA+20j
ROM:000096FE move.w #$80,d6 ; 'Ç'
ROM:00009702 jsr Randomize ; randomize a few bits in D7 based on RAM:dea4
ROM:00009706 add.w d7,d4
ROM:00009708 jsr Randomize ; randomize a few bits in D7 based on RAM:dea4
ROM:0000970C sub.w d7,d4
ROM:0000970E add.w #$80,d4 ; 'Ç'
ROM:00009712 lsr.w #8,d4
ROM:00009714 move.w d4,d6
ROM:00009716 movem.w (sp)+,d1-d5
ROM:0000971A sub.w d3,d4
ROM:0000971C mulu.w d4,d0
ROM:0000971E add.w #$80,d0 ; 'Ç'
ROM:00009722 lsr.w #8,d0
ROM:00009724 add.w d3,d0
ROM:00009726 add.w d6,d1
ROM:00009728 cmp.w d0,d1
ROM:0000972A bge.s loc_972E
ROM:0000972C addq.w #1,d6
ROM:0000972E
ROM:0000972E loc_972E: ; CODE XREF: sub_96BA+70j
ROM:0000972E move.w d6,d1
ROM:00009730 movem.l (sp)+,d0/d2-a0
ROM:00009734 rts
ROM:00009734 ; End of function sub_96BA
Also, I could probably write a python chunk up to do this algorithm... not super familiar with ruby though I'm sure I could figure it out if you really wanted me to (since those seem to be the languages you guys are using in the CP)