I've been looking at some battle code in the PSP version, and I'm seeing some major issues with how rand() is being used. Curious to see if anyone would be able to check on the PS1 side to see if it's the same..?
Code: Select all
((rand() * 100) / 0x7fff) % 100) >= iVar2
Assembly version below --
Code: Select all
LAB_09ebafc4 XREF[1]: 09ebaf8c(j)
09ebafc4 fd 28 24 0e jal rand
09ebafc8 00 00 00 00 _nop
09ebafcc 80 18 02 00 sll v1,v0,0x2
09ebafd0 21 18 62 00 addu v1,v1,v0
09ebafd4 80 10 03 00 sll v0,v1,0x2
09ebafd8 21 10 62 00 addu v0,v1,v0
09ebafdc 80 28 02 00 sll a1,v0,0x2
09ebafe0 01 80 02 3c lui v0,0x8001
09ebafe4 03 00 42 34 ori v0,v0,0x3
09ebafe8 18 00 45 00 mult v0,a1
09ebafec c2 27 05 00 srl a0,a1,0x1f
09ebaff0 10 18 00 00 mfhi v1
09ebaff4 21 18 65 00 addu v1,v1,a1
09ebaff8 83 1b 03 00 sra v1,v1,0xe
09ebaffc 64 00 02 24 li v0,0x64
09ebb000 21 18 64 00 addu v1,v1,a0
09ebb004 1a 00 62 00 div v1,v0
09ebb008 10 10 00 00 mfhi v0
09ebb00c 2a 08 50 00 slt at,v0,s0
09ebb010 3a 00 20 14 bne at,zero,LAB_09ebb0fc
09ebb014 00 00 00 00 _nop
09ebb018 21 20 60 02 move a0,s3
09ebb01c 84 ed 7a 0e jal set_status
09ebb020 21 28 40 02 _move a1,s2
09ebb024 35 00 00 10 b LAB_09ebb0fc
09ebb028 00 00 00 00 _nop
So, on the PSP at least, what happens is: (This logic might be slightly off, this stuff hurts my brain.)
rand returns a number between 0 and 0x7FFFFFFF. The x100 is done via shifts, so numbers over the 32 bits overflow and get lost. (Still a random number I guess) Then the division of 0x7FFF (??? lol) is done through multiplication, does some shifting, and then extends the signed bit. So, if the original number was greater than 0x7FFF0000, the division by 0x7FFF results in a number greater than 0xFFFF and it sign extends it to 0xFFFF----. If its less, you get a normal number between 0 and 0x7FFF.
Essentially, before the modulo operator, we have a value in the range of (-0x7FFF, 0x7FFF), which means the modulo 100 gives us a result with the range of (-100,100), instead of the likely desired [0,100).
Since the success is checking for values greater than the rate given, for 30, a successful roll would be a number from 30 to 99. An unsuccessful roll would be 0 to 29, but since we have an extra 99 negative numbers, its -99 to 29. Essentially... cutting the success rate of all status effects in half.
Now, as if this wasn't enough... I was looking at some of the AI/monster functions, and there's even weirder stuff in here.
In the module files, where the monster data is, at least on the PSP, 0x30 of that structure contains a index that gets converted into a function pointer when the data is loaded. This monster function has... idk what all, but it also includes "AI" determination of which attack to use, for enemies with multiple attacks.
I was looking at the function for the zombie dragon. There's a different kind of malformed rand call in there:
Code: Select all
if ((rand()* 100) / 0x7fff < 0x47) {
uVar2 = 0xffffffff;
}
else {
*(code **)(param_1 + 0xc) = FUN_09e13f30;
uVar2 = 1;
}
Code: Select all
LAB_09e13eb0 XREF[1]: 09e13e98(j)
09e13eb0 fd 28 24 0e jal rand
09e13eb4 00 00 00 00 _nop
09e13eb8 80 18 02 00 sll v1,v0,0x2
09e13ebc 21 18 62 00 addu v1,v1,v0
09e13ec0 80 10 03 00 sll v0,v1,0x2
09e13ec4 21 10 62 00 addu v0,v1,v0
09e13ec8 80 20 02 00 sll a0,v0,0x2
09e13ecc 01 80 02 3c lui v0,0x8001
09e13ed0 03 00 42 34 ori v0,v0,0x3
09e13ed4 18 00 44 00 mult v0,a0
09e13ed8 c2 1f 04 00 srl v1,a0,0x1f
09e13edc 10 10 00 00 mfhi v0
09e13ee0 21 10 44 00 addu v0,v0,a0
09e13ee4 83 13 02 00 sra v0,v0,0xe
09e13ee8 21 10 43 00 addu v0,v0,v1
09e13eec 47 00 41 28 slti at,v0,0x47
09e13ef0 05 00 20 54 bnel at,zero,LAB_09e13f08
09e13ef4 ff ff 02 24 _li v0,-0x1
09e13ef8 e1 09 02 3c lui v0,0x9e1
09e13efc 30 3f 42 24 addiu v0,v0,0x3f30
09e13f00 0c 00 a2 ae sw v0=>FUN_09e13f30,0xc(s5)
09e13f04 01 00 02 24 li v0,0x1
What the heck? Scrolling through I saw a lot of rands like this, though most of them seemed to be 50/50 checks, which the code... surprisingly actually accomplishes.
So it looks like the zombie dragon should have had about a ~30% chance of using the breath attack, but in actuality uses it around ~50%. No wonder that fight can be annoyingly difficult at times.
Anyway, I was wondering if anyone could check on the PS1 and see if that plays out identically? Or is this a bug specific to the PSP port (and likely therefore the remaster as well)? I was thinking of loading it up and checking myself, but figured it would be faster to type this out since others are more familiar with the PS1 code.