1

**Scripting and Reverse Engineering / [FFVII] Enemy AI Script questions**

« **on:**2019-03-03 05:52:57 »

Hi Y'all! I'm currently working on the Enemy Module of my walkthrough, and I've decided that I want to break the AI script down for myself, instead of just blindly following the work the TFergusson has done in his Enemy Mechanics FAQ (the Eligor caused me quite a few problems).

Anyway, this is a fun project, and this webpage from the qhuimm wiki is the thing that I needed to fill in the blanks. I have had a few queries along the way though, so I've decided to ask them here.

1st Question:

Is this a check to see if ALL Players are in the front row, or if ANY of the Players are in the front row?

I had assumed it was ALL Players when I was looking at the script for MP/Guard Hound/Mono Drive/Grunt. All of these enemies uses AI script that's supposed to look at which row the player is in to choose their Attacks accordingly, but the script is illogical.

I've tried to work out what their patterns were supposed to be. Take the MP as an example, here is the opening setup for the AI Script:

So, seeing as TF has reported that flaws in the script prevent it from targetting th Players wors correctly, I wanted to see if I could fix the error, which is the AND function returning 0 for every check. If you swap the AND X functions with <- X, then it would make sense to me. If All Players are in the front row; LovalVar:PlayerRow <- 1, and if All Players are in the Back Row, then LovalVar:PlayerRow <- 2, and if the Player Row values are mixed, then it stays as 0. That works.

The thing is, I've just started looking at the Grashtrike, and the first line reads:

I had assumed that this meant that it was performing Silk ONLY if all players do not have Slow, however, after testing I can see that this actually means that it will perform Silk if ANY Player does not have Slow.

So, how does that translate to the MP? If at least one player is in the front, the var gets set to 1, and if any player is in the back, the var gets set to 2... that would mean that it can never be 0, and would bypass some of the script....

Here is the MP's full AI script for reference:

Here's how I have deciphered that, assuming that the AND functions are actually PUSH functions, and that the check in question is looking for ALL players in Front/Back Row (as opposed to any player):

I'm not 100% sure this is correct now. If anyone could clarify for me how the MP's correct AI script is supposed to work, that would be great!

Anyway, this is a fun project, and this webpage from the qhuimm wiki is the thing that I needed to fill in the blanks. I have had a few queries along the way though, so I've decided to ask them here.

1st Question:

Code: [Select]

`If ( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )`

Is this a check to see if ALL Players are in the front row, or if ANY of the Players are in the front row?

I had assumed it was ALL Players when I was looking at the script for MP/Guard Hound/Mono Drive/Grunt. All of these enemies uses AI script that's supposed to look at which row the player is in to choose their Attacks accordingly, but the script is illogical.

I've tried to work out what their patterns were supposed to be. Take the MP as an example, here is the opening setup for the AI Script:

Code: [Select]

`0x000LovalVar:PlayerRow <- 0`

0x006LovalVar:EnemyRow <- 0

0x00CLovalVar:SelfRow <- 0

0x012 If ( (BattleAddr(&AllEnemies).BattleAddr(&Row) == 0) )

0x012 {

0x01F LovalVar:EnemyRow <- LovalVar:EnemyRow AND 1

0x029 If ( (BattleAddr(&AllEnemies).BattleAddr(&Row) == 1) )

0x029 {

0x036 LovalVar:EnemyRow <- LovalVar:EnemyRow AND 2

0x040 If ( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )

0x040 {

0x04D LovalVar:PlayerRow <- LovalVar:PlayerRow AND 1

0x057 If ( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 1) )

0x057 {

0x064 LovalVar:PlayerRow <- LovalVar:PlayerRow AND 2

0x06E If ( (BattleAddr(&Self).BattleAddr(&Row) == 0) )

0x06E {

0x07B LovalVar:SelfRow <- 0

0x081 }

0x081 Else

0x081 {

0x084 LovalVar:SelfRow <- 1

So, seeing as TF has reported that flaws in the script prevent it from targetting th Players wors correctly, I wanted to see if I could fix the error, which is the AND function returning 0 for every check. If you swap the AND X functions with <- X, then it would make sense to me. If All Players are in the front row; LovalVar:PlayerRow <- 1, and if All Players are in the Back Row, then LovalVar:PlayerRow <- 2, and if the Player Row values are mixed, then it stays as 0. That works.

The thing is, I've just started looking at the Grashtrike, and the first line reads:

Code: [Select]

`If ( ( (BattleAddr(&AllPlayers).BattleAddr(&SlowStatus) == 0)`

I had assumed that this meant that it was performing Silk ONLY if all players do not have Slow, however, after testing I can see that this actually means that it will perform Silk if ANY Player does not have Slow.

So, how does that translate to the MP? If at least one player is in the front, the var gets set to 1, and if any player is in the back, the var gets set to 2... that would mean that it can never be 0, and would bypass some of the script....

Here is the MP's full AI script for reference:

Code: [Select]

`MAIN:`

0x000LovalVar:PlayerRow <- 0

0x006LovalVar:EnemyRow <- 0

0x00CLovalVar:SelfRow <- 0

0x012 If ( (BattleAddr(&AllEnemies).BattleAddr(&Row) == 0) )

0x012 {

0x01F LovalVar:EnemyRow <- LovalVar:EnemyRow AND 1

0x029 If ( (BattleAddr(&AllEnemies).BattleAddr(&Row) == 1) )

0x029 {

0x036 LovalVar:EnemyRow <- LovalVar:EnemyRow AND 2

0x040 If ( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )

0x040 {

0x04D LovalVar:PlayerRow <- LovalVar:PlayerRow AND 1

0x057 If ( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 1) )

0x057 {

0x064 LovalVar:PlayerRow <- LovalVar:PlayerRow AND 2

0x06E If ( (BattleAddr(&Self).BattleAddr(&Row) == 0) )

0x06E {

0x07B LovalVar:SelfRow <- 0

0x081 }

0x081 Else

0x081 {

0x084 LovalVar:SelfRow <- 1

0x08A If (Not (LovalVar:SelfRow) )

0x08A {

0x091 If (LovalVar:PlayerRow == 1)

0x091 {

0x099 LocalVar:Random <- 4

0x09F If (Not (Random MOD LocalVar:Random) )

0x09F {

0x0A8 If ( (LovalVar:PlayerRow AND 2 == 2) )

0x0A8 {

0x0B4 BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 1) )

0x0C3 }

0x0C3 Else

0x0C3 {

0x0C6 BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))

0x0CE LocalVar:EnemyAttack <- 272 (Machine Gun)

0x0D5 }

0x0D5 Else

0x0D5 {

0x0D8 If ( (LovalVar:PlayerRow AND 1 == 1) )

0x0D8 {

0x0E4 BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )

0x0F3 }

0x0F3 Else

0x0F3 {

0x0F6 BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))

0x0FE LocalVar:EnemyAttack <- 273 (Tonfa)

0x105 }

0x105 Else

0x105 {

0x108 }

0x108 Else

0x108 {

0x10B If (LovalVar:PlayerRow == 2)

0x10B {

0x110 LocalVar:Random <- 8

0x116 If (Not (Random MOD LocalVar:Random) )

0x116 {

0x11F If ( (LovalVar:PlayerRow AND 1 == 1) )

0x11F {

0x12B BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )

0x13A }

0x13A Else

0x13A {

0x13D BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))

0x145 LocalVar:EnemyAttack <- 273 (Tonfa)

0x14C }

0x14C Else

0x14C {

0x14F If ( (LovalVar:PlayerRow AND 2 == 2) )

0x14F {

0x15B BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 1) )

0x16A }

0x16A Else

0x16A {

0x16D BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))

0x175 LocalVar:EnemyAttack <- 272 (Machine Gun)

0x17C }

0x17C Else

0x17C {

0x17F }

0x17F Else

0x17F {

0x182 LocalVar:Random <- 2

0x188 If (Not (Random MOD LocalVar:Random) )

0x188 {

0x191 If ( (LovalVar:PlayerRow AND 2 == 2) )

0x191 {

0x19D BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 1) )

0x1AC }

0x1AC Else

0x1AC {

0x1AF BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))

0x1B7 LocalVar:EnemyAttack <- 272 (Machine Gun)

0x1BE }

0x1BE Else

0x1BE {

0x1C1 If ( (LovalVar:PlayerRow AND 1 == 1) )

0x1C1 {

0x1CD BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )

0x1DC }

0x1DC Else

0x1DC {

0x1DF BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))

0x1E7 LocalVar:EnemyAttack <- 273 (Tonfa)

0x1EE }

0x1EE Else

0x1EE {

0x1F1 POP(LovalVar:PlayerRow)

0x1F2 }

0x1F2 Else

0x1F2 {

0x1F5 If (LovalVar:PlayerRow == 1)

0x1F5 {

0x1FD LocalVar:Random <- 4

0x203 If (Not (Random MOD LocalVar:Random) )

0x203 {

0x20C If ( (LovalVar:PlayerRow AND 1 == 1) )

0x20C {

0x218 BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )

0x227 }

0x227 Else

0x227 {

0x22A BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))

0x232 LocalVar:EnemyAttack <- 273 (Tonfa)

0x239 }

0x239 Else

0x239 {

0x23C If ( (LovalVar:PlayerRow AND 2 == 2) )

0x23C {

0x248 BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 1) )

0x257 }

0x257 Else

0x257 {

0x25A BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))

0x262 LocalVar:EnemyAttack <- 272 (Machine Gun)

0x269 }

0x269 Else

0x269 {

0x26C }

0x26C Else

0x26C {

0x26F If (LovalVar:PlayerRow == 2)

0x26F {

0x274 LocalVar:Random <- 8

0x27A If (Not (Random MOD LocalVar:Random) )

0x27A {

0x283 If ( (LovalVar:PlayerRow AND 1 == 1) )

0x283 {

0x28F BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )

0x29E }

0x29E Else

0x29E {

0x2A1 BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))

0x2A9 LocalVar:EnemyAttack <- 273 (Tonfa)

0x2B0 }

0x2B0 Else

0x2B0 {

0x2B3 If ( (LovalVar:PlayerRow AND 2 == 2) )

0x2B3 {

0x2BF BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 1) )

0x2CE }

0x2CE Else

0x2CE {

0x2D1 BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))

0x2D9 LocalVar:EnemyAttack <- 272 (Machine Gun)

0x2E0 }

0x2E0 Else

0x2E0 {

0x2E3 }

0x2E3 Else

0x2E3 {

0x2E6 LocalVar:Random <- 6

0x2EC If (Not (Random MOD LocalVar:Random) )

0x2EC {

0x2F5 If ( (LovalVar:PlayerRow AND 1 == 1) )

0x2F5 {

0x301 BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )

0x310 }

0x310 Else

0x310 {

0x313 BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))

0x31B LocalVar:EnemyAttack <- 273 (Tonfa)

0x322 }

0x322 Else

0x322 {

0x325 If ( (LovalVar:PlayerRow AND 2 == 2) )

0x325 {

0x331 BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 1) )

0x340 }

0x340 Else

0x340 {

0x343 BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))

0x34B LocalVar:EnemyAttack <- 272 (Machine Gun)

0x352 }

0x352 Else

0x352 {

0x355 POP(LovalVar:PlayerRow)

0x356Perform(LocalVar:EnemyAttack, EnemyAttack)

0x35CSCRIPT END

Here's how I have deciphered that, assuming that the AND functions are actually PUSH functions, and that the check in question is looking for ALL players in Front/Back Row (as opposed to any player):

Code: [Select]

`/----------------------------------------------------------------------------\`

| BATTLE PLAN: (Unused) |

| |

| If All Players are in Back Row: 1/4 Chance use Machine Gun on Rnd Target |

| 3/4 Chance use <Tonfa> on Rnd Target |

| |

| If All Players are in Front Row: 1/8 Chance use <Tonfa> on Rnd Target |

| 7/8 Chance use Machine Gun on Rnd Target |

| |

| If the Players Rows are mixed: |

| If MP is in the Front Row: 1/2 Chance use Machine Gun on Rnd Target |

| 1/2 Chance use <Tonfa> on Rnd Target |

| |

| If MP is in the Back Row: 1/6 Chance use <Tonfa> on Rnd Target |

| 5/6 Chance use Machine Gun on Rnd Target |

\----------------------------------------------------------------------------/

I'm not 100% sure this is correct now. If anyone could clarify for me how the MP's correct AI script is supposed to work, that would be great!