Miscellaneous Forums > Scripting and Reverse Engineering

FF7.exe battle routine questions

<< < (3/3)

ergonomy_joe:
ok, so there is this function at 0x0043526A:

--- Code: ---//counters:refresh(1)?
void C_0043526A() {
struct {
int dwDecr;//bp_40
int dwCurCounter;//bp_3c
int dwNewHP;//bp_38
int dwHPMax;//bp_34
int bp_30;
int dwHPIncr;//bp_2c
int dwCountIncr_1;//bp_28
int dwDecr_2_3;//bp_24
int dwCountIncr_0;//bp_20
int dwDecr_def;//bp_1c
int j;//bp_18
int i;//bp_14//local_5
int bp_10;//local_4
int *bp_0c;//local_3
int *bp_08;//local_2
int dwDecr_0;//bp_04//local_1
}lolo;

//-- --
lolo.bp_10 = D_009AE8A0;
D_009AE8A0 ^= 1;
lolo.bp_0c = D_009AE928[lolo.bp_10];
lolo.bp_08 = D_009AE978[lolo.bp_10];
//-- --
lolo.dwDecr_0 = D_009AE8D0[lolo.bp_10] >> 13;
D_009AE8D0[lolo.bp_10] &= 0x1fff;
//-- --
for(lolo.i = 0; lolo.i < 0xa; lolo.i ++) {
lolo.bp_30 = D_009A8B10.f_0000[lolo.i].f_06;
lolo.dwCountIncr_1 = lolo.bp_0c[lolo.i];
lolo.dwCountIncr_0 = lolo.bp_08[lolo.i];
//-- some counter --
D_009AE900[lolo.i] += lolo.dwCountIncr_1;
lolo.dwDecr_def = D_009AE900[lolo.i] >> 13;
D_009AE900[lolo.i] &= 0x1fff;
//-- some counter --
D_009AE8D8[lolo.i] += lolo.dwCountIncr_0;
lolo.dwDecr_2_3 = D_009AE8D8[lolo.i] >> 12;
D_009AE8D8[lolo.i] &= 0xfff;
//-- counter for HP recovery? --
D_009AE8A8[lolo.i] += lolo.dwCountIncr_1 * lolo.bp_30;
lolo.dwHPIncr = D_009AE8A8[lolo.i] >> 15;
D_009AE8A8[lolo.i] &= 0x7fff;
//-- --
lolo.bp_0c[lolo.i] = 0;
lolo.bp_08[lolo.i] = 0;
//-- --
if((D_009AB0A0.f_03c[lolo.i].f_00 & 1) == 0) {//else 00435485
lolo.dwNewHP = D_009AB0A0.f_03c[lolo.i].dwHP + lolo.dwHPIncr;
lolo.dwHPMax = D_009AB0A0.f_03c[lolo.i].dwHPMax;
if(lolo.dwNewHP < 0) {//else 00435468
D_009AB0A0.f_03c[lolo.i].f_00 |= 1;
C_005C89E2(lolo.i, lolo.i, 1);
C_005C8931();//execute "other" scripts
lolo.dwNewHP = 0;
//goto 00435476
} else if(lolo.dwNewHP > lolo.dwHPMax) {
lolo.dwNewHP = lolo.dwHPMax;
}
D_009AB0A0.f_03c[lolo.i].dwHP = lolo.dwNewHP;
}
//-- some other states(poison, ...) --
for(lolo.j = 0; lolo.j < 0x10; lolo.j ++) {
lolo.dwCurCounter = D_009A8B10.f_0000[lolo.i].f_10[lolo.j];
if(lolo.dwCurCounter) {//else 00435531
switch(lolo.j) {
case 0:
lolo.dwDecr = lolo.dwDecr_0;
break;
case 2:
case 3:
lolo.dwDecr = lolo.dwDecr_2_3;
break;
default:
lolo.dwDecr = lolo.dwDecr_def;
}//end switch
lolo.dwCurCounter -= lolo.dwDecr;
if(lolo.dwCurCounter < 0)
lolo.dwCurCounter = 0;
if(lolo.dwCurCounter == 0)
C_00435139(2, lolo.i, lolo.j, lolo.j);//...:add event?
D_009A8B10.f_0000[lolo.i].f_10[lolo.j] = lolo.dwCurCounter;
}
}//end for
//-- --
}//end for
}
--- End code ---

From what I understand, it refreshes some counters and take their upper bits to refresh some more counters. But near the end of the function, we have "dwCurCounter", and if it reaches 0, a function at 0x00435139 is called with such parameters as: the actor id, and the event type, respectively "i" and "j" in our case (a value of 6 means poison). Function at 0x00435139 manages a list of events which are called by the function at 0x004351BD:

--- Code: ---//...:add event?
void C_00435139(int bp08, int bp0c, int bp10, int bp14) {
int local_2;
struct t_battle_inner_4 *local_1;

local_2 = D_009AE890[bp08];
local_1 = &(D_009AE280[bp08][local_2]);
if(local_1->f_00 == (unsigned char)0xff) {
local_1->f_02 = bp14;
local_1->f_01 = bp10;
local_1->f_00 = bp0c;
D_009AE890[bp08] = C_004351A3(local_2);//make next id?
}
}

//make next id?
int C_004351A3(int bp08) {
bp08 ++;
bp08 &= 0x7f;

return bp08;
}

//...:flush events?
void C_004351BD(int bp08) {
struct {
void (**local_3)(int, int);
int local_2;
struct t_battle_inner_4 *local_1;
}lolo;

C_004351C3:
lolo.local_2 = D_009AE880[bp08];
lolo.local_1 = &(D_009AE280[bp08][lolo.local_2]);
if(lolo.local_1->f_00 != (unsigned char)0xff) {
lolo.local_3 = D_007C2AC0[bp08];
lolo.local_3[lolo.local_1->f_01](lolo.local_1->f_00, lolo.local_1->f_02);
lolo.local_1->f_00 = 0xff;
D_009AE880[bp08] = C_004351A3(lolo.local_2);//make next id?
goto C_004351C3;
}
}
--- End code ---

In the case of poison, the callback is at 0x00434DB0:

--- Code: ---//(callback)2_06 "poison hit"
void C_00434DB0(int bp08, int _p0c) {
if(D_009AB0A0.f_03c[bp08].f_00 & 8) {
D_009A8B10.f_0000[bp08].f_10[6] = 0xa;
C_00432687(bp08, 3, 0x23, 0, 0);//add battle command?
}
}
--- End code ---

What does it do ?
It reloads a poison-specific counter D_009A8B10.f_0000[bp08].f_10[6], and creates an attack command that will take HP from the character actually poisonned.

A little complex I would say  :-/

nfitc1:
@ergonomy_joe

That's how poison works. It takes a delta of one of the actors' timers (I forget which one) and when it reaches a certain value it queues an action which inflicts poison damage to the poisoned. There are a lot of battle "events" that are mostly used by animations, but it can be used for almost anything.

quantumpencil:
Ok, so I got some time look into this. calling the sub (enqueue action?) called within the poison routine with the same arguments called there can be used to, unsurprisingly, re-create the poison effect in random situations.

enqueue_event(attacker_id, 3, 23, 0, 0) are pushed onto the stack prior to the call. The parameters are (as far as I'm able to tell):

attacker_id, some kind of queue priority, command index (poison has index 23h, which is capable of dealing damage, but I'm not sure where the data for these indexes are) attack index, and another 0 (I don't know what this one is for yet)

I have major need for this mechanic, as it is a much better way to implement, for instance, an overheated status (create an attack that does massive fire damage and enqueue it when the physical formula is used by an overheated actor).

So does anyone know what the last argument in the enqueue function is, and...
does anyone know how where the game is looking for the action attack metadata of poison (i.e, the fact that it uses formula 3, it's poison elemental, etc).

Thanks again guys =)

ergonomy_joe:
here is the detail of the function at 0x00432687:

--- Code: ---//add battle command?
void C_00432687(int bp08, int bp0c, int bp10, int bp14, int bp18) {
struct t_battle_local_08_HHHH local_2;

local_2.f_00 = bp0c;
local_2.f_02 = bp08;
local_2.f_03 = bp10;
local_2.f_04 = bp14;
local_2.f_06 = bp18;
C_0043258A(&local_2);//queue battle command?
}
--- End code ---

the details about "struct t_battle_local_08_HHHH" (sorry about the name) is already given in the wiki, at "Queued Actions"(although there seems to be a confusion with offsets 0,1 and 2). According to it, the last two parameters are respectively "target mask" and "action index". Not sure that helps though  :-\

quantumpencil:
Update: I've finished reversing most of the EventList and BattleActionQueue enqueue/pop logic (thanks ergonomy_joe =)), and while I still haven't totally figured out how the game is managing "time" --  which is the last piece to get dots working, I have thus far successfully:

Created a new mechanic for cloud where he has a chance to use double cut or combo an attack with death-blow (like added cut if the added cut were deathblow) after every physical attack, without materia (the first done by re-writing the attack index prior to the main command handling routine, and the second done by generating an event corresponding to "Deathblow on the current target" of priority 0 immediately after a "attack" from an actor identified as Cloud is issued.

Found the data that needs to be edited with to enable a class mod (There's a giant struct of player character data that holds enabled commands. By modifying it in BattleMain (after it is populated), you can pretty much give commands to characters by default without materia, i.e Yuffie can be given innate skills.  Similarly, you can disable access to certain commands, magic, summons or E-Skills in battle). Proved by giving Yuffie innate steal and no access to Deathblow, even if the materia is equipped. 

Replaced Sense with Provoke (Could probably do this without replacing Sense too, but it'd be harder): Enemies record the "provoker" mask in their unused AIVariables data block. If the provoke bit is set, then before any action, change the target mask to that AI stored value. If Sense is used, don't call the damage from the pointer table, but instead just set the bit associated with provoke  in the target, and store the actor mask in the targets AI block.

Once I figure out how the  game does all this clock magic, DoT affects will be next =D

Navigation

[0] Message Index

[*] Previous page

Go to full version