Author Topic: [FF7] Enemy AI main script template  (Read 18710 times)

ff7rules

  • *
  • Posts: 423
    • View Profile
[FF7] Enemy AI main script template
« on: 2009-10-03 14:12:59 »
After a long time away i have decided to post this its helped speed my project up so much that its just a crime to keep to myself anymore.

Delete the old Main part of the script and replace it with this. Yes i know its alot of work to put it all in by hand but its gonna be worth it


12 2070
02 20a0
82
91
81
60 02
34
52
70 0019
60 20
61 XXXX any attack you want
92
81
60 05
34
52
70 0024
60 20
61 XXXX any attack you want
92
81
60 05
34
52
70 0032
60 20
61 XXXX any attack you want
92
81
60 05
34
52
70 004e
60 20
61 XXXX any attack you want
92
81
60 05
34
52
70 005c
60 20
61 XXXX any attack you want
92
81
60 05
34
52
70 006a
60 20
61 XXXX any attack you want
92
81
60 05
34
52
70 0078
60 20
61 XXXX any attack you want
92
81
60 05
34
52
70 0086
60 20
61 XXXX any attack you want
92
81
60 05
34
52
70 0094
60 20
61 XXXX any attack you want
92
81
60 05
34
52
70 00a2
60 20
61 XXXX any attack you want
92
81
60 05
34
52
70 00b0
60 20
61 XXXX any attack you want
92
81
60 05
34
52
70 00be
60 20
61 XXXX any attack you want
92
81
60 05
34
52
70 00cc
60 20
61 XXXX any attack you want
92
81
60 05
70 00da
60 20
61 XXXX any attack you want
92
81
60 05
34
52
70 00e8
60 20
61 XXXX any attack you want
92
73

Ok here is the template which will do whatever attacks you choose at random. How do you add attacks youre asking well heres how.

1. First find an attack you want in PRC and copy it
2. Paste it into the scene you want to edit the AI for.
3. Look at the Attack ID for the attack thats what the XXXX is going to be in the script template.
4. Once you have inserted all the attacks and put them into the script you need to give them an animation
5. To do this go to the scene you want an click Animations/formations.
6. Find your enemy you have edited the AI for.
7. Look at the attacks it has and use whatever animation you think is best for that attack they can be all the same if you want.
8. Don't forget to save your scene!
9. Test it! if you have done everything right itll be like a whole new battle.

« Last Edit: 2014-07-17 18:24:01 by Covarr »

Murak Modder

  • *
  • Posts: 171
  • http://kevan.org/brain.cgi?murak.
    • View Profile
hm, this seems to work pretty well, it makes the enemy's attacks almost unpredictable and still provides a interesting array of attacks depending on your own personal tastes of how 'complicated' a enemy should be with the number of attacks.

ff7rules

  • *
  • Posts: 423
    • View Profile
hm, this seems to work pretty well, it makes the enemy's attacks almost unpredictable and still provides a interesting array of attacks depending on your own personal tastes of how 'complicated' a enemy should be with the number of attacks.

That was the aim, you can also repeat attacks so you have more chance of that one attack.

Bosola

  • Fire hazard!
  • *
  • Posts: 1752
    • View Profile
    • My YouTube Channel
This is really helpful. This means plenty more enemy modding should be possible for those of us a bit intimidated by the prospect of learning assembler!

Bosola

  • Fire hazard!
  • *
  • Posts: 1752
    • View Profile
    • My YouTube Channel
Also, if space were short, could I cut down the length of the script? As I understand it, the '70' opcodes are a sort of 'if popped value isn't true, go to...' command. Presumably I'd have to make sure that the 70 XXXX never pointed at a part of the script that didn't exist, am I correct? Would that be enough if I wanted to shorten the script?

I assume that I'd have to end on

61 [attack]
92
73

To finish it, right?

Or would that leave a possibility of the enemy not attacking? Could I use

92
81
60 05
34
52
72 00YY [just the next line]
60 20
61 XXXX any attack you want
92
73

Would that work? I imagine that I could delete the 72 line altogether, but that might make the 70 XXXX lines earlier on point to the wrong places in the script.
« Last Edit: 2009-11-09 22:01:50 by Bosola »

nfitc1

  • *
  • Posts: 3011
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Now that I'm actually looking at your script I have issues with it.

1:
Quote
12 2070
02 20a0
82
91

This translates into:
Code: [Select]
POP(RandomBit(AllOpponentMask))while the TargetMask address is still loaded. Should be:

Code: [Select]
12 2070
02 20a0
82
90
TargetMask <- RandomBit(AllOpponentMask)
Otherwise target could be anything. I've never tested what would happen in this case. Likely a copy error, I'll forgive it. ;)

2:
Your blocks of:
Quote
81
60 02
34
52
70 0019
60 20
61 XXXX any attack you want
92
are too long.
Try this:
Code: [Select]
81
60 XX (however many attacks said enemy has
34
at the beginning of the script then multiple:
Code: [Select]
60 WW <- sequential
71 XXXX <- next block like this
60 20
61 YYYY <- attack
92
72 ZZZZ <- To End of Script
blocks followed by:
Code: [Select]
91 <- to pop the (Random MOD 16) value
73

3:
Your jumps are WAY off. There are whole blocks that don't get used at all. Your script goes to D8, but you have two jumps beyond that. This is likely just your copying of them, so I'll excuse it.

4:
The way you have it now, an enemy could perform any number of their attacks in one turn. If that's your intention then fine. It's creating a new random number each time 81 is called. A better practice is to put a random number into a variable so it keeps its value:
Code: [Select]
12 0000
81
90
LocalVar:0000 <- Random

As yours is:
Quote
If (Not Random MOD 2)
{
}
If (Not Random MOD 5)
{
}
If (Not Random MOD 5)
{
}
...
Each one of those randoms is a different value. Again, if that's the effect you're going for then great. However this means that all 15 of your attacks can be called in one round. Since they're NOT-ed that raises the chance of EACH attack to be used to 80%. There's a 2.2% chance that said enemy will use ALL their attacks in one round without giving the player a chance to retaliate. I wouldn't really call that good practice, but it's different.

Something else to keep in mind:
COUNTER ATTACKS DON'T OCCUR UNTIL THE SCRIPT CURRENTLY EXECUTING IS COMPLETED.
I'm not yelling at you, Just trying to impress it upon people.

Let me propose this script to you all:


12   2070
02   20A0
82   
90   
81   
60   10
34   
60   00
71   001A
60   20
61   0100   *
92   
72   00E4
60   01
71   0028
60   20
61   0101   *
92   
72   00E4
60   02
71   0036
60   20
61   0102   *
92   
72   00E4
60   03
71   0044
60   20
61   0103   *
92   
72   00E4
60   04
71   0052
60   20
61   0104   *
92   
72   00E4
60   05
71   0060
60   20
61   0105   *
92   
72   00E4
60   06
71   006E
60   20
61   0106   *
92   
72   00E4
60   07
71   007C
60   20
61   0107   *
92   
72   00E4
60   08
71   008A
60   20
61   0108   *
92   
72   00E4
60   09
71   0098
60   20
61   0109   *
92   
72   00E4
60   0A
71   00A6
60   20
61   010A   *
92   
72   00E4
60   0B
71   00B4
60   20
61   010B   *
92   
72   00E4
60   0C
71   00C2
60   20
61   010C   *
92   
72   00E4
60   0D
71   00D0
60   20
61   010D   *
92   
72   00E4
60   0E
71   00DE
60   20
61   010E   *
92   
72   00E4
60   20
61   010F   *
92
91
73

That will target one random enemy with any of 16 different attacks with index 0100 - 010F (marked by '*'). Also, using the 71 jump won't pop the (Random MOD 16) value so you can compare it multiple times. It also jumps to the end after one attack is performed. It's slightly larger in size, but fewer lines. This disassembles into:

Code: [Select]
TargetMask <- RandomBit(AllOpponentMask)
If (Random MOD 16 == 0)
{
Perform([0100], EnemyAttack)
}
ElseIf (Random MOD 16 == 1)
{
Perform([0101], EnemyAttack)
}
ElseIf (Random MOD 16 == 2)
{
Perform([0102], EnemyAttack)
}
ElseIf (Random MOD 16 == 3)
{
Perform([0103], EnemyAttack)
}
ElseIf (Random MOD 16 == 4)
{
Perform([0104], EnemyAttack)
}
ElseIf (Random MOD 16 == 5)
{
Perform([0105], EnemyAttack)
}
ElseIf (Random MOD 16 == 6)
{
Perform([0106], EnemyAttack)
}
ElseIf (Random MOD 16 == 7)
{
Perform([0107], EnemyAttack)
}
ElseIf (Random MOD 16 == 8)
{
Perform([0108], EnemyAttack)
}
ElseIf (Random MOD 16 == 9)
{
Perform([0109], EnemyAttack)
}
ElseIf (Random MOD 16 == 10)
{
Perform([010A], EnemyAttack)
}
ElseIf (Random MOD 16 == 11)
{
Perform([010B], EnemyAttack)
}
ElseIf (Random MOD 16 == 12)
{
Perform([010C], EnemyAttack)
}
ElseIf (Random MOD 16 == 13)
{
Perform([010D], EnemyAttack)
}
ElseIf (Random MOD 16 == 14)
{
Perform([010E], EnemyAttack)
}
Else
{
Perform([010F], EnemyAttack)
}
POP(Random MOD 16)
SCRIPT END
More traditional and has no errors. ;) If you want fewer attacks, knock it out in chunks of
Code: [Select]
60   00
71   001A
60   20
61   0100   *
92   
72   00E4
.
Changing probabilities is fun too, but that's more complicated.

Sorry this post and that script are so long. I'm going to try to make it possible for copy-pasting from the clipboard into the next PrC/WM versions. I've fixed the length issue, but won't release it until the clipboard thing is working. Until then, keep the KERNEL.BIN script length in the triple digits at least.

Bosola

  • Fire hazard!
  • *
  • Posts: 1752
    • View Profile
    • My YouTube Channel
Thanks for that very comprehensive post. Not that this is the first time you've offered something so useful to the modding community.

I have just a couple of questions:

1. You say that for a shorter script, we should 'knock it out in chunks of [...]'. Your wording leaves me a little unsure - do you mean we should excise portions of

Quote
60   00
71   001A
60   20
61   0100   *
92  
72   00E4


2. You mention using attacks with indexes from 0100 to 010F. Why is this? Looking at the code, and with the info on opcodes, I can't see an obvious reason for this besides making it convenient for the writer to run through the text (but my understanding of assembler is rubbish). I haven't checked this, but I always believed that for an enemy to use a learnable enemy skill, it had to have a very specific attack index. Could I write a script using this template that allowed an enemy to use a learnable enemy skill?
« Last Edit: 2009-11-10 22:51:53 by Bosola »

secondadvent

  • *
  • Posts: 287
    • View Profile
1 - removing the sections similar to the one you quoted will remove excess, unneeded script once you have all of the attacks you wanted in, so for example, you only wanted to use two attacks, your final script would look something like this:

Code: [Select]
12   2070
02   20A0
82   
90   
81   
60   02 //only two attacks are to be used, if it was kept at 10 (16 decimal), it would only have a 2/16 or 1/8 chance to use one of the attacks, or else it'd do nothing.
34   
60   00
71   001A
60   20
61   0100   *
92   
72   0025 // i hope these two jumps are pointing to the correct spot :P
60   01
71   0025
60   20
61   0101   *
92   //no 72 (absolute) jump is needed to go to the end, because the end is right after this
91
73

you just remove the extra attack checks, and update the amount of attacks at the beginning, and you have a working script.

2 - those attacks, when called by the enemy AI, refer to (mostly) unused attack indexes, so i am guessing he used it as a sort of placeholder for attacks, since you will be changing them all more than likely anyway. enemy skills are from index 0048 to 005f, and are not called in any special ways for enemies, at least as far as i can tell, so you shuld easily make an enemy use an enemy skill using this template.

Bosola

  • Fire hazard!
  • *
  • Posts: 1752
    • View Profile
    • My YouTube Channel
Thanks. I had sorta guessed that leaving the line as [60 10] rather than [60 02] would mean that the first attacks would still only have a 1/16 chance of occuring each, but that the last attack would take up the 'remainder' of the values. Just removing the extra sections, I think, would make a script that did

- random no. 1-16
- if RND = 1, do alpha
- if RND = 2, do beta
- ELSE do gamma

(I know that's not strictly what the code does, I'm just outlining its operation)

So alpha and beta would only have a 1/16 chance of occurring each, but gamma has a 14/16 chance instead.

Thanks again for the help.

« Last Edit: 2009-11-10 23:55:09 by Bosola »

secondadvent

  • *
  • Posts: 287
    • View Profile
ok, the way i had written it, you would not have gotten the remaining attack chance used, and it would just skip it's turn if none of the checks activated, but if you just remove the check from the final attack, it would absorb the rest of the chances to go. i just didn't notice that NFITC1 had removed the check from the final attack, so if you want less than 16 attacks, remove all that are not used, and also remove the final attack's check if you want to make the last attack possibly have a higher chance to activate by increasing the random at the start.

there is a lot to learn about AI editing, but once you get the hang of it, everything becomes much easier, and you also notice how badly the original AI was for most things (if you couldn't tell already :roll:). it has been a while since i have done anything for ffvii, so i am kind of rusty, but if you have any questions, i will try to answer them the best that i can  :wink:.

nfitc1

  • *
  • Posts: 3011
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
@Bosola:

Yes, removing pieces of it like that will increase the chances of the final attack being executed. It's using fuzzy/implicit logic to always do SOMETHING. Secondadvent's code works, but that last check is superfluous, albeit direct/explicit logic. If you change the first 60 10 to something else, then it keeps the probabilities equal. If you get rid of seven of those blocks, subtract 7 from that number (in hex, of course) and everything will then be a 1/9 chance, rather than 8 1/16 chance attacks and one 1/2 chance attack. If that's what you want then great! You could even extend this to allow for all 32 attacks an enemy can have.
You can also play around with probabilities too. If you want three attacks to have a 1/16 chance each and four attacks to have a 1/8 chance each then one that catches the rest you can do this:

(pseudocode)
If (Random MOD 0)
{
     Perform(Attackthefirst, EnemyAttack)
}
ElseIf (Random MOD 1)
{
     Perform(Attackthesecond, EnemyAttack)
}
ElseIf (Random MOD 2)
{
     Perform(Attackthethird, EnemyAttack)
}
ElseIf ((Random MOD 3) or (Random MOD 4))
{
     Perform(Attackthefourth, EnemyAttack)
}
ElseIf ((Random MOD 5) or (Random MOD 6))
{
     Perform(Attackthefifth, EnemyAttack)
}
ElseIf ((Random MOD 7) or (Random MOD 8))
{
     Perform(Attackthesixth, EnemyAttack)
}
ElseIf ((Random MOD 9) or (Random MOD 10))
{
     Perform(Attacktheseventh, EnemyAttack)
}
Else
{
     Perform(FinalAttack, EnemyAttack)
}
Pop (Random)
END SCRIPT

I leave you to figure out the exact logic, but you can achieve this with that template if you know what to delete. Keep in mind, if you change the template, you'll have to watch your jumps. This is also the same Random number each time if you use the 71 jumps instead of the 70 jumps.

Bosola

  • Fire hazard!
  • *
  • Posts: 1752
    • View Profile
    • My YouTube Channel
Thanks for that.

I've been doing some experimentation, trying to find a script that would allow me to alter statuses. By varying the figures in Mirage's script I've made a little headway, and I thought (apparently incorrectly) that it worked. Here it is:

12 2060
10 XXXX
80
60 Y
90

Where XXXX is the status in question and Y is the value to set it to.

What to use for XXXX? Well, I found that 4220 referred to the death status, 4230 to barrier and 4225 to fury, etc. as per mentioned in the status effects section of the wiki.

So, if you want perma-regen on a monster, I thought I'd use

12 2060
10 422F
80
60 1
90

And if we enter this into a blank section, and disassemble...




...But, in the field, these scripts don't seem to work. I tried setting the [AI setup] of Midgar Zolom to grant it perma regen, but it doesn't seem to work. I then tried setting a physical counter that set HP to 1, but that didn't work either. Any ideas on where I could be going wrong?
« Last Edit: 2009-11-12 17:11:36 by Bosola »

nfitc1

  • *
  • Posts: 3011
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Few problems with this.

#1. This kind of discussion needs its own thread or forum. Not a criticism on you, there just needs to be a better place for it.

#2. Your address is wrong. Don't use initial status. That's loaded before the battle is and then ignored. Use address 400Fh instead. That sets the status directly.

#3. You won't know if it even has regen unless you sense it. The status will be there, but it won't glow like a regen'd character will. Don't know if that's how you checked or not. Hopefully so.

#4. The Pre-battle isn't the best place to set these things up. The game kinda disregards time-dependent statuses set here. Set it in the Post-Attack and it will work fine.

12 2060
10 400F
80
60 1
90

Putting that in Post-Attack will make sure it's in a virtually-constant Regen state. Regen runs out after a time, that can't be stopped. Something has to start the timer for it to work. Pre-Battle apparently can't do this.

Bosola

  • Fire hazard!
  • *
  • Posts: 1752
    • View Profile
    • My YouTube Channel
Quote
Few problems with this.

#1. This kind of discussion needs its own thread or forum. Not a criticism on you, there just needs to be a better place for it.

Yes, I do see what you mean. I suppose I just didn't want to open an entirely new thread on the matter. I do think that dedicated threads would be a good idea. Especially as otherwise there's a real risk of the ProudClod and WallMarket threads being derailed.

As for the rest - yes, it seems to work. I gave Jenova-DEATH the post-attack regen status, and she started to glow the moment she was hit by any 'attack' (damaging or not). Didn't work on Chekov - caused a freeze for some reason that I'm not quite certain of. In the previous case, the enemy didn't even show evidence of regeneration when I traced its HP with Sense. At any rate, this is all very useful.


nfitc1

  • *
  • Posts: 3011
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
OK, so get this. I got PrC to, as far as I can tell, use the windows clipboard for copy-pasting. So if you copy things like

60 20
12 2070
10 4000
60 01
81
56
93 "What's happening?!!?1?"
91
A0 01 Just popped something (%d)
91
73

as straight text, you'll be able to paste it into the AI editing window. It's a pretty flexible little format nazi. It should read

60 01

the same way it reads these:

60               01  <- spaces
60   01   <- tab
6001
60mary had a little lamb01
60543201

611234431234 will be read as "61 1234". But 61ittybitty321 will throw an error that PrC catches and disposes of. 61 on a line by itself is read as "61 0061". A little odd, but at least it doesn't crash.

I just copy-pasted my script up there through the clipboard and it pasted (sans asterisks) and disassembled exactly like I thought it would. You can inject it into a script like before, or you can paste it into a blank section and it'll make that into the script and add the end script byte if there isn't one in the script you copied. Pretty cool, huh?

Does anyone want this feature on WM? It's easy enough to inject that ability, but there's not much room for the copy paste buttons so they'd likely be a right-click menu.

Bosola

  • Fire hazard!
  • *
  • Posts: 1752
    • View Profile
    • My YouTube Channel
Sounds good. Would make it a lot simpler to use templates, too.

secondadvent

  • *
  • Posts: 287
    • View Profile
you wouldn't happen to be planning on having a way to dump single enemy AI sections, would you? that would be a very nice thing to have (especially if it can import them as well :-P), and is something i have been wanting for a while :wink:.

nfitc1

  • *
  • Posts: 3011
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
you wouldn't happen to be planning on having a way to dump single enemy AI sections, would you? that would be a very nice thing to have (especially if it can import them as well :-P), and is something i have been wanting for a while :wink:.

That's a good point, but you could just copy-paste them yourselves if you only want one enemy's worth.

ff7rules

  • *
  • Posts: 423
    • View Profile
wow once again nfitc1 you have outdone me, i propsed this template purely to help noobs and yeah i have written it out wrong its meant to perform 16 attacks on one random enemy so thanks for correcting that. The new addition of been able to copy and paste into PRc would be a godsend! i am so sick of typing this template in again and again and again lol. thanks for helping people out on this and you also tought me something new so thanks.

Bosola

  • Fire hazard!
  • *
  • Posts: 1752
    • View Profile
    • My YouTube Channel
I'm not sure if this is right, but I think I've written a template to insert at the opening of a script, that activates whenever the creature is in a certain status.

Here it is, in this case checking for Barrier.

02 2060
01 4010 <- This is Barrier
80
60 00 <- I'm having the enemy respond to *not* having Barrier, so this is 0. If you want an enemy to cure itself, choose 1 instead
40
70 [points to the start of the normal script, usually 0019]
12 2070
02 2060 <- Target self
90
60 20
60 X <- Attack / spell index
92
72 [point to position of the END opcode]
{Normal script starts here...}

It seems to work, although I haven't thoroughly tested it yet.


ff7rules

  • *
  • Posts: 423
    • View Profile
I'm not sure if this is right, but I think I've written a template to insert at the opening of a script, that activates whenever the creature is in a certain status.

Here it is, in this case checking for Barrier.

02 2060
01 4010 <- This is Barrier
80
60 00 <- I'm having the enemy respond to *not* having Barrier, so this is 0. If you want an enemy to cure itself, choose 1 instead
40
70 [points to the start of the normal script, usually 0019]
12 2070
02 2060 <- Target self
90
60 20
60 X <- Attack / spell index
92
72 [point to position of the END opcode]
{Normal script starts here...}

It seems to work, although I haven't thoroughly tested it yet.



This is the pre battle script you are trying to do yeah? looks right apart from i always thought the pointer was to020 not 019 but im proberbly wrong.

Bosola

  • Fire hazard!
  • *
  • Posts: 1752
    • View Profile
    • My YouTube Channel
No. This is to be inserted at the start of a Main script.

It means that an enemy will, as its first priority, buff / heal itself when necessary (at least that's the theory).

I'm pretty sure the pointer is 19, though I've been wrong plenty of times before. At any rate, I'd recommend checking before using it.

Bosola

  • Fire hazard!
  • *
  • Posts: 1752
    • View Profile
    • My YouTube Channel
Edit: See the message below for a better script

Simple Alternating Script

This AI script simply has an enemy swap between two attacks. This won't be as useful as a normal 'random' script, but may be appropriate in specific circumstances.

You need to use the following Pre-Battle code:

10 0020
60 00
90
73

In formations where LocalVar20 (0020) is otherwise being used, use an address that isn't being taken (find the highest address of any LocalVar used in the battle, and add 20). Obviously, if several enemies will use this script, you'll have to find another address to replace 0020.

Assuming you're using 0020 as the LocalVar for this, use the following for the Main / Counter / Ally Death script:

00 0020
60 0
71 001E
12 2070
02 20A0
82

90
60 20
61 XXXX
92
10 0020
60 01
90
72 0032
10 0020
60 00
90
12 2070
02 20A0
82

90
60 20
61 YYYY

92
73

This is for targeting. Change it if you don't want to target a random single enemy

This is where you insert the indexes of the attacks you want to use.

This disassembles to:

If (LocalVar:0020 == 0)
{
   TargetMask <- RandomBit(AllOpponentMask)
   Perform("whatever"[xxxx], EnemyAttack)
   LocalVar:0020 <- 1

}

Else
{
   LocalVar:0020 <- 0
   TargetMask <- RandomBit(AllOpponentMask)
   Perform("etc"[yyyy], EnemyAttack)
}
SCRIPT END


'Whatever' is performed first; then 'etc', then 'whatever', then 'etc'...

Tested and (seems) to work.
« Last Edit: 2009-12-27 19:07:22 by Bosola »

nfitc1

  • *
  • Posts: 3011
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Bosola: Good script and nice concept, but let me see if I can "upgrade it". You forgot to pop the LocalVar(0020) at the end of your script, but it's likely not going to cause a problem on "easy" monsters that only take a few rounds to defeat. I don't know the size of the stack, but I can't say whether it gets cleared completely after the enemies turn or not. Best err on the side of caution, ne? ;)

Here's my version:

11   0020
01   0020
60   00
71   0015
12   0040
61   0100
90
72   001C
12   0040
61   0101
90
12   2070
02   20A0
82
90
60   20
02   0040
92
60   01
30
60   01
34
90
73

translates to:
Code: [Select]
If (LocalVar:0020 == 0)
{
LocalVar:0040 <- 256   *attack index of attack 1
}
Else
{
LocalVar:0040 <- 257   *attack index of attack 2
}
TargetMask <- RandomBit(AllOpponentMask)
Perform(LocalVar:0040, EnemyAttack)
LocalVar:0020 <- LocalVar:0020 + 1 MOD 1
SCRIPT END

More tightly weaved, but same function and it's two bytes shorter. This also makes it easier to add additional attacks:

60   01
71   AAAA <- address of next test of LocalVar(0020)
12   0040
61   XXXX <- next attack
90
72   BBBB <- address of target assignment

disassembles:

Code: [Select]
ElseIf (LocalVar:0020 == 1)
{
LocalVar:0040 <- 258
}

Downsides? This assumes all attacks will be single-target attacks. Easily fixed, but it becomes a little larger:

12   2070
02   20A0
82
90
11   0020
01   0020
60   00
71   001D
12   0040
61   0100
90
72   0049
60   01
71   002C
12   0040
61   0102
90
72   0049
12   2070
02   20A0
90
60   02
71   0042
12   0040
61   0101
90
72   0049
12   0040
61   0103
90
60   20
02   0040
92
60   01
30
60   01
34
90
73

Disassembles into:

Code: [Select]
TargetMask <- RandomBit(AllOpponentMask)
If (LocalVar:0020 == 0)
{
LocalVar:0040 <- 256   *attack index of attack 1 that targets one
}
ElseIf (LocalVar:0020 == 1)
{
LocalVar:0040 <- 258   *attack index of attack 2 that targets one
}
Else
{
TargetMask <- AllOpponentMask
If (LocalVar:0020 == 2)
{
LocalVar:0040 <- 257   *attack index of attack 1 that targets all enemies
}
Else
{
LocalVar:0040 <- 259   *attack index of attack 2 that targets all enemies
}
}
Perform(LocalVar:0040, EnemyAttack)
LocalVar:0020 <- LocalVar:0020 + 1 MOD 4   *value between 0-3
SCRIPT END

This is more of a cycle than an alternate, but it has very basic conditions.

I'm not sure about the PSX version, but all "varibles" in the PC version are initialized at 0. That means that the Pre-battle assigning of LocalVar(0020) <- 00 isn't necessary on the PC version.

secondadvent

  • *
  • Posts: 287
    • View Profile
yeah, i had stopped doing that after some time as well, after having seen a few AI sections, since some enemies do not initialize the variable at all and read it in later to see if it is 0 (for first attack). I think this is the same for the psx version as well, since i had originally used some of my AI changes on it and everything worked fine, but i cannot say for sure right now :-P. i like these layouts, they are all nice for a quick and easy setup for an enemy, but i still like to do all of mine manually (and happen to have a thing for row checking :wink:).