Qhimm.com Forums

Final Fantasy 7 => FF7 Tools => Topic started by: ff7rules on 2009-10-03 14:12:59

Title: [FF7] Enemy AI main script template
Post by: ff7rules 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.

Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: Murak Modder on 2009-10-03 15:59:30
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.
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: ff7rules on 2009-10-03 16:01:42
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.
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: Bosola on 2009-11-09 20:27:35
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!
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: Bosola on 2009-11-09 21:43:43
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.
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: nfitc1 on 2009-11-10 15:28:27
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.
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: Bosola on 2009-11-10 22:50:01
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?
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: secondadvent on 2009-11-10 23:19:30
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.
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: Bosola on 2009-11-10 23:53:15
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.

Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: secondadvent on 2009-11-11 00:35:07
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:.
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: nfitc1 on 2009-11-11 13:14:49
@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.
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: Bosola on 2009-11-12 17:09:53
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...

(http://img69.imageshack.us/img69/9963/regen.png)


...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?
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: nfitc1 on 2009-11-12 20:36:08
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.
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: Bosola on 2009-11-12 21:34:57
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.

Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: nfitc1 on 2009-11-13 18:43:14
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.
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: Bosola on 2009-11-13 21:54:04
Sounds good. Would make it a lot simpler to use templates, too.
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: secondadvent on 2009-11-15 00:46:26
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:.
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: nfitc1 on 2009-11-15 20:46:17
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.
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: ff7rules on 2009-11-15 23:35:04
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.
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: Bosola on 2009-11-16 12:29:44
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.

Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: ff7rules on 2009-11-16 12:31:39
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.
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: Bosola on 2009-11-16 12:56:54
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.
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: Bosola on 2009-12-20 04:49:57
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.
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: nfitc1 on 2009-12-21 16:04:43
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.
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: secondadvent on 2009-12-21 16:17:28
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:).
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: Bosola on 2010-01-30 01:57:40
Here's another template. It looks for targets without or with a particular status, then chooses to buff or debuff them. Let's say that our monster wants to haste everyone on its team that it can.

02 2080 [friends - 20A0 for opponents]
00 4008 [haste]
80 [creates a list of friends and their hasted / un-hasted statuses]
60 0 [haste not active]
40 [compare with value '0' - returns a 1 if there are un-hasted friends]
02 2060
02 4140
80 [pushes own MP]
61 [index of spell to use - likely haste]
86
42 [sees if the spell costs less MP than available]
50 [IF both are TRUE...]
70 yy [don't jump to yy (the ELSE action)]
12 2070
02 2080 [friends]
00 4008 [haste]
80
60 0 [haste not active]
40
82
90
60 20 [use if an enemy specific attack; use 01 if player-usable magic]
61 XX [index of attack]
92
72 LLLL  [last line of script]
[This next line is YY, would be 32 if nothing preceded the script]
Etc. for else...


This would disassemble to:

If ( ( (AllAllyMask.Status:Haste == 0)  And  (Self.MP >= MPCost(of attack)) ) )
{
   TargetMask <- RandomBit( (AllAllyMask.Status:Haste == 0) )
   Perform("Whatever"[XXXX], EnemyAttack)

}

Else...

================
For direct copy-pasting...
================

02 2080
00 4008
80
60 0
40
02 2060
02 4140
80
61 19
86
42
50
70 32
12 2070
02 2080
00 4008
80
60 0
40
82
90
60 01
61 19
92
72 LLLL
[This next line is YY, would be 32 if nothing preceded the script]
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: Bosola on 2010-01-30 02:05:20
So, in the above example, you would have a monster looking at its friends. If friends don't all have haste, then it randomly selects one and casts some haste-conferring move on it (could be the spell, or some other buffing move). You could make a small set of changes to have it look at the party, choose members who don't have poison and cast bio on them.

Of course, this would mean that should the party have ribbons, the enemy will spend the entire battle bio-ing fruitlessly. There are ways around this, such as including a counter, so that the monster doesn't do it too many times, and eventually gives up.
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: Bosola on 2010-01-30 02:06:15
[Sorry, chose 'quote' rather than 'modify'...]
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: Bosola on 2010-01-30 23:25:07
QUADRA-POST

I've been looking into how H0512 and H0512-opt 'work', hoping for code that can tell me how to make one creature support a specific peer, but not itself.

Here's how it does it:

1. In H0512's Pre-Battle script, there is a set of opcodes:

12 0020
02 2050
02 4120
80
60 41
40
90
73

This pushes the address of localvar20 to the stack. Then, it uses a mask to create a list of players who are both active, and have enemy ID (address 4120) $41 (65 - that's H0512-opt's ID), and stores it at 0020.

2. Now, one way it can interact with its peers is to directly change the statuses of them in a script. One example appears in the Death script, where H0512-opt is made un-targetable:

12 0020
10 4023
80
60 00
90

This uses the mask, taking the list of active players with ID 65, then specifying the target-able flag. Basically, you use 12 0020 as you would use 12 2060 if you wanted to change the stats of the script owner, or 12 20A0 if you wanted to change the stats of all opponents.

3. What if you want to, say, target a peer with a particular ID? Well, you can probably guess from above that you might substitute 02 0020 for a 'all opponent mask', 'all allies mask' etc. etc. You're right. H0512's code for targeting H0512-opt is:

12 2070
02 0020
90

You can also mask the list provided by 0020. H0512 masks the list, targetting Opt-units which are dead:

12 2070
02 0020
00 4000
80
60 01
40
90

Address 4000 is 'dead'. A list of Opt-units and their death-statuses is created, then the opcode 40 compares the value of each entry with the 'True' condition. You could add an 82 before the 'store' opcode (90) of that if you wanted to pick a random dead H0512-opt unit.

You could easily adapt this code to have a monster buff another monster in certain conditions. I think, though, that you'd have to change the way that LocalVar0020 is set, instead setting it at the start of the script and having it choose from *alive* units, or else I think you might get your monster trying to cast haste on something that's dead. Of course, you could just set a condition saying that the ability is only used when monster X is both unbuffed and alive, and only target those living, but I think changing the way LocalVar0020 is set would be easier.

Also, I was wondering if one of our veterans could tell me: what if I wanted to use multiple conditions in targeting? What if I wanted to choose targets who were both asleep and slowed?

Edit: Found it. I looked at how Helletic Hojo locates targets for Pile Banger:

12 2070 [pushes address of 'target mask']
02 20A0 [all opps]
00 4006 [confu]
80 [mask - all opponents and their confu statuses]
60 00
40 [compare - creates list of opponents without confu]
90 [store this to target mask]
12 2070
02 2070 [loads the list of enemies from the target mask]
00 4002 [sleep]
80
60 00
40
90 [so, takes the list from the target mask, then sees the sleep status of these targets, and returns to the target mask only party members in neither confu nor sleep]
12 2070
02 2070 [again]
82
90 [and a random target from that list]

Ta-da!
Title: Re: Enemy AI main script template now anyone can make there own battles
Post by: Bosola on 2010-02-02 03:50:57
Edit: This is a chunky and unnecessarily complicated script. I've no idea what I was trying to do when designing it, when I could just have a healer's main script look for allies with a certain ID whose HP was less than their MaxHP. I must have been trying to achieve something else, for some reason or other, but I'm not sure what.

I've been posting consecutively; I hope no-one thinks of this as spamming. However, there has been a fair bit of interest in these topics, so I'm going to post another template, one that I've disassembled but not yet tried out.

It's similar to the ideas above, where one creature heals its friends. In this case, though, we're looking to restore HP rather than remove debuffs.

So, the monster that's going to be healed needs the counter-script:

12 0020

02 2060
03 4160
80
02 2060
03 4180
80
41

90

[This stores a non-zero at LocalVar0020 if MaxHP != CurrentHP]

It also needs a death-script that clears the value of 0020, so that our 'healer' won't target dead allies

12 0020
60 0
90


The friend needs to be able to read this state, then act accordingly;

02 0020
70 [address of the creature's other move]
12 2070
02 2080
02 4120
80
60 [ID of peer]
40
90
60 20
60 [atk number]
92
12 0020
60 0
90
72 [send to end of script]

This 'main' template can be used as a part of a larger AI structure. It could be followed by a simple attack instruction (choose random enemy, hit with a stick) or it could be part of something far more complicated, like

Create random number
If odd,
 - see if an enemy needs help, use atk1 if so, or go to next section if not
If even,
 - use atk2
End script.

I'm going to test this out with MPs and guard hounds, seeing if I can get the MPs to heal their dogs half the time when injured.