Author Topic: [FFVII] Enemy AI Script questions  (Read 29967 times)

DynamixDJ

  • *
  • Posts: 240
  • 1111 1111 0111
    • View Profile
[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:

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!
« Last Edit: 2019-03-03 06:50:05 by DynamixDJ »

codemann8

  • *
  • Posts: 55
    • View Profile
Re: [FFVII] Enemy AI Script questions
« Reply #1 on: 2019-03-03 09:53:45 »
The first question I'd have is where you're getting the text version of these scripts from? I'm assuming these are probably disassembled from ProudClod. The word of caution there is to worry about the lesser known opcodes that have a higher chance of inaccuracies, although i don't know if any of these lesser known opcodes are used for MP, not only lesser known opcodes but also special cases that maybe weren't considered.  It's possible that some of these outputs could have errors, but NFITC would be the one to comment on how confident he is with the accuracy of that output.

It also looks like there are several layers of IFs, but I don't see any closing brackets, which is important because that will determine whether they are nested or sequential.

DynamixDJ

  • *
  • Posts: 240
  • 1111 1111 0111
    • View Profile
Re: [FFVII] Enemy AI Script questions
« Reply #2 on: 2019-03-03 10:35:42 »
I'm first disassembling it via Proud Clod, then using the Find-All > Replace-All feature in my .txt editor to assign the argument values to the variable mapping as you see. This is where I'm getting the info for the mapping:

http://wiki.ffrtt.ru/index.php/FF7/Battle/Battle_Scenes/Battle_AI_Addresses

Here is a list of variables that I've been working with, I'm adding to the list as and when I need to:

Code: [Select]
BattleAddr(&2050) = BattleAddr(&ActiveActors)
BattleAddr(&2060) = BattleAddr(&Self)
BattleAddr(&2070) = BattleAddr(&TargetSelected)
BattleAddr(&2080) = BattleAddr(&AllEnemies)
BattleAddr(&2090) = BattleAddr(&ActiveEnemies)
BattleAddr(&20A0) = BattleAddr(&AllPlayers)
BattleAddr(&20B0) = BattleAddr(&ActivePlayers)



BattleAddr(&4000) = BattleAddr(&DeathStatus)
BattleAddr(&4001) = BattleAddr(&NearDeathStatus)
BattleAddr(&4002) = BattleAddr(&SleepStatus)
BattleAddr(&4003) = BattleAddr(&PoisonStatus)
BattleAddr(&4004) = BattleAddr(&SadnessStatus)
BattleAddr(&4005) = BattleAddr(&FuryStatus)
BattleAddr(&4006) = BattleAddr(&ConfusionStatus)
BattleAddr(&4007) = BattleAddr(&SilenceStatus)
BattleAddr(&4008) = BattleAddr(&HasteStatus)
BattleAddr(&4009) = BattleAddr(&SlowStatus)
BattleAddr(&400A) = BattleAddr(&StopStatus)
BattleAddr(&400B) = BattleAddr(&FrogStatus)
BattleAddr(&400C) = BattleAddr(&SmallStatus)
BattleAddr(&400D) = BattleAddr(&SlowNumbStatus)
BattleAddr(&400E) = BattleAddr(&PetrifyStatus)
BattleAddr(&400F) = BattleAddr(&RegenStatus)
BattleAddr(&4010) = BattleAddr(&BarrierStatus)
BattleAddr(&4011) = BattleAddr(&MBarrierStatus)
BattleAddr(&4012) = BattleAddr(&ReflectStatus)
BattleAddr(&4013) = BattleAddr(&DualStatus)
BattleAddr(&4014) = BattleAddr(&ShieldStatus)
BattleAddr(&4015) = BattleAddr(&DSentenceStatus)
BattleAddr(&4016) = BattleAddr(&ManipulateStatus)
BattleAddr(&4017) = BattleAddr(&BerserkStatus)
BattleAddr(&4018) = BattleAddr(&PeerlessStatus)
BattleAddr(&4019) = BattleAddr(&ParalyzedStatus)
BattleAddr(&401A) = BattleAddr(&DarknessStatus)
BattleAddr(&401B) = BattleAddr(&DualDrainStatus)
BattleAddr(&401C) = BattleAddr(&DeathForceStatus)
BattleAddr(&401D) = BattleAddr(&ResistStatus)
BattleAddr(&401E) = BattleAddr(&LuckyGirlStatus)
BattleAddr(&401F) = BattleAddr(&ImprisonedStatus)


BattleAddr(&4026) = BattleAddr(&Row)
BattleAddr(&4060) = BattleAddr(&PlayerID)
BattleAddr(&4080) = BattleAddr(&IdleAnimID)
BattleAddr(&4088) = BattleAddr(&DamagedAnimID)
BattleAddr(&40D0) = BattleAddr(&LastAttacker)
BattleAddr(&4100) = BattleAddr(&Def)
BattleAddr(&4110) = BattleAddr(&MDef)
BattleAddr(&4140) = BattleAddr(&CurrentMP)
BattleAddr(&4150) = BattleAddr(&MaxMP)
BattleAddr(&4160) = BattleAddr(&CurrentHP)
BattleAddr(&4180) = BattleAddr(&MaxHP)

DynamixDJ

  • *
  • Posts: 240
  • 1111 1111 0111
    • View Profile
Re: [FFVII] Enemy AI Script questions
« Reply #3 on: 2019-03-04 00:55:50 »
BattleAddr(&4020)
BattleAddr(&4022)
BattleAddr(&4023)
BattleAddr(&4024)

What do these do? They can be found after the Action 0138 Escape is performed, which is used by Prowler, Bandit and Vice:


Code: [Select]
BattleAddr(&TargetSelected) <- BattleAddr(&Self)
Perform("Escape"[0138], EnemyAttack)
BattleAddr(&Self).BattleAddr(&4020) <- 0
BattleAddr(&Self).BattleAddr(&4023) <- 0
BattleAddr(&Self).BattleAddr(&4022) <- 0
BattleAddr(&Self).BattleAddr(&4024) <- 0

Edit - Same vars are found on the Hell House [2C] (1st Form). I guess this has something to do with stat transfers when an enemy leaves the battle

Double edit - Perhaps.... Exp, Gil, AP and....... ???? - Another edit - Nope, AP is 0x42BO, Gil is 0x42C0, and EXP is 0x42E0. The mystery remains...

Another edit - OK, I have a suspicion that they could be setting the main enemy battle flags:

BattleAddr(&4020) = 0x0001 OFF = Invisible
BattleAddr(&4021) = 0x0002 OFF = Groups the enemies together during a PINCER ATTACK.
BattleAddr(&4022) = 0x0004 OFF = ???? (Bad Rap Sample and Poodler Sample only)
BattleAddr(&4023) = 0x0008 OFF = Non-Targetable
BattleAddr(&4024) =  0x0010 OFF = Disables Main AI Script

This would fit, seeing as the four variables that are used are 4020 4022 4023 & 4024, meaning the 0x0002 flag (4021) wouldn't be set during the instances in which an enemy removes itself from battle, as it wouldn't need to. Edit - the quimm wiki's description for 4021 is "Ally of current actor", which could fit....

Bear in mind that with the Hell House and Aero Combatant are turning the same vars ON for the subsequent enemy that takes it's place:

Aero Combatant (1st form):
Code: [Select]
PRE-BATTLE:

0x000LocalVar:TransformID <-  (BitCount(BattleAddr(&Self).BattleAddr(&PlayerID)) == BattleAddr(&ActiveActors).BattleAddr(&PlayerID))
0x014LocalVar:TransformID <-  (LocalVar:TransformID.BattleAddr(&4120) == 47)
0x022SCRIPT END


MAIN:

0x000BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x008 If (Not  (Random MOD 3) )
0x008 {
0x010 Perform("Kick"[0158], EnemyAttack)
0x016 }
0x016 Else
0x016 {
0x019 Perform("Propeller"[0159], EnemyAttack)
0x01FSCRIPT END


GENERAL COUNTER:

0x000 If (Not  (Random MOD 2) )
0x000 {
0x008 BattleAddr(&TargetSelected) <- BattleAddr(&Self)
0x00F Perform([011F], EnemyAttack)
0x015 BattleAddr(&Self).BattleAddr(&4020) <- 0
0x01F BattleAddr(&Self).BattleAddr(&4023) <- 0
0x029 LocalVar:TransformID.BattleAddr(&4020) <- 1
0x033 LocalVar:TransformID.BattleAddr(&4023) <- 1
0x03D LocalVar:TransformID.BattleAddr(&4022) <- 1
0x047 LocalVar:TransformID.BattleAddr(&4024) <- 1
0x051 TransferStatus(BattleAddr(&Self), LocalVar:TransformID)
0x058 BattleAddr(&Self).BattleAddr(&4020) <- 0
0x062 BattleAddr(&Self).BattleAddr(&4023) <- 0
0x06C BattleAddr(&Self).BattleAddr(&4022) <- 0
0x076 BattleAddr(&Self).BattleAddr(&4024) <- 0
0x080SCRIPT END


DEATH COUNTER:

0x000LocalVar:TransformID.BattleAddr(&4020) <- 0
0x00ALocalVar:TransformID.BattleAddr(&4023) <- 0
0x014LocalVar:TransformID.BattleAddr(&4022) <- 0
0x01ELocalVar:TransformID.BattleAddr(&4024) <- 0
0x028SCRIPT END

I can't see anywhere else in the script that handles enabling of the AI script/visibility/targetability of the new enemy.

On a side note, that 0x0004 Enemy Battle Flag that I'm unsure of could be disabling battle rewards, seeing as Bad Rap Sample and Poodler Sample do not give any rewards....
« Last Edit: 2019-03-04 08:39:52 by DynamixDJ »

nfitc1

  • *
  • Posts: 3011
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Re: [FFVII] Enemy AI Script questions
« Reply #4 on: 2019-03-04 16:01:08 »
Oh this is the thread for me. :D

Regarding to the (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) conundrum. This, as you noticed, translates to if there are NO players in the back row. This has to do with the data type that is returned by the AllPlayers address. It's a 2X type variable meaning it returns a list (of 3 in this case) values with the state of each player. When that is masked by row, it changes those 3 values to each players' row values. In this case, if ANY of them are zero then the condition passes. That's just the nature of the 2X variable type and conditional checks. I wish there were a non-ugly way to indicate that in the AI editor.

Now to the 4022 variable, I believe this unsetting this value indicates the enemy does not need to be defeated in order for the battle to end. I think some set pieces set this in their init scripts. Check the helicopter in the Rufus battle and the truck and tiny bronco in the Palmer battle. Just going by memory here, but I'm pretty sure they unset the 4022 flag. A few bosses do as well IIRC. Check Mystery Ninja Custom 7 script and Diamond Weapon's Death script.

DynamixDJ

  • *
  • Posts: 240
  • 1111 1111 0111
    • View Profile
Re: [FFVII] Enemy AI Script questions
« Reply #5 on: 2019-03-04 23:43:40 »
Ah, so it's not "All players in the Front row", it's  "No Players in the Back Row", which is the same thing when it comes to rows. With the Slow check, it's not as I initially thought "All Players without Slow", but rather "No Player with Slow". This fits, thanks!

In regards to 4023 and 4024, I'm 99% certain that I'm correct in thinking that 4023 is Actor's Targetability, and 4024 is Actor's AI Script enabled/disabled, after having dissected Reno:Turks I and his Pyramids.

If I'm right in thinking that, then that would mean that 4020 - 4024 does indeed reflect the Actor's Battle Flags as mentioned. And, if I'm right about that. then that means that you have solved the mystery of 0x0004 Enemy Battle Flag; as mentioned, Poodler Sample and Bad Rap sample are the only enemies in the game that go into the fight with this flag disabled. Your description of the actor needeing to be defetaed for the Battle to end fits perfectly, not just with the P/BR Sample, but also with the instances in which I've witnessed the 4023 flag be enabled/disabled via the AI script.

I'm going to jot down 4023 as you've described, and I'm gonna keep going through each enemy sequentially keeping my eyes open for consistency, but I'm fairly happy the shoe fits. Thanks!

I must say, this is quite a fun project to do, seeing as I don't think anyone has worked with the AI sctipt down in the way I currently am, which is to try to break each enemy's "Battle Plan" down into something the layman can understand. TF did a fantastic job, but I'm trying to do one better.

Take the Eligor, for example. I've converted this:

Code: [Select]
40 - [28] Eligor



LocalVar:0000 = LocalVar:OpenAttack
LocalVar:0020 = LocalVar:Stage
LocalVar:0040 = LocalVar:1stChr
LocalVar:0060 = LocalVar:2ndChr
LocalVar:0080 = LovalVar:3rdChr

BattleAddr(&2060) = BattleAddr(&Self)
BattleAddr(&2070) = BattleAddr(&TargetSelected)
BattleAddr(&20A0) = BattleAddr(&AllPlayers)
BattleAddr(&4000) = BattleAddr(&DeathStatus)
BattleAddr(&4002) = BattleAddr(&SleepStatus)
BattleAddr(&4007) = BattleAddr(&SilenceStatus)
BattleAddr(&4026) = BattleAddr(&Row)
BattleAddr(&4140) = BattleAddr(&CurrentMP)
BattleAddr(&4160) = BattleAddr(&CurrentHP)
BattleAddr(&4180) = BattleAddr(&MaxHP)



MAIN:

0x000 If (Not  (LocalVar:OpenAttack) )
0x000 {
0x007 If (Random MOD 3 == 0)
0x007 {
0x010 If ( (BattleAddr(&Self).BattleAddr(&CurrentMP) >= MPCost(Silence [000D])) )
0x010 {
0x01E BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&SilenceStatus) == 0) )
0x02D Perform("Silence"[000D], EnemyAttack)
0x032 }
0x032 Else
0x032 {
0x035 If (Not  (LocalVar:Stage) )
0x035 {
0x03C If ( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x03C {
0x049 BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x058 }
0x058 Else
0x058 {
0x05B BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x063 Perform("Sword Attack"[014A], EnemyAttack)
0x069 }
0x069 Else
0x069 {
0x06C LocalVar:1stChr <- FlagBit(0)
0x073 LocalVar:2ndChr <- FlagBit(1)
0x07A LovalVar:3rdChr <- FlagBit(2)
0x081 If ( (LocalVar:2ndChr.BattleAddr(&DeathStatus) == 0) )
0x081 {
0x08E BattleAddr(&TargetSelected) <- LocalVar:2ndChr
0x095 Perform("Mono Laser"[014B], EnemyAttack)
0x09B If ( (LocalVar:1stChr.BattleAddr(&DeathStatus) == 0) )
0x09B {
0x0A8 BattleAddr(&TargetSelected) <- LocalVar:1stChr
0x0AF Perform("Mono Laser"[014B], EnemyAttack)
0x0B5 If ( (LovalVar:3rdChr.BattleAddr(&DeathStatus) == 0) )
0x0B5 {
0x0C2 BattleAddr(&TargetSelected) <- LovalVar:3rdChr
0x0C9 Perform("Mono Laser"[014B], EnemyAttack)
0x0CF }
0x0CF Else
0x0CF {
0x0D2 }
0x0D2 Else
0x0D2 {
0x0D5 If (Random MOD 3 == 1)
0x0D5 {
0x0DA If ( (BattleAddr(&Self).BattleAddr(&CurrentMP) >= MPCost(Sleepel [000B])) )
0x0DA {
0x0E8 BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&SleepStatus) == 0) )
0x0F7 Perform("Sleepel"[000B], EnemyAttack)
0x0FC }
0x0FC Else
0x0FC {
0x0FF If (Not  (LocalVar:Stage) )
0x0FF {
0x106 If ( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x106 {
0x113 BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x122 }
0x122 Else
0x122 {
0x125 BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x12D Perform("Sword Attack"[014A], EnemyAttack)
0x133 }
0x133 Else
0x133 {
0x136 LocalVar:1stChr <- FlagBit(0)
0x13D LocalVar:2ndChr <- FlagBit(1)
0x144 LovalVar:3rdChr <- FlagBit(2)
0x14B If ( (LocalVar:2ndChr.BattleAddr(&DeathStatus) == 0) )
0x14B {
0x158 BattleAddr(&TargetSelected) <- LocalVar:2ndChr
0x15F Perform("Mono Laser"[014B], EnemyAttack)
0x165 If ( (LocalVar:1stChr.BattleAddr(&DeathStatus) == 0) )
0x165 {
0x172 BattleAddr(&TargetSelected) <- LocalVar:1stChr
0x179 Perform("Mono Laser"[014B], EnemyAttack)
0x17F If ( (LovalVar:3rdChr.BattleAddr(&DeathStatus) == 0) )
0x17F {
0x18C BattleAddr(&TargetSelected) <- LovalVar:3rdChr
0x193 Perform("Mono Laser"[014B], EnemyAttack)
0x199 }
0x199 Else
0x199 {
0x19C }
0x19C Else
0x19C {
0x19F }
0x19F Else
0x19F {
0x1A2 POP(Random MOD 3)
0x1A3 LocalVar:OpenAttack <- 1
0x1A9 }
0x1A9 Else
0x1A9 {
0x1AC If (Not  (LocalVar:Stage) )
0x1AC {
0x1B3 If (Random MOD 12 == 0)
0x1B3 {
0x1BC }
0x1BC Else
0x1BC {
0x1BF If (Random MOD 12 == 1)
0x1BF {
0x1C4 If ( (BattleAddr(&AllPlayers).BattleAddr(&SilenceStatus) == 0) )
0x1C4 {
0x1D1 If ( (BattleAddr(&Self).BattleAddr(&CurrentMP) >= MPCost(Silence [000D])) )
0x1D1 {
0x1DF BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&SilenceStatus) == 0) )
0x1EE Perform("Silence"[000D], EnemyAttack)
0x1F3 }
0x1F3 Else
0x1F3 {
0x1F6 If (Not  (LocalVar:Stage) )
0x1F6 {
0x1FD If ( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x1FD {
0x20A BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x219 }
0x219 Else
0x219 {
0x21C BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x224 Perform("Sword Attack"[014A], EnemyAttack)
0x22A }
0x22A Else
0x22A {
0x22D LocalVar:1stChr <- FlagBit(0)
0x234 LocalVar:2ndChr <- FlagBit(1)
0x23B LovalVar:3rdChr <- FlagBit(2)
0x242 If ( (LocalVar:2ndChr.BattleAddr(&DeathStatus) == 0) )
0x242 {
0x24F BattleAddr(&TargetSelected) <- LocalVar:2ndChr
0x256 Perform("Mono Laser"[014B], EnemyAttack)
0x25C If ( (LocalVar:1stChr.BattleAddr(&DeathStatus) == 0) )
0x25C {
0x269 BattleAddr(&TargetSelected) <- LocalVar:1stChr
0x270 Perform("Mono Laser"[014B], EnemyAttack)
0x276 If ( (LovalVar:3rdChr.BattleAddr(&DeathStatus) == 0) )
0x276 {
0x283 BattleAddr(&TargetSelected) <- LovalVar:3rdChr
0x28A Perform("Mono Laser"[014B], EnemyAttack)
0x290 }
0x290 Else
0x290 {
0x293 If ( (BattleAddr(&AllPlayers).BattleAddr(&SleepStatus) == 0) )
0x293 {
0x2A0 If ( (BattleAddr(&Self).BattleAddr(&CurrentMP) >= MPCost(Sleepel [000B])) )
0x2A0 {
0x2AE BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&SleepStatus) == 0) )
0x2BD Perform("Sleepel"[000B], EnemyAttack)
0x2C2 }
0x2C2 Else
0x2C2 {
0x2C5 If (Not  (LocalVar:Stage) )
0x2C5 {
0x2CC If ( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x2CC {
0x2D9 BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x2E8 }
0x2E8 Else
0x2E8 {
0x2EB BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x2F3 Perform("Sword Attack"[014A], EnemyAttack)
0x2F9 }
0x2F9 Else
0x2F9 {
0x2FC LocalVar:1stChr <- FlagBit(0)
0x303 LocalVar:2ndChr <- FlagBit(1)
0x30A LovalVar:3rdChr <- FlagBit(2)
0x311 If ( (LocalVar:2ndChr.BattleAddr(&DeathStatus) == 0) )
0x311 {
0x31E BattleAddr(&TargetSelected) <- LocalVar:2ndChr
0x325 Perform("Mono Laser"[014B], EnemyAttack)
0x32B If ( (LocalVar:1stChr.BattleAddr(&DeathStatus) == 0) )
0x32B {
0x338 BattleAddr(&TargetSelected) <- LocalVar:1stChr
0x33F Perform("Mono Laser"[014B], EnemyAttack)
0x345 If ( (LovalVar:3rdChr.BattleAddr(&DeathStatus) == 0) )
0x345 {
0x352 BattleAddr(&TargetSelected) <- LovalVar:3rdChr
0x359 Perform("Mono Laser"[014B], EnemyAttack)
0x35F }
0x35F Else
0x35F {
0x362 If ( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x362 {
0x36F BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x37E }
0x37E Else
0x37E {
0x381 BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x389 Perform("Sword Attack"[014A], EnemyAttack)
0x38F }
0x38F Else
0x38F {
0x392 }
0x392 Else
0x392 {
0x395 If (Random MOD 12 == 2)
0x395 {
0x39A }
0x39A Else
0x39A {
0x39D If (Random MOD 12 == 3)
0x39D {
0x3A2 If ( (BattleAddr(&AllPlayers).BattleAddr(&SleepStatus) == 0) )
0x3A2 {
0x3AF If ( (BattleAddr(&Self).BattleAddr(&CurrentMP) >= MPCost(Sleepel [000B])) )
0x3AF {
0x3BD BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&SleepStatus) == 0) )
0x3CC Perform("Sleepel"[000B], EnemyAttack)
0x3D1 }
0x3D1 Else
0x3D1 {
0x3D4 If (Not  (LocalVar:Stage) )
0x3D4 {
0x3DB If ( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x3DB {
0x3E8 BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x3F7 }
0x3F7 Else
0x3F7 {
0x3FA BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x402 Perform("Sword Attack"[014A], EnemyAttack)
0x408 }
0x408 Else
0x408 {
0x40B LocalVar:1stChr <- FlagBit(0)
0x412 LocalVar:2ndChr <- FlagBit(1)
0x419 LovalVar:3rdChr <- FlagBit(2)
0x420 If ( (LocalVar:2ndChr.BattleAddr(&DeathStatus) == 0) )
0x420 {
0x42D BattleAddr(&TargetSelected) <- LocalVar:2ndChr
0x434 Perform("Mono Laser"[014B], EnemyAttack)
0x43A If ( (LocalVar:1stChr.BattleAddr(&DeathStatus) == 0) )
0x43A {
0x447 BattleAddr(&TargetSelected) <- LocalVar:1stChr
0x44E Perform("Mono Laser"[014B], EnemyAttack)
0x454 If ( (LovalVar:3rdChr.BattleAddr(&DeathStatus) == 0) )
0x454 {
0x461 BattleAddr(&TargetSelected) <- LovalVar:3rdChr
0x468 Perform("Mono Laser"[014B], EnemyAttack)
0x46E }
0x46E Else
0x46E {
0x471 If ( (BattleAddr(&AllPlayers).BattleAddr(&SilenceStatus) == 0) )
0x471 {
0x47E If ( (BattleAddr(&Self).BattleAddr(&CurrentMP) >= MPCost(Silence [000D])) )
0x47E {
0x48C BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&SilenceStatus) == 0) )
0x49B Perform("Silence"[000D], EnemyAttack)
0x4A0 }
0x4A0 Else
0x4A0 {
0x4A3 If (Not  (LocalVar:Stage) )
0x4A3 {
0x4AA If ( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x4AA {
0x4B7 BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x4C6 }
0x4C6 Else
0x4C6 {
0x4C9 BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x4D1 Perform("Sword Attack"[014A], EnemyAttack)
0x4D7 }
0x4D7 Else
0x4D7 {
0x4DA LocalVar:1stChr <- FlagBit(0)
0x4E1 LocalVar:2ndChr <- FlagBit(1)
0x4E8 LovalVar:3rdChr <- FlagBit(2)
0x4EF If ( (LocalVar:2ndChr.BattleAddr(&DeathStatus) == 0) )
0x4EF {
0x4FC BattleAddr(&TargetSelected) <- LocalVar:2ndChr
0x503 Perform("Mono Laser"[014B], EnemyAttack)
0x509 If ( (LocalVar:1stChr.BattleAddr(&DeathStatus) == 0) )
0x509 {
0x516 BattleAddr(&TargetSelected) <- LocalVar:1stChr
0x51D Perform("Mono Laser"[014B], EnemyAttack)
0x523 If ( (LovalVar:3rdChr.BattleAddr(&DeathStatus) == 0) )
0x523 {
0x530 BattleAddr(&TargetSelected) <- LovalVar:3rdChr
0x537 Perform("Mono Laser"[014B], EnemyAttack)
0x53D }
0x53D Else
0x53D {
0x540 If ( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x540 {
0x54D BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x55C }
0x55C Else
0x55C {
0x55F BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x567 Perform("Sword Attack"[014A], EnemyAttack)
0x56D }
0x56D Else
0x56D {
0x570 }
0x570 Else
0x570 {
0x573 If (Random MOD 12 == 4)
0x573 {
0x578 }
0x578 Else
0x578 {
0x57B If (Random MOD 12 == 5)
0x57B {
0x580 }
0x580 Else
0x580 {
0x583 If (Random MOD 12 == 6)
0x583 {
0x588 }
0x588 Else
0x588 {
0x58B If (Random MOD 12 == 7)
0x58B {
0x590 If ( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 1) )
0x590 {
0x59D BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 1) )
0x5AC }
0x5AC Else
0x5AC {
0x5AF BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x5B7 Perform("Mono Laser"[014B], EnemyAttack)
0x5BD }
0x5BD Else
0x5BD {
0x5C0 }
0x5C0 Else
0x5C0 {
0x5C3 If ( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x5C3 {
0x5D0 BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x5DF }
0x5DF Else
0x5DF {
0x5E2 BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x5EA Perform("Sword Attack"[014A], EnemyAttack)
0x5F0 }
0x5F0 Else
0x5F0 {
0x5F3 POP(Random MOD 12)
0x5F4 }
0x5F4 Else
0x5F4 {
0x5F7 If (Random MOD 5 == 0)
0x5F7 {
0x600 If ( (BattleAddr(&AllPlayers).BattleAddr(&SilenceStatus) == 0) )
0x600 {
0x60D If ( (BattleAddr(&Self).BattleAddr(&CurrentMP) >= MPCost(Silence [000D])) )
0x60D {
0x61B BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&SilenceStatus) == 0) )
0x62A Perform("Silence"[000D], EnemyAttack)
0x62F }
0x62F Else
0x62F {
0x632 If (Not  (LocalVar:Stage) )
0x632 {
0x639 If ( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x639 {
0x646 BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x655 }
0x655 Else
0x655 {
0x658 BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x660 Perform("Sword Attack"[014A], EnemyAttack)
0x666 }
0x666 Else
0x666 {
0x669 LocalVar:1stChr <- FlagBit(0)
0x670 LocalVar:2ndChr <- FlagBit(1)
0x677 LovalVar:3rdChr <- FlagBit(2)
0x67E If ( (LocalVar:2ndChr.BattleAddr(&DeathStatus) == 0) )
0x67E {
0x68B BattleAddr(&TargetSelected) <- LocalVar:2ndChr
0x692 Perform("Mono Laser"[014B], EnemyAttack)
0x698 If ( (LocalVar:1stChr.BattleAddr(&DeathStatus) == 0) )
0x698 {
0x6A5 BattleAddr(&TargetSelected) <- LocalVar:1stChr
0x6AC Perform("Mono Laser"[014B], EnemyAttack)
0x6B2 If ( (LovalVar:3rdChr.BattleAddr(&DeathStatus) == 0) )
0x6B2 {
0x6BF BattleAddr(&TargetSelected) <- LovalVar:3rdChr
0x6C6 Perform("Mono Laser"[014B], EnemyAttack)
0x6CC }
0x6CC Else
0x6CC {
0x6CF If ( (BattleAddr(&AllPlayers).BattleAddr(&SleepStatus) == 0) )
0x6CF {
0x6DC If ( (BattleAddr(&Self).BattleAddr(&CurrentMP) >= MPCost(Sleepel [000B])) )
0x6DC {
0x6EA BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&SleepStatus) == 0) )
0x6F9 Perform("Sleepel"[000B], EnemyAttack)
0x6FE }
0x6FE Else
0x6FE {
0x701 If (Not  (LocalVar:Stage) )
0x701 {
0x708 If ( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x708 {
0x715 BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x724 }
0x724 Else
0x724 {
0x727 BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x72F Perform("Sword Attack"[014A], EnemyAttack)
0x735 }
0x735 Else
0x735 {
0x738 LocalVar:1stChr <- FlagBit(0)
0x73F LocalVar:2ndChr <- FlagBit(1)
0x746 LovalVar:3rdChr <- FlagBit(2)
0x74D If ( (LocalVar:2ndChr.BattleAddr(&DeathStatus) == 0) )
0x74D {
0x75A BattleAddr(&TargetSelected) <- LocalVar:2ndChr
0x761 Perform("Mono Laser"[014B], EnemyAttack)
0x767 If ( (LocalVar:1stChr.BattleAddr(&DeathStatus) == 0) )
0x767 {
0x774 BattleAddr(&TargetSelected) <- LocalVar:1stChr
0x77B Perform("Mono Laser"[014B], EnemyAttack)
0x781 If ( (LovalVar:3rdChr.BattleAddr(&DeathStatus) == 0) )
0x781 {
0x78E BattleAddr(&TargetSelected) <- LovalVar:3rdChr
0x795 Perform("Mono Laser"[014B], EnemyAttack)
0x79B }
0x79B Else
0x79B {
0x79E LocalVar:1stChr <- FlagBit(0)
0x7A5 LocalVar:2ndChr <- FlagBit(1)
0x7AC LovalVar:3rdChr <- FlagBit(2)
0x7B3 If ( (LocalVar:2ndChr.BattleAddr(&DeathStatus) == 0) )
0x7B3 {
0x7C0 BattleAddr(&TargetSelected) <- LocalVar:2ndChr
0x7C7 Perform("Mono Laser"[014B], EnemyAttack)
0x7CD If ( (LocalVar:1stChr.BattleAddr(&DeathStatus) == 0) )
0x7CD {
0x7DA BattleAddr(&TargetSelected) <- LocalVar:1stChr
0x7E1 Perform("Mono Laser"[014B], EnemyAttack)
0x7E7 If ( (LovalVar:3rdChr.BattleAddr(&DeathStatus) == 0) )
0x7E7 {
0x7F4 BattleAddr(&TargetSelected) <- LovalVar:3rdChr
0x7FB Perform("Mono Laser"[014B], EnemyAttack)
0x801 }
0x801 Else
0x801 {
0x804 }
0x804 Else
0x804 {
0x807 If (Random MOD 5 == 1)
0x807 {
0x80C If ( (BattleAddr(&AllPlayers).BattleAddr(&SleepStatus) == 0) )
0x80C {
0x819 If ( (BattleAddr(&Self).BattleAddr(&CurrentMP) >= MPCost(Sleepel [000B])) )
0x819 {
0x827 BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&SleepStatus) == 0) )
0x836 Perform("Sleepel"[000B], EnemyAttack)
0x83B }
0x83B Else
0x83B {
0x83E If (Not  (LocalVar:Stage) )
0x83E {
0x845 If ( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x845 {
0x852 BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x861 }
0x861 Else
0x861 {
0x864 BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x86C Perform("Sword Attack"[014A], EnemyAttack)
0x872 }
0x872 Else
0x872 {
0x875 LocalVar:1stChr <- FlagBit(0)
0x87C LocalVar:2ndChr <- FlagBit(1)
0x883 LovalVar:3rdChr <- FlagBit(2)
0x88A If ( (LocalVar:2ndChr.BattleAddr(&DeathStatus) == 0) )
0x88A {
0x897 BattleAddr(&TargetSelected) <- LocalVar:2ndChr
0x89E Perform("Mono Laser"[014B], EnemyAttack)
0x8A4 If ( (LocalVar:1stChr.BattleAddr(&DeathStatus) == 0) )
0x8A4 {
0x8B1 BattleAddr(&TargetSelected) <- LocalVar:1stChr
0x8B8 Perform("Mono Laser"[014B], EnemyAttack)
0x8BE If ( (LovalVar:3rdChr.BattleAddr(&DeathStatus) == 0) )
0x8BE {
0x8CB BattleAddr(&TargetSelected) <- LovalVar:3rdChr
0x8D2 Perform("Mono Laser"[014B], EnemyAttack)
0x8D8 }
0x8D8 Else
0x8D8 {
0x8DB If ( (BattleAddr(&AllPlayers).BattleAddr(&SilenceStatus) == 0) )
0x8DB {
0x8E8 If ( (BattleAddr(&Self).BattleAddr(&CurrentMP) >= MPCost(Silence [000D])) )
0x8E8 {
0x8F6 BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&SilenceStatus) == 0) )
0x905 Perform("Silence"[000D], EnemyAttack)
0x90A }
0x90A Else
0x90A {
0x90D If (Not  (LocalVar:Stage) )
0x90D {
0x914 If ( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x914 {
0x921 BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) )
0x930 }
0x930 Else
0x930 {
0x933 BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x93B Perform("Sword Attack"[014A], EnemyAttack)
0x941 }
0x941 Else
0x941 {
0x944 LocalVar:1stChr <- FlagBit(0)
0x94B LocalVar:2ndChr <- FlagBit(1)
0x952 LovalVar:3rdChr <- FlagBit(2)
0x959 If ( (LocalVar:2ndChr.BattleAddr(&DeathStatus) == 0) )
0x959 {
0x966 BattleAddr(&TargetSelected) <- LocalVar:2ndChr
0x96D Perform("Mono Laser"[014B], EnemyAttack)
0x973 If ( (LocalVar:1stChr.BattleAddr(&DeathStatus) == 0) )
0x973 {
0x980 BattleAddr(&TargetSelected) <- LocalVar:1stChr
0x987 Perform("Mono Laser"[014B], EnemyAttack)
0x98D If ( (LovalVar:3rdChr.BattleAddr(&DeathStatus) == 0) )
0x98D {
0x99A BattleAddr(&TargetSelected) <- LovalVar:3rdChr
0x9A1 Perform("Mono Laser"[014B], EnemyAttack)
0x9A7 }
0x9A7 Else
0x9A7 {
0x9AA LocalVar:1stChr <- FlagBit(0)
0x9B1 LocalVar:2ndChr <- FlagBit(1)
0x9B8 LovalVar:3rdChr <- FlagBit(2)
0x9BF If ( (LocalVar:2ndChr.BattleAddr(&DeathStatus) == 0) )
0x9BF {
0x9CC BattleAddr(&TargetSelected) <- LocalVar:2ndChr
0x9D3 Perform("Mono Laser"[014B], EnemyAttack)
0x9D9 If ( (LocalVar:1stChr.BattleAddr(&DeathStatus) == 0) )
0x9D9 {
0x9E6 BattleAddr(&TargetSelected) <- LocalVar:1stChr
0x9ED Perform("Mono Laser"[014B], EnemyAttack)
0x9F3 If ( (LovalVar:3rdChr.BattleAddr(&DeathStatus) == 0) )
0x9F3 {
0xA00 BattleAddr(&TargetSelected) <- LovalVar:3rdChr
0xA07 Perform("Mono Laser"[014B], EnemyAttack)
0xA0D }
0xA0D Else
0xA0D {
0xA10 }
0xA10 Else
0xA10 {
0xA13 LocalVar:1stChr <- FlagBit(0)
0xA1A LocalVar:2ndChr <- FlagBit(1)
0xA21 LovalVar:3rdChr <- FlagBit(2)
0xA28 If ( (LocalVar:2ndChr.BattleAddr(&DeathStatus) == 0) )
0xA28 {
0xA35 BattleAddr(&TargetSelected) <- LocalVar:2ndChr
0xA3C Perform("Mono Laser"[014B], EnemyAttack)
0xA42 If ( (LocalVar:1stChr.BattleAddr(&DeathStatus) == 0) )
0xA42 {
0xA4F BattleAddr(&TargetSelected) <- LocalVar:1stChr
0xA56 Perform("Mono Laser"[014B], EnemyAttack)
0xA5C If ( (LovalVar:3rdChr.BattleAddr(&DeathStatus) == 0) )
0xA5C {
0xA69 BattleAddr(&TargetSelected) <- LovalVar:3rdChr
0xA70 Perform("Mono Laser"[014B], EnemyAttack)
0xA76 }
0xA76 Else
0xA76 {
0xA79 POP(Random MOD 5)
0xA7ASCRIPT END


GENERAL COUNTER:

0x000 If ( (BattleAddr(&Self).BattleAddr(&CurrentHP) <= BattleAddr(&Self).BattleAddr(&MaxHP) / 2) )
0x000 {
0x015 LocalVar:Stage <- 1
0x01B }
0x01B Else
0x01B {
0x01E LocalVar:Stage <- 0
0x024SCRIPT END

Into this:

Code: [Select]
/----------------------------------------------------------------------------\
|   BATTLE PLAN:                                                             |
|                                                                            |
|  Opening AttacK: 1/3 Chance use Silence on Rnd Target without Silence;     |
|                       If Eligor's MP < 24:                                 |
|                        Eligor's HP  > 50%: Use Phase 1 Attack              |
|                        Eligor's HP <= 50%: Use Phase 2 Attack              |
|                                                                            |
|                  1/3 Chance use Sleepel on Rnd Target without Sleep,       |
|                        If Eligor's MP < 8:                                 |
|                         Eligor's HP  > 50%: Use Phase 1 Attack             |
|                         Eligor's HP <= 50%: Use Phase 2 Attack             |
|                                                                            |
|                  1/3 Chance skip a turn                                    |
|                                                                            |
|                                                                            |
|  Phase 1 Attack: Use <Sword Attack> on Rnd Target in Front Row; If All     |
|                   Targets are in Back Row, select Rnd Target               |
|                                                                            |
|  Phase 2 Attack: Use Mono Laser on 2nd Target; If 2nd Target has Death, do |
|                   nothing                                                  |
|                  Use Mono Laser on 1st Target; If 1st Target has Death, do |
|                   nothing (Follow-Up Attack)                               |
|                  Use Mono Laser on 3rd Target; If 3rd Target has Death, do |
|                   nothing (Follow-Up Attack)                               |
|                                                                            |
|                                                                            |
|  Phase 1: (Repeats)                                                        |
|                                                                            |
|   Eligor's HP > 50%:                                                       |
|                                                                            |
|    1/6 Chance: Use Silence on Rnd Target without Silence; If Eligor's      |
|                 MP < 24, use Phase 1 Attack                                |
|                                                                            |
|                If All Targets have Silence, use Sleepel on Rnd Target; If  |
|                 Eligor's MP < 8, use Phase 1 Attack                        |
|                                                                            |
|                If All Targets have Sleep, use Phase 1 Attack               |
|                                                                            |
|    1/6 Chance: Use Sleepel on Rnd Target without Sleep; If Eligor's        |
|                 MP < 8, use Phase 1 Attack                                 |
|                                                                            |
|                If All Targets have Sleep, use Silence on Rnd Target; If    |
|                 Eligor's MP < 24, use Phase 1 Attack                       |
|                                                                            |
|                If All Targets have Silence, use Phase 1 Attack             |
|                                                                            |
|    2/6 Chance: Use Mono Laser on Rnd Target in Back Row; If All Targets    |
|                 are in Front Row, select Rnd Target                        |
|                                                                            |
|    2/6 Chance: Use Phase 1 Attack                                          |
|                                                                            |
|                                                                            |
|  Phase 2: (Repeats)                                                        |
|                                                                            |
|   Eligor's HP <= 50%:                                                      |
|                                                                            |
|    1/5 Chance: Use Silence on Rnd Target without Silence; If Eligor's      |
|                 MP < 24, use Phase 2 Attack                                |
|                                                                            |
|                If All Targets have Silence, use Sleepel on Rnd Target; If  |
|                 Eligor's MP < 8, use Phase 2 Attack                        |
|                                                                            |
|                If All Targets have Sleep, use Phase 2 Attack               |
|                                                                            |
|    1/5 Chance: Use Sleepel on Rnd Target without Sleep; If Eligor's        |
|                 MP < 8, use Phase 2 Attack                                 |
|                                                                            |
|                If All Targets have Sleep, use Silence on Rnd Target; If    |
|                 Eligor's MP < 24, use Phase 2 Attack                       |
|                                                                            |
|                If All Targets have Silence, use Phase 2 Attack             |
|                                                                            |
|    3/5 Chance: Use Phase 2 Attack                                          |
\----------------------------------------------------------------------------/

Once I'm done I'm gonna upload each individual enemy to Paste Bin. I'm also gonna C+P the raw AI Script as opcodes+arguments, so that when the time finally comes to writing my own AI script I'll be able to take snippets from each enemy to build my own custom script. It's a really fun project! I'm sure I'll be back  soon with my next question :)

On a side note NFI, do you use Discord much? I've sent you a PM in the past but you never got...
« Last Edit: 2019-03-05 00:49:25 by DynamixDJ »

nfitc1

  • *
  • Posts: 3011
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Re: [FFVII] Enemy AI Script questions
« Reply #6 on: 2019-03-05 03:49:53 »
Eligor made me rage quit. I understand what it was doing, but he got WAY more of a detailed script than he needed. It's main script is longer and convoluted than Ruby Weapon's; And HE controls his tentacles!

You have the 4020, 4021, 4023 and 4024 addresses correct to my notes. I haven't tested 4022 very much. It's used in 59 different enemies (none of which are the Hojo samples. Not sure where you get that they are) and they are typically enemies that have multiple parts and are controlling certain parts. Some exceptions are enemies like Chocobos (which is why I think this flag is "not necessary to defeat to win").

I used to check Discord a lot. I haven't been on lately due to lack of time. I'll give it a check now though. I did get that PM, but I didn't have anything constructive to add. I'm almost daily checking this site though.
« Last Edit: 2019-03-05 03:57:25 by NFITC1 »

DynamixDJ

  • *
  • Posts: 240
  • 1111 1111 0111
    • View Profile
Re: [FFVII] Enemy AI Script questions
« Reply #7 on: 2019-03-05 04:58:09 »
The reason I know that Hojo's samples use that flag is that I spotted it when working on my Enemy Formation Charts.

Look at offset 0x0C in the Battle Formation Data.These four bytes only consider the last 5 bits, and these 5 bits are what the 4020 - 4024 addresses pull.
0x01 I think isn't the Actor's visibility, but rather its "presence" in the battle. 0x02 is more accurately "Enemy grouping during a pincer attack" 0 = group one, and 1= group two (or the other way round, I can't remember). 0x04 is unknown, but you've allocated that as "Indicates if Actor needs to die for Battle to end".

The only Formation to use the "FFE3" Battle Formation Flag (Non-Targetable, Main AI script disabled and 0x04 OFF) is formation 358h, specifically the BR Sample and P Sample enemies. These are the only two enemies in the game that enter the battle with the 0x04 flag off; all other instances of that flag being toggled occur via in-battle AI script.
« Last Edit: 2019-03-05 05:00:12 by DynamixDJ »

DynamixDJ

  • *
  • Posts: 240
  • 1111 1111 0111
    • View Profile
Re: [FFVII] Enemy AI Script questions
« Reply #8 on: 2019-03-05 05:13:07 »
When you mention enemies with multiple limbs, are you sure that you're not confusing it wih the 0x0010 offset of the Battle Setup formate? I remember we discussed before about the fact that bit 1 was a bit of a mystery to me, and I recall you saying then that that flag has something to do with enemies with multiple limbs. In fact, I made a list of enemies which have this flag ON for the pre-battle setup flag ([FF], [FB], [EF], [F3], [EB] & [E3]):

 - Ghirofelgo / Chain
 - Mirage
 - Dorky Face
 - Jersey
 - Ying, Yang, Ying/Yang
 - Ancient Dragon
 - Schizo Left / Schizo Right
 - Guardian (+ Guardian Left and Guardian Right)
 - Carry Armour / Left Arm / Right Arm
 - Proud Clod / Jamar Armour
 - Helletic Hojo / Right Arm / Left Arm
 - Mover
 - Jenova Synthesis
 - Bizarro Sephiroth
 - Roulette Cannon / Pedestal
 - Diamond Weapon
 - Ruby Weapon / Right + left Tentacle
 - Emerald Weapon / Leg / Eye

Anyway, I don't think this is the same as the 4022 address. We see the Vice and Hell House use 4022, and they don't have multiple limbs
« Last Edit: 2019-03-05 05:15:55 by DynamixDJ »

nfitc1

  • *
  • Posts: 3011
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Re: [FFVII] Enemy AI Script questions
« Reply #9 on: 2019-03-05 14:54:49 »
Ack! I was only looking at the AI. I know those flags pull into the 402X memory region, but I always forget to look in form data.

I didn't say multiple limbs, I said multiple parts. This includes Hell House as it essentially has two "parts". One being the thing you initially encounter and the other being the possessed thing after the initial house thing is defeated.

DynamixDJ

  • *
  • Posts: 240
  • 1111 1111 0111
    • View Profile
Re: [FFVII] Enemy AI Script questions
« Reply #10 on: 2019-03-06 09:06:33 »
Ah, my bad. I'm only assuming that 4022 is pulling the 0x04 flag in the enemy battle setup, but I could be wrong.

Anyway, in regards to the Setup Flag used (0x0010 offset in the Battle Setup Format), I still have Bit1 0x02 jotted down as ?'s, it would be good to give it a clear description as to what that flag does. I recall you saying it has something do with the animations when an enemy does have multiple limbs (not parts; I get what you're saying now about the other flag). Or, if an enemy interacts with another enemy during one of their animations (like the Movers).

A concise explanation to this flag would be really useful :). It's the 2 bytes that's usually set to FFFD to indicate a normal battle (0x02 OFF), or FFED to indicate Pre-Emp disabled.

nfitc1

  • *
  • Posts: 3011
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Re: [FFVII] Enemy AI Script questions
« Reply #11 on: 2019-03-06 15:06:39 »
I'm not going to say I'm lazy, but can you provide a link to where I discussed that. :P I'm not doubting that I said this, but I probably said other things around that and the context would make more sense to me and be able to explain it.

DynamixDJ

  • *
  • Posts: 240
  • 1111 1111 0111
    • View Profile
Re: [FFVII] Enemy AI Script questions
« Reply #12 on: 2019-03-06 20:22:20 »
Haha lol. I know it's somewhere in the PMs that we've sent each other, or possibly in a topic I posted a few years back (I'm not saying I'm lazy either.......)

Anyway, just a quickie regarding the MP again, we can see that the script doesn't work properly, I'm compiling a list of errors for DLPB and I would like to offer working fixes.

The fix that I have suggested for the MP (and if this works then the same thing can be applied to the Grunt/Guard Hound/Mono Drive also):

change:

Code: [Select]
0x040 If ( (BattleAddr(&AllPlayers).BattleAddr(&FrontRow) == 0) )
0x040 {
0x04D LovalVar:PlayerRow <- LovalVar:PlayerRow AND 1
0x057 If ( (BattleAddr(&AllPlayers).BattleAddr(&FrontRow) == 1) )
0x057 {
0x064 LovalVar:PlayerRow <- LovalVar:PlayerRow AND 2
0x06E If ( (BattleAddr(&Self).BattleAddr(&FrontRow) == 0) )

TO:

Code: [Select]
0x040 If ( (BattleAddr(&AllPlayers).BattleAddr(&FrontRow) == 0) )
0x040 {
0x04D LovalVar:PlayerRow <- LovalVar:PlayerRow <- 1
0x057 If ( (BattleAddr(&AllPlayers).BattleAddr(&FrontRow) == 1) )
0x057 {
0x064 LovalVar:PlayerRow <- LovalVar:PlayerRow <- 2
0x06E If ( (BattleAddr(&Self).BattleAddr(&FrontRow) == 0) )

This should fix the subsequent AND checks to make it work properly... I think. NFI?

Edit - actually, thinking on it, it could be a fun exercise just to tidy up the whole script, it'll put everything I've learned to the test. It's obvious that the opening bombing mission was a "trial and error" task for the coders, they probably had a cool off period after the demo was finished to restructure the way they compile the script (Enemy AI and field script; why on earth are they using [2][0] $GM to signify that the Restore Materia has been obtained haha lol??)

I'll have a go at that once I've escaped Midgar, which I honestly thought I would have done by now lol
« Last Edit: 2019-03-06 20:27:04 by DynamixDJ »

nfitc1

  • *
  • Posts: 3011
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Re: [FFVII] Enemy AI Script questions
« Reply #13 on: 2019-03-06 21:10:54 »
Once you enter Midgar, you never leave.

Though...no. those changes don't make sense. I'm guessing you meant this:
Code: [Select]
0x040 If ( (BattleAddr(&AllPlayers).BattleAddr(&FrontRow) == 0) )
0x040 {
0x04D LocalVar:PlayerRow <- 1
0x057 If ( (BattleAddr(&AllPlayers).BattleAddr(&FrontRow) == 1) )
0x057 {
0x064 LocalVar:PlayerRow <- 2
0x06E If ( (BattleAddr(&Self).BattleAddr(&FrontRow) == 0) )
THAT would make sense. :)
I just took a look at it and at first glance the vanilla script is totally stupid. Trying to AND a value that you just set to 0 will always result in 0. However, maybe they intended this. It appears that the intended function is if all playable characters are in the back row then prioritize the machine gun action.

There is a 1:4 chance of Tonfa in the event that the LocalVar:PlayerRow = 1
There is a 1:8 chance of Tonfa if LocalVar:PlayerRow = 2
However, since neither of those are possible, it defaults to a 1:6 chance of a Tonfa

This idea probably got scrapped since both Machine Gun and Tonfa are long range attacks and their damage output is not reduced due to target's row. Rather than remove the code that would allow for this chance behavior, it is simply mitigated by not allowing it to logically happen. Then, should the need arise in the future to change it back, it's a simple fix. A SIMPLER fix is to change the initial assignment at position 0x004 to be a 3. Then these values will be correctly AND'd and produce a non-zero result. The script is still stupid for other reasons (obvious copy-pasted code blocks) but it has more function again. If I wanted to take the time I could make a much cleaner code. I'm just not sure it's worth it on such a simple enemy. Although it might provide some good practice for other scripts... I might change my mind if I find myself bored later. Actually, it is CERTAINLY not worth it because the entire script is based around the possibility that Tonfa is a short range action. Since it's not there is no need for 90% of the script. It can be as simple as
Code: [Select]
If (Not (Random MOD 6))
{
   LocalVar:0080 <- 272 (Machine Gun)
} Else {
   LocalVar:0080 <- 273 (Tonfa)
}
TargetMask <- RandomBit( AllOpponentMask )
Perform(LocalVar:0080, EnemyAttack)
SCRIPT END
That is literally all it needs to be. This prioritizes Tonfa (since it's a more damaging action) at a 5:6 ratio.
« Last Edit: 2019-03-06 21:20:26 by NFITC1 »

DynamixDJ

  • *
  • Posts: 240
  • 1111 1111 0111
    • View Profile
Re: [FFVII] Enemy AI Script questions
« Reply #14 on: 2019-03-06 22:10:32 »
Quote
Though...no. those changes don't make sense. I'm guessing you meant this:
Code: [Select]
0x040   If ( (BattleAddr(&AllPlayers).BattleAddr(&FrontRow) == 0) )
0x040   {
0x04D   LocalVar:PlayerRow <- 1
0x057   If ( (BattleAddr(&AllPlayers).BattleAddr(&FrontRow) == 1) )
0x057   {
0x064   LocalVar:PlayerRow <- 2
0x06E   If ( (BattleAddr(&Self).BattleAddr(&FrontRow) == 0) )
THAT would make sense. :)

Yes, indeed, that's exactly what I meant lol.

I agree that it looks quite a lot like they had left this script in as it was to give them the option to invoke the Front/Back row behaviour at a later date, or they decided that it wasn't needed so they simply overwrote it. I didn't clock that changing the initial value of LocalVar:0020 from 0 to 3 would also neatly fix the issue, kudos (kind of annoyed I didn't see that myself :))

In your custom script you've missed out the part that does work, which is the check to see if the MP is in the Front Row or back row, and we do see an instance on the Sector 8 Bridge with MP's in the back row. Here is how (I think) it should look:

Code: [Select]
MAIN:

     LovalVar:SelfRow <- 0
      If ( (BattleAddr(&Self).BattleAddr(&Row) == 0) )
      {
      LovalVar:SelfRow <- 0
      }
      Else
      {
      LovalVar:SelfRow <- 1
      If (Not  (LovalVar:SelfRow) )
      {
      LocalVar:Random <- 2
      If (Not  (Random MOD LocalVar:Random) )
      {
      BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
      LocalVar:EnemyAttack <- 272 (Machine Gun)
      }
      Else
      {
      BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
      LocalVar:EnemyAttack <- 273 (Tonfa)
      }
      Else
      {
      LocalVar:Random <- 6
      If (Not  (Random MOD LocalVar:Random) )
      {
      BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
      LocalVar:EnemyAttack <- 273 (Tonfa)
      }
      Else
      {
      BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
      LocalVar:EnemyAttack <- 272 (Machine Gun)
      }
      Else
      {
      POP(LovalVar:SelfROw)
     Perform(LocalVar:EnemyAttack, EnemyAttack)
     SCRIPT END

As mentioned, this weirdness in the script occurs in the first four enemies 10h - 13h, and all of them can have their script completely simplified in a similar way.
« Last Edit: 2019-03-06 22:19:24 by DynamixDJ »

DynamixDJ

  • *
  • Posts: 240
  • 1111 1111 0111
    • View Profile
Re: [FFVII] Enemy AI Script questions
« Reply #15 on: 2019-03-07 00:47:12 »
Remind me again what happens to enemies that take double damage from Statuses (Sword Dance, Soldier:3rd, Corvette take double from Confusion; Palmer takes double from Sleep).

I know that they would take double damage from Added Effect+Confusion on your weapon, but does it also mean that the Confusion/Sleep infliction chance would now be 100%?

nfitc1

  • *
  • Posts: 3011
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Re: [FFVII] Enemy AI Script questions
« Reply #16 on: 2019-03-07 02:54:00 »
It just affects damage done by actions that inflict the status

DynamixDJ

  • *
  • Posts: 240
  • 1111 1111 0111
    • View Profile
Re: [FFVII] Enemy AI Script questions
« Reply #17 on: 2019-03-07 07:41:04 »
Man the Warning Board + Machine Gun + Laser Cannon are cheesing me off.

I don't get what's going on at 0x130 here with the Machine Gun:

Code: [Select]
PRE-BATTLE:

0x000LocalVar:WarningBoard <-  (BitCount(BattleAddr(&Self).BattleAddr(&PlayerID)) == BattleAddr(&ActiveActors).BattleAddr(&PlayerID))
0x014LocalVar:WarningBoard <-  (LocalVar:WarningBoard.BattleAddr(&SelfID) == 50)
0x022LocalVar:LaserCannon <-  (BitCount(BattleAddr(&Self).BattleAddr(&PlayerID)) == BattleAddr(&ActiveActors).BattleAddr(&PlayerID))
0x036LocalVar:LaserCannon <-  (LocalVar:LaserCannon.BattleAddr(&SelfID) == 52)
0x044SCRIPT END


MAIN:

0x000LocalVar:SelfActive <- 0
0x006 If (LocalVar:WarningBoard.BattleAddr(&SelfActive))
0x006 {
0x010 LocalVar:SelfActive <- LocalVar:SelfActive OR 1
0x01A If (BattleAddr(&Self).BattleAddr(&SelfActive))
0x01A {
0x024 LocalVar:SelfActive <- LocalVar:SelfActive OR 2
0x02E If (LocalVar:LaserCannon.BattleAddr(&SelfActive))
0x02E {
0x038 LocalVar:SelfActive <- LocalVar:SelfActive OR 4
0x042 If ( (BattleAddr(&Self).BattleAddr(&AllyAngry) == 1) )
0x042 {
0x04F If (LocalVar:SelfActive == 1)
0x04F {
0x057 If ( (BattleAddr(&Self).BattleAddr(&Angry) == 0) )
0x057 {
0x064 If ( (BattleAddr(&AllPlayers).BattleAddr(&ParalyzedStatus) == 0) )
0x064 {
0x071 If (Not  (Random MOD 2) )
0x071 {
0x079 BattleAddr(&Self).BattleAddr(&SelfActive) <- 1
0x083 BattleAddr(&Self).BattleAddr(&Targetable) <- 1
0x08D BattleAddr(&TargetSelected) <- BattleAddr(&Self)
0x094 Perform("Appear"[014F], EnemyAttack)
0x09A BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x0A2 Perform("Machine Gun"[0110], EnemyAttack)
0x0A8 }
0x0A8 Else
0x0A8 {
0x0AB If (Random MOD 4)
0x0AB {
0x0B2 BattleAddr(&Self).BattleAddr(&SelfActive) <- 1
0x0BC BattleAddr(&Self).BattleAddr(&Targetable) <- 1
0x0C6 BattleAddr(&TargetSelected) <- BattleAddr(&Self)
0x0CD Perform("Appear"[014F], EnemyAttack)
0x0D3 BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x0DB Perform("Machine Gun"[0110], EnemyAttack)
0x0E1 }
0x0E1 Else
0x0E1 {
0x0E4 BattleAddr(&Self).BattleAddr(&SelfActive) <- 1
0x0EE BattleAddr(&Self).BattleAddr(&Targetable) <- 1
0x0F8 BattleAddr(&TargetSelected) <- BattleAddr(&Self)
0x0FF Perform("Appear"[014F], EnemyAttack)
0x105 BattleAddr(&Self).BattleAddr(&Angry) <- 0
0x10F If (Not  (Random MOD 2) )
0x10F {
0x117 BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x11F Perform("Machine Gun"[0110], EnemyAttack)
0x125 }
0x125 Else
0x125 {
0x128 }
0x128 Else
0x128 {
0x12B If (LocalVar:SelfActive == 3)
0x12B {
0x130 If ( (LocalVar:LaserCannon.BattleAddr(&DeathStatus) == 0) )
0x130 {
0x13D If (Random MOD 4 == 0)
0x13D {
0x146 BattleAddr(&TargetSelected) <- BattleAddr(&Self)
0x14D Perform("Vanish"[014E], EnemyAttack)
0x153 BattleAddr(&Self).BattleAddr(&SelfActive) <- 0
0x15D BattleAddr(&Self).BattleAddr(&Targetable) <- 0
0x167 LocalVar:LaserCannon.BattleAddr(&Angry) <- 1
0x171 }
0x171 Else
0x171 {
0x174 }
0x174 Else
0x174 {
0x177 If (Random MOD 4 == 1)
0x177 {
0x17C BattleAddr(&TargetSelected) <- BattleAddr(&Self)
0x183 Perform("Vanish"[014E], EnemyAttack)
0x189 BattleAddr(&Self).BattleAddr(&SelfActive) <- 0
0x193 BattleAddr(&Self).BattleAddr(&Targetable) <- 0
0x19D }
0x19D Else
0x19D {
0x1A0 }
0x1A0 Else
0x1A0 {
0x1A3 BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x1AB Perform("Machine Gun"[0110], EnemyAttack)
0x1B1 }
0x1B1 Else
0x1B1 {
0x1B4 POP(Random MOD 4)
0x1B5 }
0x1B5 Else
0x1B5 {
0x1B8 BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x1C0 Perform("Machine Gun"[0110], EnemyAttack)
0x1C6 }
0x1C6 Else
0x1C6 {
0x1C9 }
0x1C9 Else
0x1C9 {
0x1CC If (LocalVar:SelfActive == 5)
0x1CC {
0x1D1 }
0x1D1 Else
0x1D1 {
0x1D4 }
0x1D4 Else
0x1D4 {
0x1D7 POP(LocalVar:SelfActive)
0x1D8SCRIPT END

It's checking to see if Laser Cannon doesn't have the Death Status, which it always will pass seeing as the Machine Gun cannot spawn in if you've killed the Laser Cannon.

Am I missing something, or is the check at 0x130 always going to pass, meaning that use of Machine Gun at 0x1B5 will never occur?
« Last Edit: 2019-03-07 07:43:09 by DynamixDJ »

DynamixDJ

  • *
  • Posts: 240
  • 1111 1111 0111
    • View Profile
Re: [FFVII] Enemy AI Script questions
« Reply #18 on: 2019-03-07 08:13:41 »
OK, I think I understand it now. It's a flaw in the scripting, there should be a Death Counter.

When you kill either the Mchn Gun or Lsr Can, there is no script to toggle the 4020 "SelfActive" var from 1 to 0 (like we see when one of the counterparts disappears). Because of this, once you've killed the Laser Cannon, the next time the Machine Gun's script runs, the check at 0x02E passes and the final value that is returned is always 5, which bypasses all of the scripts and acts as though the Laser Cannon was still alive.

I think this is an error. When you kill either the MG or LC another enemy will not spawn in, and the Warning Board will just sit there till you kill it. It seems to me as though the Laser Cannon should have spawned in again once you kill the Machine Gun and vice-versa (unless the other one has been killed obv).

If you add a Death Counter script for the MG and LC that looks like this, I think that will solve the problem:

Code: [Select]
DEATH COUNTER:

0x000BattleAddr(&Self).BattleAddr(&SelfActive) <- 0
0x0OASCRIPT END

or:

DEATH COUNTER:

0x000BattleAddr(&2060).BattleAddr(&4020) <- 0
0x0OASCRIPT END

Thing is, this flag may automatically get set to 0 upon playing the normal Death script for an enemy, in which case I don't know what's going on...
« Last Edit: 2019-03-07 09:55:38 by DynamixDJ »

DynamixDJ

  • *
  • Posts: 240
  • 1111 1111 0111
    • View Profile
Re: [FFVII] Enemy AI Script questions
« Reply #19 on: 2019-03-07 09:35:53 »
YES! The last two hours haven't been wasted :) :)

My first bit of AI script editing was a success. It took me a while, Pr Cl didn't like it when I tried to copy and paste and delete large chunks of AI script, but I finally got the script I wanted which was this:
Code: [Select]
12 2060
10 4020
80
60 00
90

This is the death counter script that I've added for the Machine Gun and Laser Cannon, which disassembles to:

Code: [Select]
0x000: PUSH (2060) Type (12)
0x003: PUSH (4020) Type (10)
0x006: MASK
0x007: PUSH (00) Type (01)
0x009: STRA
0x00A: END

0x000BattleAddr(&2060).BattleAddr(&4020) <- 0
0x00ASCRIPT END

I know it doesn't look like much, but I'm only a beginner lol. Anyway, after trying to mess around with Steam and getting it to not delete my save, then working out which bloody scene.bin the Steam version pulls from, I finally managed to battle the Warning Board, and that script did exactly what I had intended it to do.

It has flagged the killed enemy as non-active. So, let's say I've killed the Machine Gun; normally the MG will still be flagged as active, so when the LC takes it turn it does nothing and does not spawn in. With the added Death Counter script the MG is now flagged as non-active, so the LC re-spawns in like it was supposed to.

Again, I'm only assuming that it was supposed to respawn in givent the fact that both the MG and LC have redundant script that searches for the Death status of its counterpart. And I've fixed it :) Now I can move on :)

One for DLPB
« Last Edit: 2019-03-07 10:11:01 by DynamixDJ »

DynamixDJ

  • *
  • Posts: 240
  • 1111 1111 0111
    • View Profile
Re: [FFVII] Enemy AI Script questions
« Reply #20 on: 2019-03-07 11:07:51 »
Need some clarification on what's going on here with the Laser Cannon:


Code: [Select]
0x161 }
0x161 Else
0x161 {
0x164 If (LocalVar:SelfActive == 3)
0x164 {
0x169 If ( (LocalVar:MachineGun.BattleAddr(&DeathStatus) == 0) )
0x169 {
0x176 If (Random MOD 4 == 0)
0x176 {
0x17F BattleAddr(&TargetSelected) <- BattleAddr(&Self)
0x186 Perform("Vanish"[014E], EnemyAttack)
0x18C BattleAddr(&Self).BattleAddr(&SelfActive) <- 0
0x196 BattleAddr(&Self).BattleAddr(&Targetable) <- 0
0x1A0 LocalVar:MachineGun.BattleAddr(&Angry) <- 1
0x1AA }
0x1AA Else
0x1AA {
0x1AD }
0x1AD Else
0x1AD {
0x1B0 If (Random MOD 4 == 1)
0x1B0 {
0x1B5 BattleAddr(&TargetSelected) <- BattleAddr(&Self)
0x1BC Perform("Vanish"[014E], EnemyAttack)
0x1C2 BattleAddr(&Self).BattleAddr(&SelfActive) <- 0
0x1CC BattleAddr(&Self).BattleAddr(&Targetable) <- 0
0x1D6 }
0x1D6 Else
0x1D6 {
0x1D9 }
0x1D9 Else
0x1D9 {
0x1DC If ( (BattleAddr(&AllPlayers).BattleAddr(&ParalyzedStatus) == 0) )
0x1DC {
0x1E9 If (Not  (Random MOD 4) )
0x1E9 {
0x1F1 If ( (BattleAddr(&AllPlayers).BattleAddr(&ParalyzedStatus) == 0) )
0x1F1 {
0x1FE BattleAddr(&TargetSelected) <- RandomBit( (BattleAddr(&AllPlayers).BattleAddr(&ParalyzedStatus) == 0) )
0x20D }
0x20D Else
0x20D {
0x210 BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x218 Perform("Paralaser"[0169], EnemyAttack)
0x21E }
0x21E Else
0x21E {
0x221 BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x229 Perform("Laser Cannon"[0117], EnemyAttack)
0x22F }
0x22F Else
0x22F {
0x232 BattleAddr(&TargetSelected) <- RandomBit(BattleAddr(&AllPlayers))
0x23A Perform("Laser Cannon"[0117], EnemyAttack)
0x240 }
0x240 Else
0x240 {
0x243 POP(Random MOD 4)
0x244 }
0x244 Else
0x244 {

I'm reading this as:

1/4 chance use Vanish and set Machine Gun's "Angry" TempVar to 1
1/4 chance use vanish
2/4 chance: 1/4 chance use paralyser on rnd target without Paralyse
                  3/4 chance use Laser Cannon

which actually makes it:

2/8 chance use Vanish and set Machine Gun's "Angry" TempVar to 1
2/8 chance use vanish
1/8 chance use paralyser on rnd target without Paralyse
3/8 chance use Laser Cannon

Am I correct with this?

edit no I'm not, I'm completely wrong....
It goes:

1/4 chance use Vanish on Self and set Machine Gun's "Angry" TempVar to 1
1/4 chance use vanish on Self
2/4 chance: At least one Target without Paralyzed:
                   1/4 chance use paralyser on rnd target without Paralyse
                   3/4 chance use Laser Cannon on Rnd Target
                  All Targets with Paralyzed:
                   Use Laser Cannon on Rnd Target


Damn, the Machine Gun and Laser Cannon have been the most long winded enemies to try to decipher so far, even more so than the Eligor! Time for a break :)
« Last Edit: 2019-03-07 11:23:01 by DynamixDJ »

DynamixDJ

  • *
  • Posts: 240
  • 1111 1111 0111
    • View Profile
Re: [FFVII] Enemy AI Script questions
« Reply #21 on: 2019-03-07 16:06:58 »
There's a bug with the Soldier:3rd. It checks for the MP cost when using Sleepel, Ice2 and Bolt2, nut not Flying Sickle which costs 8MP. When it tries to use Flying Sickle with less than 8 MP it displays the message: Soldier:3rd's skill power is all used up

Kaldarasha

  • *
  • Posts: 2449
  • Prince of Model Editing
    • View Profile
Re: [FFVII] Enemy AI Script questions
« Reply #22 on: 2019-03-07 17:04:27 »
Sounds correct to me. I guess it's like a free turn for the player and shows that the enemy has run out of MP.

nfitc1

  • *
  • Posts: 3011
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Re: [FFVII] Enemy AI Script questions
« Reply #23 on: 2019-03-07 18:24:08 »
Yeah. While it is an odd choice to check for some actions and exclude one, this is still essentially intended behavior. Soldier:3rd only has 40 MP so it can only do Ice2 or Bolt2 once without the player somehow interfering (giving an ether by mistake or something). Personally, I think most human enemies should be allowed to utilize items once or twice if the need arises. Say they can use an ether (rather, an action that imitates the ether item) if their AI selects something they don't have MP for. That makes them "waste" their turn on a utility action which makes the experience of fighting "intelligent" characters more believable. This is during the ShinRa HQ raid so you'd expect company guards would have access to such supplies. But I digress...

This is definitely one of those AIs that could be simplified.

DynamixDJ

  • *
  • Posts: 240
  • 1111 1111 0111
    • View Profile
Re: [FFVII] Enemy AI Script questions
« Reply #24 on: 2019-03-08 06:46:17 »
I can understand the want to have a few Enemies that will run out of MP, the main issue that I have with the Soldier:3rd is the fact that it will call on Flying Sickle very often once it has run out of MP, which means that after just a few uses of Flying Sickle/Sleepel (8mp) and Ice2/Bolt2 (22mp), it will spend approximately 2/3 of its turn just doing nothing, with the text string appearing at the top.

Take a look at the Soldier:3rd's simplified AI script:

Code: [Select]
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  |
|   SOLDIER:3rd's BATTLE PLAN:                                               |
|                                                                            |
|  OPENING ATTACK: 1/4 Chance start from 1st Attack                          |
|                  1/4 Chance start from 2nd Attack                          |
|                  1/4 Chance start from 3rd Attack                          |
|                  1/4 Chance start from 5th Attack                          |
|                                                                            |
|    *If All Targets have Sleep, select Rnd Target                           |
|                                                                            |
|                                                                            |
|  1ST ATTACK: (<Split>)                                                     |
|                                                                            |
|   Use <Split> on Rnd Target without without Sleep*                         |
|                                                                            |
|    FOLLOW-UP: SOLDIER:3rd's HP > 75%                                       |
|                1/4 Chance: Use <Split> on Rnd Target without Sleep*        |
|                3/4 Chance: Do Nothing                                      |
|                                                                            |
|               SOLDIER:3rd's HP <= 75%:                                     |
|                1/3 Chance: Use <Split> on Rnd Target without Sleep*        |
|                2/3 Chance: Do Nothing                                      |
|                                                                            |
|               SOLDIER:3rd's HP <= 50%:                                     |
|                1/2 Chance: Use <Split> on Rnd Target without Sleep*        |
|                1/2 Chance: Do Nothing                                      |
|                                                                            |
|               SOLDIER:3rd's HP <= 25%:                                     |
|                Use <Split> on Rnd Target without Sleep*                    |
|                                                                            |
|                                                                            |
|  2ND ATTACK: (Sleepel)                                                     |
|                                                                            |
|   SOLDIER:3rd's HP > 75%:                                                  |
|                                                                            |
|    2/8 Chance: Use Sleepel on Rnd Target without Sleep; If All Targets     |
|                 have Sleep OR SOLDIER:3rd's MP < 8, move onto remaining    |
|                 6/8 chance                                                 |
|    3/8 Chance: Use <Split> on Rnd Target without Sleep*                    |
|    3/8 Chance: Use Flying Sickle on Rnd Target without Sleep*              |
|                                                                            |
|   SOLDIER:3rd's HP <= 75%:                                                 |
|                                                                            |
|    2/4 Chance: Use Sleepel on Rnd Target without Sleep; If All Targets     |
|                 have Sleep OR SOLDIER:3rd's MP < 8, move onto remaining    |
|                 2/4 chance                                                 |
|    1/4 Chance: Use <Split> on Rnd Target without Sleep*                    |
|    1/4 Chance: Use Flying Sickle on Rnd Target without Sleep*              |
|                                                                            |
|   SOLDIER:3rd's HP <= 50%:                                                 |
|                                                                            |
|    Use Sleepel on Rnd Target without Sleep; If All Targets have Sleep OR   |
|     SOLDIER:3rd's MP < 8:                                                  |
|      1/2 Chance use <Split> on Rnd Target without Sleep*                   |
|      1/2 Chance use Flying Sickle on Rnd Target without Sleep*             |
|                                                                            |
|                                                                            |
|    After 2ND ATTACK: 1/2 Chance jump to 3rd Attack for next turn           |
|                      1/2 Chance jump to 4th Attack for next turn           |
|                                                                            |
|                                                                            |
|  3RD ATTACK: (Bolt2)                                                       |
|                                                                            |
|   SOLDIER:3rd's HP > 75%:                                                  |
|                                                                            |
|    2/8 Chance: Use Bolt2 on Rnd Target with Sleep; If no Targets have      |
|                 Sleep, select Rnd Target; If SOLDIER:3rd's MP < 22, move   |
|                 onto remaining 6/8 chance                                  |
|    3/8 Chance: Use <Split> on Rnd Target without Sleep*                    |
|    3/8 Chance: Use Flying Sickle on Rnd Target without Sleep*              |
|                                                                            |
|   SOLDIER:3rd's HP <= 75%:                                                 |
|                                                                            |
|    2/4 Chance: Use Bolt2 on Rnd Target with Sleep; If no Targets have      |
|                 Sleep, select Rnd Target; If SOLDIER:3rd's MP < 22, move   |
|                 onto remaining 2/4 chance                                  |
|    1/4 Chance: Use <Split> on Rnd Target without Sleep*                    |
|    1/4 Chance: Use Flying Sickle on Rnd Target without Sleep*              |
|                                                                            |
|   SOLDIER:3rd's HP <= 50%:                                                 |
|                                                                            |
|    Use Bolt2 on Rnd Target with Sleep; If no Targets have Sleep, select    |
|     Rnd Target; If SOLDIER:3rd's MP < 22:                                  |
|      1/2 Chance use <Split> on Rnd Target without Sleep*                   |
|      1/2 Chance use Flying Sickle on Rnd Target without Sleep*             |
|                                                                            |
|                                                                            |
|    After 3RD ATTACK: 1/2 Chance jump to 1st Attack for next turn           |
|                      1/2 Chance jump to 5th Attack for next turn           |
|                                                                            |
|                                                                            |
|  4TH ATTACK: (Ice2)                                                        |
|                                                                            |
|   SOLDIER:3rd's HP > 75%:                                                  |
|                                                                            |
|    2/8 Chance: Use Ice2 on Rnd Target with Sleep; If no Targets have       |
|                 Sleep, select Rnd Target; If SOLDIER:3rd's MP < 22, move   |
|                 onto remaining 6/8 chance                                  |
|    3/8 Chance: Use <Split> on Rnd Target without Sleep*                    |
|    3/8 Chance: Use Flying Sickle on Rnd Target without Sleep*              |
|                                                                            |
|   SOLDIER:3rd's HP <= 75%:                                                 |
|                                                                            |
|    2/4 Chance: Use Ice2 on Rnd Target with Sleep; If no Targets have       |
|                 Sleep, select Rnd Target; If SOLDIER:3rd's MP < 22, move   |
|                 onto remaining 4/4 chance                                  |
|    1/4 Chance: Use <Split> on Rnd Target without Sleep*                    |
|    1/4 Chance: Use Flying Sickle on Rnd Target without Sleep*              |
|                                                                            |
|   SOLDIER:3rd's HP <= 50%:                                                 |
|                                                                            |
|    Use Ice2 on Rnd Target with Sleep; If no Targets have Sleep, select Rnd |
|     Target; If SOLDIER:3rd's MP < 22:                                      |
|      1/2 Chance use <Split> on Rnd Target without Sleep*                   |
|      1/2 Chance use Flying Sickle on Rnd Target without Sleep*             |
|                                                                            |
|                                                                            |
|    After 4TH ATTACK: 1/2 Chance jump to 1st Attack for next turn           |
|                      1/2 Chance jump to 5th Attack for next turn           |
|                                                                            |
|                                                                            |
|  5TH ATTACK: (Flying Sickle)                                               |
|                                                                            |
|   Use Flying Sickle on Rnd Target without Sleep*                           |
|                                                                            |
|  FOLLOW-UP: SOLDIER:3rd's HP > 75%                                         |
|              1/4 Chance: Use Flying Sickle on Rnd Target without Sleep*    |
|              3/4 Chance: Do Nothing                                        |
|                                                                            |
|             SOLDIER:3rd's HP <= 75%:                                       |
|              1/3 Chance: Use Flying Sickle on Rnd Target without Sleep*    |
|              2/3 Chance: Do Nothing                                        |
|                                                                            |
|             SOLDIER:3rd's HP <= 50%:                                       |
|              1/2 Chance: Use Flying Sickle on Rnd Target without Sleep*    |
|              1/2 Chance: Do Nothing                                        |
|                                                                            |
|             SOLDIER:3rd's HP <= 25%:                                       |
|              Use Flying Sickle on Rnd Target without Sleep*                |
|                                                                            |
|                                                                            |
|    After 5TH ATTACK: Jump to 2nd Attack for next turn                      |
|>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~o~~~~~~~~~~~~~~~~~~~~~~~~~~~~<|

So, once it has less than 8MP, it will:

 - Use Split on rnd target (sometimes twice)
 - 1/2 Chance use Flying Sickle
 - 1/2 Chance use Flying Sickle
 - 1/2 Chance use Flying Sickle
 - Use Flying Sickle

so of its 5 Attacks, it could spend four of those doing nothing, and seeing as the attack sequence can jump around quite a lot you could find yourself not being attacked at all by an enemy that quite frankly, shouldn't be this underwhelming.

In my opinion, having the Soldier:3rd only attack players physically when they do not have sleep, or only attacking them magically when they do have sleep is a clever AI design. It's the type of thing that I want to focus on when I write my mod, instead of just 50% do this 50% do that. The Solder:3rd will never cure a character of sleep; you have to cure them yourself by attacking them or wait it out.

The Soldier:3rd goes from being a well thought out enemy to an enemy that poses very little risk to the Player. If anything, it would make more sense to balance it out a little bit by increasing the chance of using Split, and decreasing the chances of using Flying Sickle for 3 of the 5 attacks.

The Flapbeat only have a 1/4 chance of using Flying Sickle, so when they run out of MP at least 3/4 of the time they will still attack the player and the same thing with the Frozen Nail.

Also, I hadn't thought of the idea of having an Ether simulated attack, that's a good one, deffo gonna consider that for my mod!
« Last Edit: 2019-03-08 06:50:40 by DynamixDJ »