Author Topic: [FF8] Monster stats and level formulas  (Read 9441 times)

myst6re

  • *
  • Posts: 639
  • Light King of the Savegame - Field Master - FF8.fr
    • View Profile
    • FF8.fr
[FF8] Monster stats and level formulas
« on: 2016-04-14 20:28:28 »
Hello everyone! I need your help this time: In dat files, monster stats are represented by 4 numbers, which are used in an equation with monster level. Does anyone know the right equation for each monster attribute?

What we know (from Ifrit editor):

Code: [Select]
HP = Math.floor(number_0 * level * level / 20) + number_1 * level + number_2
EXP = number_0 * (5 * (level - level_party_avg)/level_party_avg + 4))
EXP_LAST = Math.floor(number_1 * (5 * (level - level_party_last)/level_party_last + 4))
# If the result > 255, STR = 255
STR = level * number_0 / 10 + level / number_1 - level / number_3 + number_2) / 4
MAG = Math.floor(level * number_0) + Math.floor(level/number_1) - Math.floor(Math.floor(Math.floor(level*level/2)/number_2)/4))
VIT = level * number_0 + Math.floor(level/number_1) + number_2 - Math.floor(level/number_3)
SPR = level * number_0 + Math.floor(level/number_1) + number_2 - Math.floor(level/number_3)
SPD = level * number_0 + Math.floor(level/number_1) + number_2 - Math.floor(level/number_3)
EVA = level * number_0 + Math.floor(level/number_1) + number_2 - Math.floor(level/number_3)

I think HP and EXP are good. For the rest...

JBedford128

  • *
  • Posts: 113
    • View Profile
Re: [FF8] Monster stats and level formulas
« Reply #1 on: 2016-04-14 21:04:05 »
Edit: Okay, so I'll preface this by saying sometimes they match the Ultimania stats (which give stats at Lv1, every ten levels, and the max level), sometimes they are way off.  I do know that some changes were made from JP>EN so this could be a product of that. Also I'm suspicious of my math.floors, I may have added them at random to see what gave me the right results.

Here's the formulas I used (in Lua) for the FFWiki, probably with more brackets than I need. You can see the results of these and judge whether they are correct on the individual enemy articles on the wiki: example.

STR, MAG:
local calc = math.floor(((lv*a/10)+(lv/b)-math.floor((lv^2)/(2*d))+c)/4)
return math.min(calc, 255)

VIT, SPR, SPD, EVA:
local calc = math.floor(lv/b)-math.floor(lv/d)+(lv*a)+c
return math.min(calc, 255)

« Last Edit: 2016-04-14 21:22:58 by JBedford128 »

JWP

  • *
  • Posts: 194
    • View Profile
Re: [FF8] Monster stats and level formulas
« Reply #2 on: 2016-04-14 21:17:56 »
I noticed when poking around the memory at the actual values that some of the values in Ifrit were 1 off but it's probably a rounding issue, I could take a look in the code and see where it pushes the value from.

EDIT:
Function that gets the values in FF8_EN.exe (Steam version) is at 0x48C3F0 and takes 3 parameters:
param 1: level
param 2: pointer to section 7 of a dat file
param 3: integer (0 = str, 1 = mag (think this actually vit), 2 = vit (think this is actually mag), 3 = spr, 4 = spd, 5 = eva)

Is the information on http://wiki.qhimm.com/view/FF8/FileFormat_DAT#Section_7:_Informations_.26_stats in the correct order?
Since I'd expect the formulas for str and mag to be the same rather than str and vit - I'm guessing mag and vit should be swapped since the order they usually appear in is str, vit, mag, spr, spd, luck, eva (although monsters have no luck).

For the following sections:
ESI = pointer to the 4 bytes needed, so ESI = 1st byte, ESI+1 = 2nd byte, ESI+2 = 3rd byte, ESI+3 = 4th byte
arg.1 = level
result ends up in EAX
ok, here is the ASM for the mag, spr, spd and eva cases (it gets capped to 255 after this but I've omitted that part) - I think this is actually for vit, spr, spd and eva, see note above:
Code: [Select]
0048C447  |.  8B7C24 10     MOV EDI,DWORD PTR SS:[ARG.1]             ; default case of switch FF8_EN.48C43D
0048C44B  |.  33C9          XOR ECX,ECX
0048C44D  |.  8A4E 03       MOV CL,BYTE PTR DS:[ESI+3]
0048C450  |.  8BC7          MOV EAX,EDI
0048C452  |.  99            CDQ
0048C453  |.  F7F9          IDIV ECX
0048C455  |.  33DB          XOR EBX,EBX
0048C457  |.  8A5E 01       MOV BL,BYTE PTR DS:[ESI+1]
0048C45A  |.  8BC8          MOV ECX,EAX
0048C45C  |.  8BC7          MOV EAX,EDI
0048C45E  |.  99            CDQ
0048C45F  |.  F7FB          IDIV EBX
0048C461  |.  33D2          XOR EDX,EDX
0048C463  |.  8A16          MOV DL,BYTE PTR DS:[ESI]
0048C465  |.  0FAFD7        IMUL EDX,EDI
0048C468  |.  2BC1          SUB EAX,ECX
0048C46A  |.  33C9          XOR ECX,ECX
0048C46C  |.  8A4E 02       MOV CL,BYTE PTR DS:[ESI+2]
0048C46F  |.  03C2          ADD EAX,EDX
0048C471  |.  03C1          ADD EAX,ECX

if you're not using integer arithmetic, this is the same as: floor(lvl/b) - floor(lvl/d) + a*lvl + c - which is the same as JBedford128's formula above


ASM for str and vit (it gets capped to 255 after this but I've omitted that part): - I think this is actually for str and mag, see note above
Code: [Select]
0048C480  |.  8B7C24 10     MOV EDI,DWORD PTR SS:[ARG.1]             ; cases 0, 2 of switch FF8_EN.48C43D
0048C484  |.  33C9          XOR ECX,ECX //ecx = 0
0048C486  |.  8BC7          MOV EAX,EDI //eax = lvl
0048C488  |.  8A4E 03       MOV CL,BYTE PTR DS:[ESI+3] //cl = d
0048C48B  |.  0FAFC7        IMUL EAX,EDI EAX //edx:eax = lvl^2
0048C48E  |.  99            CDQ //sign extend eax into edx
0048C48F  |.  F7F9          IDIV ECX //eax = floor(lvl^2/d)
0048C491  |.  33DB          XOR EBX,EBX //ebx = 0
0048C493  |.  8A5E 01       MOV BL,BYTE PTR DS:[ESI+1] bl = b
0048C496  |.  99            CDQ //sign extend eax into edx
0048C497  |.  2BC2          SUB EAX,EDX //not sure about this one edx will either be 0xFFFFFFFF or 0x00000000 eax ends up as eax or eax + 1
0048C499  |.  8BC8          MOV ECX,EAX //ecx = floor(lvl^2/d)
0048C49B  |.  8BC7          MOV EAX,EDI //eax = lvl
0048C49D  |.  99            CDQ //sign extend eax into edx
0048C49E  |.  F7FB          IDIV EBX //eax = floor(lvl/b)
0048C4A0  |.  D1F9          SAR ECX,1 //ecx = floor(floor(lvl^2/d)/2)
0048C4A2  |.  8BD8          MOV EBX,EAX //ebx = floor(lvl/b)
0048C4A4  |.  B8 67666666   MOV EAX,0x66666667 //eax = 0x66666667
0048C4A9  |.  2BD9          SUB EBX,ECX //ebx = floor(lvl/b) - floor(floor(lvl^2/d)/2)
0048C4AB  |.  33C9          XOR ECX,ECX //ecx = 0
0048C4AD  |.  8A0E          MOV CL,BYTE PTR DS:[ESI] cl = a
0048C4AF  |.  0FAFCF        IMUL ECX,EDI //edx:ecx = lvl*a
0048C4B2  |.  F7E9          IMUL ECX //edx:eax = lvl*a*0x66666667
0048C4B4  |.  C1FA 02       SAR EDX,2 //edx = floor(lvl*a*0x66666667/2^34)
0048C4B7  |.  8BC2          MOV EAX,EDX //eax = floor(lvl*a*0x66666667/2^34)
0048C4B9  |.  03D3          ADD EDX,EBX //edx = floor(lvl*a*0x66666667/2^34) + floor(lvl/b) - floor(floor(lvl^2/d)/2)
0048C4BB  |.  C1E8 1F       SHR EAX,1F //eax = sign bit of eax
0048C4BE  |.  03C2          ADD EAX,EDX //not sure what effect this has
0048C4C0  |.  33D2          XOR EDX,EDX //edx = 0
0048C4C2  |.  8A56 02       MOV DL,BYTE PTR DS:[ESI+2] dl = c
0048C4C5  |.  03C2          ADD EAX,EDX //eax = floor(lvl*a*0x66666667/2^34) + floor(lvl/b) - floor(floor(lvl^2/d)/2) + c
0048C4C7  |.  99            CDQ //sign extend eax into edx
0048C4C8  |.  83E2 03       AND EDX,0x00000003 //edx = either 0x00000000%4 or 0xFFFFFFFF%4, so 0 or 3 depending on sign of eax
0048C4CB  |.  03C2          ADD EAX,EDX //eax = floor(lvl*a*0x66666667/2^34) + floor(lvl/b) - floor(floor(lvl^2/d)/2) + c
0048C4CD  |.  C1F8 02       SAR EAX,2//eax = floor((floor(lvl*a*0x66666667/2^34) + floor(lvl/b) - floor(floor(lvl^2/d)/2) + c)/4)

So something like:
floor((floor((lvl*a*0x66666667)/2^34) + floor(lvl/b) - floor(floor(lvl^2/d)/2) + c)/4)
as a rough guess.
There are a couple of things I'm not sure about above though and probably some mistakes in the annotations above. Someone that's better at signed arithmetic would probably help :P.
The formula actually looks fairly close to what JBedford128 had (note that 0x66666667/2^34 is ~1/10 - it's so close that you could probably just use divide by 10 and it probably was in the original code - I suspect it's a compiler optimization for division by 10) - the only main difference is the flooring.
I'd say that:
floor((floor(lvl*a/10) + floor(lvl/b) - floor(floor(lvl^2/d)/2) + c)/4) probably works.
or if you're using integer arithmetic:
((lvl*a)/10 + lvl/b - (lvl^2/d)/2 + c)/4
I do wonder if this differs from the PSX code though.
« Last Edit: 2016-04-18 09:56:24 by JWP »

myst6re

  • *
  • Posts: 639
  • Light King of the Savegame - Field Master - FF8.fr
    • View Profile
    • FF8.fr
Re: [FF8] Monster stats and level formulas
« Reply #3 on: 2016-04-15 21:21:48 »
Edit: Okay, so I'll preface this by saying sometimes they match the Ultimania stats (which give stats at Lv1, every ten levels, and the max level), sometimes they are way off.  I do know that some changes were made from JP>EN so this could be a product of that. Also I'm suspicious of my math.floors, I may have added them at random to see what gave me the right results.

I compared french PS and japanese version, here are the differences (in hex):

Tri-Point
Mag (or vit?) | JP: 01 14 64 02 | FR: 01 14 23 02

Left Orb (or Right?)
Spd | JP: 00 01 14 0a | FR: 00 01 05 03

Right Orb (or Left?)
Spd | JP: 00 01 28 0a | FR: 00 01 0a 03

Jumbo Cactuar
HP | JP: 00 00 3c 00 | FR: 00 00 1e 1e

Anakronox
HP | JP: 00 00 04 1e | FR: 00 00 03 1e

Left Probe / Right Probe
Spd | JP: 00 03 32 06 | FR: 00 03 1e 06

Trauma
Spd | JP: 00 01 24 01 | FR: 00 01 18 01

Fujin (119)
Str | JP: 46 05 55 82 | FR: 41 05 3c 82

Raijin (120)
Str | JP: 50 05 78 82 | FR: 50 05 6e 82


Thanks for your answers JWP and JBedford128 ! that is really helpful

myst6re

  • *
  • Posts: 639
  • Light King of the Savegame - Field Master - FF8.fr
    • View Profile
    • FF8.fr
Re: [FF8] Monster stats and level formulas
« Reply #4 on: 2016-04-17 19:53:34 »
Ok, I can confirm STR/MAG formula:

Code: [Select]
result = floor((floor(level * a/10) + floor(level / b) - floor((level * level) / (d * 2)) + c)/4)
result = min(result, 255)

And VIT/SPR/SPD/EVA formula:

Code: [Select]
result = floor(level / b) - floor(level / d) + a * level + c
result = min(result, 255)

My EXP formula seems correct too (where "a" the value from the DAT file):

Code: [Select]
EXP = floor(a * (5 * (level - level_party_avg) / level_party_avg + 4))
EXP_LAST = floor(a * (5 * (level - level_party_last) / level_party_last + 4))

Note: values on the Ultimania guide seems to be EXP / 4 and EXP_LAST / 4, for level 1 members.

But still, the HP formula is sometimes wrong, like JWP says, in Iron Giant for example. Now I use (I was wrong in my first post):

Code: [Select]
HP = floor(a * lvl * lvl/20) + (a + c * 100) * lvl + b * 10 + d * 1000

Someone know more about this? JWP?
« Last Edit: 2016-04-21 21:18:46 by myst6re »

JWP

  • *
  • Posts: 194
    • View Profile
Re: [FF8] Monster stats and level formulas
« Reply #5 on: 2016-04-17 23:52:49 »
looking at the code:
Code: [Select]
0048BD33  |.  8A57 1B       MOV DL,BYTE PTR DS:[EDI+1B] //dl = d
0048BD36  |.  8886 CB7BD201 MOV BYTE PTR DS:[ESI+1D27BCB],AL //line not used in calculation
0048BD3C  |.  33C0          XOR EAX,EAX //eax = 0
0048BD3E  |.  33C9          XOR ECX,ECX //ecx = 0
0048BD40  |.  8D1492        LEA EDX,[EDX*4+EDX] //edx = d*5
0048BD43  |.  8A47 18       MOV AL,BYTE PTR DS:[EDI+18] al = a
0048BD46  |.  8A8E CC7BD201 MOV CL,BYTE PTR DS:[ESI+1D27BCC] //cl = lvl
0048BD4C  |.  8D2C92        LEA EBP,[EDX*4+EDX] //ebp = d*25
0048BD4F  |.  33D2          XOR EDX,EDX //edx = 0
0048BD51  |.  8A57 19       MOV DL,BYTE PTR DS:[EDI+19] //dl = b
0048BD54  |.  8D14AA        LEA EDX,[EBP*4+EDX] //edx = 100*d + b
0048BD57  |.  8D2C92        LEA EBP,[EDX*4+EDX] //ebp = 500*d + 5*b
0048BD5A  |.  33D2          XOR EDX,EDX //edx = 0
0048BD5C  |.  8A57 1A       MOV DL,BYTE PTR DS:[EDI+1A] //dl = c
0048BD5F  |.  8D1492        LEA EDX,[EDX*4+EDX] //edx = c*5
0048BD62  |.  8D1492        LEA EDX,[EDX*4+EDX] //edx = c*25
0048BD65  |.  8D1490        LEA EDX,[EDX*4+EAX] //edx = c*100 + a
0048BD68  |.  0FAFC1        IMUL EAX,ECX //eax = a*lvl
0048BD6B  |.  0FAFC1        IMUL EAX,ECX //eax = a*lvl*lvl
0048BD6E  |.  0FAFD1        IMUL EDX,ECX //edx = (c*100 + a)*lvl
0048BD71  |.  8BC8          MOV ECX,EAX //ecx = a*lvl*lvl
0048BD73  |.  B8 67666666   MOV EAX,66666667 //eax = 0x66666667
0048BD78  |.  8D2C6A        LEA EBP,[EBP*2+EDX] //ebp = 1000*d + 10*b + (c*100 + a)*lvl
0048BD7B  |.  F7E9          IMUL ECX //edx:eax = (a*lvl*lvl)*0x66666667
0048BD7D  |.  C1FA 03       SAR EDX,3 //edx = floor((a*lvl*lvl)*0x66666667/2^35)
0048BD80  |.  8BC2          MOV EAX,EDX //eax = floor((a*lvl*lvl)*0x66666667/2^35)
0048BD82  |.  03D5          ADD EDX,EBP //edx = floor((a*lvl*lvl)*0x66666667/2^35) + 1000*d + 10*b + (c*100 + a)*lvl
0048BD84  |.  C1E8 1F       SHR EAX,1F //add 1 if the result was negative (this isn't going to happen, it's probably due to them putting the result in a signed int)
0048BD87  |.  03C2          ADD EAX,EDX

Think this gives:
HP = floor(a*lvl*lvl/20) + 1000*d + 10*b + (c*100 + a)*lvl
or if you're using integers:
(a*lvl*lvl)/20 + 1000*d + 10*b + (c*100 + a)*lvl

This looks like it matches with what you have and also the formula I gave several years ago in this post: http://forums.qhimm.com/index.php?topic=6961.msg86799#msg86799
Quote
BYTE HP[4]; //HP = (HP[0]*x*x)/20 + (HP[0] + 100*HP[2])*x + 10*HP[1] + 1000*HP[3], where x = monster lvl

You mentioned MAG twice in your post, did you mean VIT/SPR/SPD/EVA?
« Last Edit: 2016-04-18 09:54:49 by JWP »

myst6re

  • *
  • Posts: 639
  • Light King of the Savegame - Field Master - FF8.fr
    • View Profile
    • FF8.fr
Re: [FF8] Monster stats and level formulas
« Reply #6 on: 2016-04-18 19:50:19 »
You mentioned MAG twice in your post, did you mean VIT/SPR/SPD/EVA?

Yes, sorry for the confusion, and I fixed this on the wiki page yesterday.

So for the PC version, we're sure that is the right formula :) . It would be good to know if the PS version shares the same formulas.

JWP

  • *
  • Posts: 194
    • View Profile
Re: [FF8] Monster stats and level formulas
« Reply #7 on: 2016-04-19 10:23:49 »
Ah nice  :), I should probably see about getting a wiki account rather than just dumping info all over the forum  :-P.
I'm curious as to what project you're using the formulas for :-P.
I'd guess that the original PS game was written in C/C++ and they copied functions like that for the PC version but I could be wrong.

I'm tempted to take a look at the PS version to see if some of the data structures in memory are similar and it might be easier to find information with snapshotting.
In addition to this, seeing the same routines in 2 different ASM implementations might help with reverse engineering.
PSX looks like it has a pretty good R3000 debugger to do that sort of thing.

While looking through the forums for FF8 posts (to see if there was anything I didn't know), I came across this old post: http://forums.qhimm.com/index.php?topic=3723.msg51764#msg51764
It seems that people have looked through the section of code before - although I think their interpretation is slightly off since there aren't enough brackets - divisions end up taking priority, which causes different results when using integer division:
Code: [Select]
4*4/3 = 4 * floor(4/3) = 4
(4*4)/3 = floor(16/3) = 5
Note the division order doesn't actually make a difference due to the nested division property mentioned below:
Code: [Select]
floor(floor(x/y)/z) = floor(x/(y*z)) = floor(x/(z*y)) = floor(floor(x/z)/y)
So many things just seem to get lost in time on this forum :(.

btw, I noticed that the STR/MAG formula can be simplified slightly due to this property:
https://en.wikipedia.org/wiki/Floor_and_ceiling_functions#Nested_divisions
it might be possible to simplify it further but I'm not too familiar with that sort of mathematics.

Code: [Select]
floor((floor(level * a/10) + floor(level / b) - floor(level * level / d / 2) + c)/4)
« Last Edit: 2016-04-19 15:20:49 by JWP »

myst6re

  • *
  • Posts: 639
  • Light King of the Savegame - Field Master - FF8.fr
    • View Profile
    • FF8.fr
Re: [FF8] Monster stats and level formulas
« Reply #8 on: 2016-04-21 21:17:36 »
I'm curious as to what project you're using the formulas for :-P.

A french website that will become the FF8 reference guide.

I removed nested floors, it works well :)
For now, Iron Giant is the only monster I know with errors, according to the Ultimania, maybe the error isn't on our side...

Code: [Select]
Values : 30, 0, 6, 0
Level   Ultimania   Known formula
LV1     663 HP      631 HP
LV10    6900 HP     6450 HP
LV20    14400 HP    13200 HP
LV30    22500 HP    20250 HP
LV40    31200 HP    27600 HP
LV50    40500 HP    35250 HP
LV60    50400 HP    43200 HP
LV70    60900 HP    51450 HP
LV80    72000 HP    60000 HP
LV90    83700 HP    68850 HP
LV100   96000 HP    78000 HP

There is nothing special about Iron Giant HPs on IA scripts.
« Last Edit: 2016-04-21 21:20:38 by myst6re »

JWP

  • *
  • Posts: 194
    • View Profile
Re: [FF8] Monster stats and level formulas
« Reply #9 on: 2016-04-21 21:56:34 »
I forced a battle against a lvl 20 Iron Giant by using encounter code 248 (8420 when entering it) in the debug room and memory editing the level just after it loaded using a memory breakpoint.
It had 13200 HP so either it's an issue with the guide or the guide is based on a different version of the game.
« Last Edit: 2016-04-21 22:26:55 by JWP »

JBedford128

  • *
  • Posts: 113
    • View Profile
Re: [FF8] Monster stats and level formulas
« Reply #10 on: 2016-04-21 22:38:28 »
There are a few enemies with different HPs in the Ultimania guide to what I calculated. These enemies are Torama, Elnoyle, Iron Giant, and Behemoth. I think that's it.

I believe I determined their (simplified) formula myself. On their FFWiki pages I list them.

So I figured Behemoth's Japanese formula was 3*lv*lv + 260*lv + 35000. Which I woud assume is [60, 0, 2, 35] in the data. Unless the Ultimania is just plain wrong. It might be worth mentioning that the English guides also use the same HP values as the Ultimania, but then again, they also use images of red Gerogero so I assume they were just given the Japanese information when writing the guides.

Kitsune

  • *
  • Posts: 19
    • View Profile
Re: [FF8] Monster stats and level formulas
« Reply #11 on: 2022-01-15 14:30:51 »
Hi Mystere,

I try to calculate the characters Stats by formul but I don't have the right formul. By example for the Strenght of Squall at Level 7 (starting level in game) I have 6 in STR but in game this is 17.


Do you have the right formuls? STR, VIT, MAG,etc.


Thanks.