[FFVII] Enemy AI Script questions

  • Thread starter Thread starter DynamixDJ
  • Start date Start date
Status
Not open for further replies.
lol, no it doesn't, that was a runscript error in my head lol

What's going on 'ere with the 2-Faced? It seems as though the 2-Faced should have a 100% chance of countering with one of its two attacks if it is the last enemy alive, however, after testing, that's not the case:

Code: [Select]
Code:
0x000LocalVar:0000 <-  (BitCount(BattleAddr(&2060).BattleAddr(&4060)) == BattleAddr(&2050).BattleAddr(&4060)) 0x014LocalVar:0000 <- LocalVar:0000 AND  NOT BattleAddr(&2060)0x020LocalVar:0020 <-  (LocalVar:0000.BattleAddr(&4120) == 107) 0x02E If ( ( (LocalVar:0000.BattleAddr(&4000) == 1)  And  (LocalVar:0020.BattleAddr(&4000) == 0) ) )0x02E {0x046  If (Not  (Random MOD 2) )0x046  {0x04E  BattleAddr(&2070) <- RandomBit(BattleAddr(&20A0))0x056  Perform("Self-destruct"[01ED], EnemyAttack)0x05C  BattleAddr(&2060).BattleAddr(&4020) <- 00x066  BattleAddr(&2060).BattleAddr(&4023) <- 00x070  BattleAddr(&2060).BattleAddr(&4022) <- 00x07A  BattleAddr(&2060).BattleAddr(&4024) <- 00x084  }0x084  Else0x084  {0x087  BattleAddr(&2070) <- BattleAddr(&20A0)0x08E  Perform("Cure3"[0002], EnemyAttack)0x093  BattleAddr(&2060).BattleAddr(&4020) <- 00x09D  BattleAddr(&2060).BattleAddr(&4023) <- 00x0A7  BattleAddr(&2060).BattleAddr(&4022) <- 00x0B1  BattleAddr(&2060).BattleAddr(&4024) <- 00x0BB }0x0BB Else0x0BB {0x0BE  If (Random MOD 8 == 0)0x0BE  {0x0C7  BattleAddr(&2070) <- RandomBit(BattleAddr(&20A0))0x0CF  Perform("Self-destruct"[01ED], EnemyAttack)0x0D5  BattleAddr(&2060).BattleAddr(&4020) <- 00x0DF  BattleAddr(&2060).BattleAddr(&4023) <- 00x0E9  BattleAddr(&2060).BattleAddr(&4022) <- 00x0F3  BattleAddr(&2060).BattleAddr(&4024) <- 00x0FD  }0x0FD  Else0x0FD  {0x100   }0x100   Else0x100   {0x103    If (Random MOD 8 == 1)0x103    {0x108   BattleAddr(&2070) <- BattleAddr(&20A0)0x10F   Perform("Cure3"[0002], EnemyAttack)0x114   BattleAddr(&2060).BattleAddr(&4020) <- 00x11E   BattleAddr(&2060).BattleAddr(&4023) <- 00x128   BattleAddr(&2060).BattleAddr(&4022) <- 00x132   BattleAddr(&2060).BattleAddr(&4024) <- 00x13C   }0x13C   Else0x13C   {0x13F    }0x13F    Else0x13F    {0x142   }0x142   Else0x142   {0x145 POP(Random MOD 8)0x146SCRIPT END
I *think* the problem lies with the use of 2050, which is "Active Actors". This means that the initial script for the first check only runs if the 2-Faced is the last actor alive, which abviously can't happen becuase you'd get a game over before that.

If 2050 was changed to 2090 ("Active Enemies") would that fix the problem? I'm gonna test to find out. I've noted down 2090 as active enemies, although technically 2090 is "all actors that the current actor regards as an ally", but to make it easier in my mind I've just jotted it as active enemies (and 20B0 as Active Players).

What one earth?? Why is the vanilla steam version giving my formation IDs that are 4 out? I'm fighting the Mt. Corel Bridge enemies and the forced Cokatolis battle in Corel Prison, all the ID#s are -4. The Formation in the well was #516 1x 2-Face and 2x Bandit instead of #520 4- 2 Faced Pnicer Attack.

What's up with that? I've checked the flevel.lgp in C:\Steam Library\steamapps\common\FINAL FANTASY VII\data\field and the formation IDs are correct.

Regardless, chenging 2050 to 2090 did not work, so I've no clue what's going on here....
Ah, maybe it is correct but it's only triggering the script if it's the last *2-Faced* alive, not the last enemy. I guess I'll have to fight the Land Worm #524 to get the 4x 2-Faced #520 lol
 
Last edited:
OK I'd somehow corrupted the scene.bin, I've sorted that now, but the corrected script did not work. Changing it to 2050 to 2080 didn't work either.

How di I make it so the 2-Faced passes the check at 0x02E and has a 100% chance of countering when it is the last 2 Faced alive as intended?

edit - I've been trying for hours to get the script working properly. I've tried changing:

0x014LocalVar:OtherActors <- LocalVar:OtherActors AND  NOT BattleAddr(&2060)

to

0x014LocalVar:OtherActors <- LocalVar:OtherActors AND  NOT BattleAddr(&2060).BattleAddr(&4060)

That took me a while to work out that the MASK opcode was needed. that didn't work.

Then I tried changing

If ( ( (LocalVar:0000.BattleAddr(&4000) == 1)  And  (LocalVar:0020.BattleAddr(&4000) == 0) ) )

to

If ( ( (LocalVar:0000.BattleAddr(&4000) == 1)  And  (LocalVar:0020.BattleAddr(&4000) == 1) ) )

that didn't work. Then I tried the same thing but the 2050 to 2080. Still didn't work. Then I tried a combination of all different things, and now I give up and I'm going to bed.

I just wanna make sure I understand this correctly

0x000LocalVar:0000 <-  (BitCount(BattleAddr(&2060).BattleAddr(&4060)) == BattleAddr(&2050).BattleAddr(&4060))

First this looks at the bit of the current character ID, and changes it to the bit of all active character iDs. So if there are four enemies (in the well) then there are 7 actors, and LocalVar:0000 to this point will be 127, or [0111 1111].

Then, the LovalVar:000 AND NOT 2060 toggles the bit of the current actor, so let's say we are dealing with the 2-Faced A (bit 3), then 0x04 gets toggled so we end up with 119, or [0111 0111].

The final bit that sets LocalVar:0020 sets the bitmask of 0000 to match any other actors that have the ID of 6B (107) so LocalVar 0020 becomes 112, or [0111 0000].

As it stands, it makes no sense because the check is to see if 6 other actors are dead, and the other 2-faced enemies are still alive. It's nonsensical, assuming I'm reading correctly. The point is, how do I fix it? The things I've tried should have worked but for now I can't see straight....

Lol, so much for blasting through all of the Corel Prison enemies in one night, I didn't even get past the first enemy haha. Been staring at the 2-faced for about 6 hours lol
 
Last edited:
OK, I've been completely misreading what 4060 is. TF has cleared it up (the wiki could do with being a little clearer):

  Formation ID
    Used to determine whether multiple monsters are part of the same 'group'.
    A Formation ID is visible in battles with multiple monsters of the
    same type by looking at the 'A'/'B'/'C' after the monster's name.
    So, "Hammer Blaster A" has a Formation ID of 0, while "Hammer Blaster B"
    has a Formation ID of 1.

I think I've analysed the problem correctly. This is what I've written up on the 2-Faced:

A flaw exists with the 2-Faced's AI Script. If the other (non 2-Faced) enemies that share the same 'Group ID' as the 2-Faced have been killed (2-Faced A = Group 0; 2-Faced B = 1 etc), then the 2-Faced *should* have had a 100% chance of activating its Counter Script, with a 50/50 chance of using either Attack. As it stands, the Counter script will check for the Death Status of the other 2-Faced in the same group, which is illogical due to the fact there can only be one "2-Faced A" (or "B" or "C").
 
Last edited:
What's going on with the Harpy's general counter script? It changes the player flag of the Chocobo the lower its HP. The only way the script runs as it should is if LocalVar:0020 is set to 3, seeing as the Chocobo is is the 4th Actor (1st Enemy). This only happens when the Harpy's HP is <= 25%.

This doesn't appear to be a glitch either. It seems to be intentional. God only knows what they were trying to do there...

If you manage to kill the Harpy while its HP is above 25% then the player flag for the Chocobo is changed, and the Chocobo only exists in the 1st enemy slot, slot "3" so the default Chocobo Capturing script is *not* executed from the harpy. Instead you have to wait for the Chocobo's ATB guage to fill, at which point the Chocobo will be captured. The Chocobo may say "Wark! Wark!" as if it is about to run away (instead of displaying "Captured a Chocobo"), but the script still ends the battle and gives you the Chocobo if this happens. TF reported that the Chocobo would flee the battle in this instance, but I haven't seen that happen yet.
 
I'm pretty convinced that 42B0 is not the Actor's AP value. I think it has something to do with the evade animation used.....
 
What is the following doing, found on Yang's Physical and Magical counter script?

PUSH (24) Type (01)
PUSH (00) Type (01)
 
I'm pretty convinced that 42B0 is not the Actor's AP value. I think it has something to do with the evade animation used.....
Yeah, totally NOT AP. 42E0 is EXP and 42C0 is Gil so I assumed that 42B0 was AP. It looks like it's a damage reaction animation. When a player misses the enemy just restarts their idle animation. There is no special "dodge" animation on enemies.

What is the following doing, found on Yang's Physical and Magical counter script?

PUSH (24) Type (01)
PUSH (00) Type (01)
That's actually queuing an action of command index 24. I have no idea what its affects are. If a flag is set, it sets some other flags. ??? TF's FAQ doesn't have a clue what it does either. It's not a graphical thing...

If BF2E08 bit 1 is active, set 9AEA78 to 1. That just skips a few steps in the command flow. I can't tell that it actually does anything.
 
Ah, I thought I was connecting some dots. The Black Bat has the action 'Dodge' [023A] which is not used via the AI script. I noticed that the Animation value was 4, so I made the assumption that the 42B0 assignment to 4 meant that it was using that animation.

A quick question regarding Added Effect: I'm fairly certain that you get a *fresh* 19% chance for each Status you have on your weapon (Hades) when you attack, as opposed to a single 19% chance to inflict every status (correct me if I'm wrong).

My question is this - if you pass the chance to inflict Stop on an enemy that already has Stop (or Paralyzed on an enemy that is Paralyzed etc), will the 'Stop Timer' reset to 0, or will it remain unaffected?
 
A quick question regarding Added Effect: I'm fairly certain that you get a *fresh* 19% chance for each Status you have on your weapon (Hades) when you attack, as opposed to a single 19% chance to inflict every status (correct me if I'm wrong).
That would make sense, except..... It looks like a few fuzzy logics are in play here. First, it would appear that multiple strikes would make multiple attempts to inflict. Second, it seems like there's a logical oddity that means you can only inflict, remove, or toggle effects, but it's not possible to do all three with vanilla data. Third, the code assumes that there is single inflict chance for all statuses. It's flat for all added-effect statuses. There's just as much chance that you can cause Death and Sleep on the same action.

My question is this - if you pass the chance to inflict Stop on an enemy that already has Stop (or Paralyzed on an enemy that is Paralyzed etc), will the 'Stop Timer' reset to 0, or will it remain unaffected?
Looks like the timer is only set when the status actually changes. I believe the logic of the battle is that infliction will miss if attempting to inflict a timed status on a target that already has that status. I just tested with Barrier and it missed an already barrier'd target.
 
What happens if a target's condition changes after the actor's ATB gauge has filled, but before its turn?

Let's say that the actor will use Attack A on a rnd target without Frog. Does the script run as soon as its ATB is filled, or does it run when it is about to perform its Attack? If it selects its target as the ATB is filled, and the target that is chosen becomes inflicted with Frog after the script is run, will it still try to use the attack on the target, even though the target has now become inflicted with Frog (where the check in the script targets an enemy without Frog)?
 
What happens if a target's condition changes after the actor's ATB gauge has filled, but before its turn?

Let's say that the actor will use Attack A on a rnd target without Frog. Does the script run as soon as its ATB is filled, or does it run when it is about to perform its Attack? If it selects its target as the ATB is filled, and the target that is chosen becomes inflicted with Frog after the script is run, will it still try to use the attack on the target, even though the target has now become inflicted with Frog (where the check in the script targets an enemy without Frog)?
Scripts are only run when they are about to be executed, i.e. the moment they are to be run, which is generally quite a while after the respective ATB gauge has been filled up. This makes redundancy scenarios like the one you envision impossible.

Let's say you are to fight six Touch Me in a pincer attack with just Cloud and these six Touch Me had their AI altered to use Frog Song every turn if at least one character is not a Frog and Frog Jab otherwise. Now, since it's a pincer attack, all of these six Touch Me start with their ATB gauges filled at the beginning of the battle. If scripts being run as soon as ATB gauges are filled were the case, these six Touch Me should all cast Frog Song on Cloud. However, that is not what would happen. The first Touch Me to act would cast Frog Song, turning Cloud into a Frog (let's say all attacks cannot miss), the second one would use Frog Jab, removing the Frog status from Cloud, then the third Touch Me would cast Frog Song again, the fourth one Frog Jab, the fifth one Frog Song and the sixth one Frog Jab again.
 
Thanks for clearing that up :) I suspected as much, I just needed to be sure
 
I'm a little bit confused what this script is doing with the Jumping:

Code: [Select]
Code:
0x008: PSHA (20A0) Type (02)0x00B: PSHA (4000) Type (00)0x00E: MASK0x00F: PUSH (01) Type (01)0x011: EQU0x012: CNTB0x013: PUSH (01) Type (01)0x008 If (BitCount( (BattleAddr(&20A0).BattleAddr(&4000) == 1) ) == 1)
It seems to be checking to see if exactly one player has Death, but after testing this check didn't seem to do anything; the Jumping appeared to have a 1/2 chance of using Club Sword regardless of how many players were dead (it's supposed to have a 1/4 chance of using the attack if the check passes).

TF seemed to think this check will always fail, aswell
 
As for 2040. The only enemies that use it are Bottomswell, Carry Armor/Left Arm/Right Arm, Elfadunk, Roulette Cannon, SOLDIER:2nd, Turks:Rude. Roulette Cannon and SOLDIER:2nd can appear in the same formation so they might be able to shed some light as to what the intended function is.
The script with the Roulette Cannon is basically a copy and paste of part of Bottomswell's script. It makes sure that it will only use the attack if two or more players are alive; if only one player is alive, it will not use the attack, and target selected will be 2040.

It looks like it's just a coincidence that the Soldier:2nd and roulette cannon appear together and both use 2040. Until now I've been able to dismiss the var, as it doesn't seem to be doing much. With the Soldier:2nd, however, there's quite a lot going on with 2040. I kind of need to know what's going on with it before I can move on....

Just decided to have a quick look at Carry Armor. Any idea what 4300 is doing?

2090. There's another new one that I don't know what it's doing
 
Last edited:
OK, after testing it seems that 4300 is used when a playable character is imprisoned within Carry Armor's Arms, and is used to damage the imprisoned actor, amongst other things. I'll look into it more thoroughly when I actually get to Carr Armor.

For now though, I've spent quite a lot of time with Cheat Engine, and 2040 never seems to be any value other than 0. I think I was correct in my original assumption that this will reset the var to 0, as that's exactly what it seems to be doing.

-edit- The more I look at it, the more I'm sure that 2040 is used to clear the last attacker that is set as the SOLDIER:2nd's target. The same thing is used for the Elfadunk.
 
Last edited:
I'm a little bit confused what this script is doing with the Jumping:

Code: [Select]
Code:
0x008: PSHA (20A0) Type (02)0x00B: PSHA (4000) Type (00)0x00E: MASK0x00F: PUSH (01) Type (01)0x011: EQU0x012: CNTB0x013: PUSH (01) Type (01)0x008 If (BitCount( (BattleAddr(&20A0).BattleAddr(&4000) == 1) ) == 1)
It seems to be checking to see if exactly one player has Death, but after testing this check didn't seem to do anything; the Jumping appeared to have a 1/2 chance of using Club Sword regardless of how many players were dead (it's supposed to have a 1/4 chance of using the attack if the check passes).

TF seemed to think this check will always fail, aswell
I have been trying and trying to figure this one out. It seems that the 4X family SKIPS character 0. I'm still looking into this, but in my test character 0 was definitely dead and the count of successes was still 0.

EDIT:
I got it! 20A0 is the LIVING enemy actors. So testing the death status against living targets will always result in a 0.
 
Last edited:
^^ Awesomness dude, that one was bugging me for a while. It did inspire me to work with Cheat Engine a bit more closely. I've mapped out all of the six enemy slots LocalVar addresses, which helps to see what is going on in real-time. I've also found the address that deals with the next Formation ID, so I can do tests on any formation I want without having to load save files or mess around with MR lol.
 
OK, after staring at Ultimate Weapon for hours, I now know precisely what's going on. The A0 opcode was throwing me off for a bit, but I figured it all out in the end (without having to ask for help lol). It's interesting to note that a small bug occurs once its HP has dropped to less than 65,536; it won't store the lower byte's value and will undercut the stored HP by up to 255.

Anyway, I've observed that the 0x004 flag in 0x0C1F of the save map is not a flag indicating that Ultimate has been killed; it's a flag that signifies that its HP has reached 'critical' and is ready to move onto the Cosmo Canyon phase. 0x001 is used to indicate that it has been killed, which is also used by the Battle Square, but technically 0x001 should be "Ultimate killed", and 0x004 should be "Ultimate HP Critical" or something to that effect.

edit I didn't realise how easy it was to make an account and edit the wiki (at http://wiki.ffrtt.ru). I'm gonna start updating the save map as and when.

double edit - there is no glitch with Ultimate Weapon; I had the value stored as 2 bytes, not 3 (dur). Cheat Engine doesn't allow for three bytes, just 2 or 4, which means if I set it to 4 bytes, the value overspills into the next byte. I've had to separate out its HP into three separate bytes, which isn't too hard to manage: (Byte3 * 65,536) + (Byte2 * 256) + (Byte1).
 
Last edited:
Any notion as to why Diamond Weapon is trying to "RunScript(15)", when there is no script for Custom Event 8? I wonder what the intention was there...
 
Dunno. The only other enemy that I can think that uses RunScript is Mystery Ninja. However, she uses it to end the battle after 10 turns without triggering the death script that the WM scripts are checking for. Maybe Diamond was going to do something similar (or a placeholder was added in case something came up). As it is the WM script to trigger the battle just engages the battle and begins an FMV after the battle is over. Maybe there could have been other outcomes to the battle. That would have been neat.
 
Status
Not open for further replies.
Back
Top