I noticed that there isn't any AI coding tutorial here except for Winane's AI method but it does not show how to code moves for the cpu to use, So i thought to give it a shot, I'll be explaining the "Cpu only commands" activation method not Winane's method, It takes just few seconds to kick in, I know that Winane's method is better but Cpu only commands is easier to learn and much more simple.
Before i proceed, I feel obligated to say that if it wasn't for Liger i wouldn't type this tutorial, He tought me every thing i know, Ok now lets begin...
First off, Open the CMD file of the character and add lines like these to declare the AI only commands and above any thing else in the CMD file:
;-| AI |------------------------------------------------------
[Command]
name = "CPU1" <------------------------(AI Command Name)
command = U, D, F, U, D, F <------------(Command combination)
time = 0
[Command]
name = "CPU2"
command = U, B, F, U, D, F
time = 0
[Command]
name = "CPU3"
command = U, D, D, U, D, F
time = 0
[Command]
name = "CPU4"
command = U, F, U, B, U, D, F
time = 0
[Command]
name = "CPU5"
command = B, B, B, U, B, U, D, F
time = 0
[Command]
name = "CPU6"
command = U, D, B, U, B, U, D, F
time = 0
[Command]
name = "CPU7"
command = F, F, B, U, B, U, D, F
time = 0
[Command]
name = "CPU8"
command = U, D, U, U, B, U, D, F
time = 0
[Command]
name = "CPU9"
command = F, B, B, U, B, U, D, F
time = 0
[Command]
name = "CPU10"
command = F, F, B, B, U, B, U, D, F
time = 0
You can add as much more as you like but don't exceed 60 cause the CMD only supports 128 unique commands per character including human commands, And you will not need that much any way, I only use 40 or even less, Also don't use same command combination, Each cpu command must have a unique combination and it doesn't matter what the combination is as you see above it's just some random combinations.
It's time to announce to the CPU that it has control over the character, You can add the activation code in the CMD file under [Statedef -1] preferbly at the end of the CMD, Or you can program the AI in one of the CNS files of the character under [Statedef -2] or under [Statedef -3] but those statedefs must be declared, in some characters -2 or -3 are not declared so you have to declare them yourself by typing [Statedef -2] or [Statedef -3] and then add the activation code under them, in the CMD [Statedef -1] is always declared by the creator so no need to declare it again but if you separate the AI from the character files in a separate file, You're gonna have to declare it again.
Activation Code:
; AI switch -> ON
[State -1, Activate AI]
type = Varset
triggerall = var(59) != 1
trigger1 = command = "CPU1"
trigger2 = command = "CPU2"
trigger3 = command = "CPU3"
trigger4 = command = "CPU4"
trigger5 = command = "CPU5"
trigger6 = command = "CPU6"
trigger7 = command = "CPU7"
trigger8 = command = "CPU8"
trigger9 = command = "CPU9"
trigger10 = command = "CPU10"
v = 59
value = 1
This is a variable set telling the computer that when Var(59) is = 1 then do this and do that, But you must make sure that the variable 59 is available and not used in any thing else in the character coding or else use another variable, Also you must copy and past this line "triggerall = var(59) != 1"in each and every human command in the CMD file to differ between the commands you use to play with the character and the commands the CPU use against you, You'll find in the character CMD file human only commands look like this:
[State -1,FLK]
type = ChangeState
value = 310
triggerall = command != "holddown"
triggerall = statetype = S && ctrl
trigger1 = command = "a" && command = "holdfwd"
trigger2 = command = "a" && command = "holdback"
Add the AI cancelation code to it like this:
[State -1,FLK]
type = ChangeState
value = 310
triggerall = var(59) != 1 <---------------- This cancels the AI usage of the human commands
triggerall = command != "holddown"
triggerall = statetype = S && ctrl
trigger1 = command = "a" && command = "holdfwd"
trigger2 = command = "a" && command = "holdback"
It's a lot of copy and paste but that way you'll cancel any interferance from the default Mugen AI, Now the character is like a blank page for you to write what you see fit, But before we venture into AI programing, Open the CNS file and in the [Data] paragraph change the number of the IntPersistIndex to = 58, if it's already = 58 then keep it and if this line is not typed, Type it yourself "IntPersistIndex = 58" don't ask why cause i realy don't know, it just activates the AI faster i think.
Edit: This means if you activated the AI in round one, it'll still be active in round 2+. (Thanks Cyanide)
Edit: Please note that the programing of the AI itself i'm about to explain doesn't matter if you use this activation method or Winane's method, It's the way AI is programed in both.
Now you start programing the AI, Each cpu command must have [state -1] declaration or -2 or -3 depending on the state you're programming the AI in, For example:
[State -1, AI] ; punch
type = ChangeState
value = 200 <----------------------- State no. of the animation you want the CPU to perform
triggerall = roundstate = 2 <--------- Trigger during the fighting phase of the round "Round One...Fight"
triggerall = var(59) = 1 <------------ AI variable that must be used in every CPU command
trigger1 = ctrl = 1 <----------------- Character can be controled (Not performing a move)
Now you told the CPU that when Var(59) equals 1 and you have control over the character then perform a change state, In this case the state wanted is a weak punch(value = 200), There are 2 ways to know the state no. of the character moves while coding the AI, First one is choosing the character in training mode and turn on debug mode by pressing ctrl+D, Performing every move and the debug mode will tell you the state no. of the moves, The second way is the easiest just open the character in fighter factory and press the animation tab, you'll find the begin action no. bar displays every move state no.
But the code above is very basic and will make the character keep punching continuasly like an idiot, So you must add more triggers to make the character punch only in a certain situation like this:
[State -1, AI] ; punch
type = ChangeState
value = 200 <----------------------- State no. of the animation you want the CPU to perform
triggerall = roundstate = 2 <--------- Trigger during the fighting phase of the round "Round One...Fight"
triggerall = var(59) = 1 <------------ AI variable that must be used in every CPU command
triggerall = statetype != A <--------- Trigger when the char is not in air
triggerall = random < 500 <---------- Trigger the move in 50% of the times the conditions is met (i'll explain more)
triggerall = p2statetype != L <------- Trigger when opponent is not lying on the floor
triggerall = p2bodydist x = [0,40] <--- Trigger when the opponent is between 0 to 40 pixels away horizontaly
triggerall = p2statetype != A <------- Trigger when the opponent is not in air
trigger1 = ctrl = 1 <----------------- Character can be controled (Not performing a move)
Now you told the CPU that when Var(59) is equal 1, The char is not jumping, Opponent is not jumping, Opponent is close, Opponent is not lying down and char is controlable then perform a weak punch, Now the code is much smarter.
The differance between triggerall and trigger1 is:
Triggerall is a condition that must be met for the whole changestate to be performed but trigger1 is a condition that triggers one part of the changestate, You can have trigger2, trigger3 or more like this:
[State -1, AI] ; punch
type = ChangeState
value = 200 <----------------------- State no. of the animation you want the CPU to perform
triggerall = roundstate = 2 <--------- Trigger during the fighting phase of the round "Round One...Fight"
triggerall = var(59) = 1 <--------------- AI variable that must be used in every CPU command
triggerall = statetype != A <------------ Trigger when the char is not in air
triggerall = random < 500 <------------- Trigger the move in 50% of the times the conditions is met (i'll explain more)
trigger1 = p2statetype != L <----------- Trigger when opponent is not lying on the floor
trigger1 = p2bodydist x = [0,40] <------- Trigger when the opponent is between 0 to 40 pixels away horizontaly
trigger1 = p2statetype != A <----------- Trigger when the opponent is not in air
trigger1 = ctrl = 1 <-------------------- Character can be controled (Not performing a move)
trigger2 = stateno = 210 && movehit<--- Trigger it in a combo when in stateno. 210 and the move hit the opponent
trigger3 = stateno = 400 && animtime = 0<--chain it to stateno. 400 and the animation is over
You can see that trigger1 is a different condition than trigger2 and trigger3, Three different conditions that the changestate can be performed in but all must meet the triggerall condition to be performed.
Now how to chain moves to each other, as you can see in the code above there is "trigger2 = stateno = 210 && movehit" means that trigger the weak punch after the low punch (210) is performed and did hit the opponent, There is also "movecontact" meaning that the move made contact whether it's a hit or got blocked.
Ok it's past midnight here and i have to sleep to go to work cause i look like this :beadyeyes2: so i'll continue tommorow and add a list of triggers and thier discreption to complete the tutorial and start answering your questions and/or curses.
Please do explain more about the intpersistindex Cyanide, If they use Var(30) what's the no. to use in the intpersistindex? Would it be 30 as well or 29? I don't realy understand it.
I must continue explaining how chain combos work cause i explained it in a rush, We used the weak punch in our first AI move and we want to chain a weak kick to it, Then we use the weak punch as a trigger for the next move like this:
[State -1, AI] ;weak kick
type = ChangeState
value = 210
triggerall = roundstate = 2
triggerall = var(59) = 1
triggerall = statetype != A
triggerall = p2bodydist x < 45 <---------------- Opponent is still close after recieving the punch
triggerall = p2statetype != L
trigger1 = stateno = 200 && movecontact <---- Trigger when in state no.200 and the move made contact
So the AI will always use the weak kick(state 210) after the weak punch(state 200), if you plan to program different moves after the weak punch then you must add a random trigger for both AIs like this:
[State -1, AI] ;weak kick
type = ChangeState
value = 210
triggerall = roundstate = 2
triggerall = var(59) = 1
triggerall = statetype != A
triggerall = p2bodydist x < 45
triggerall = random < 200 <------------------- lower nomber cause the AI will check this first
triggerall = p2statetype != L
trigger1 = stateno = 400 && movecontact
[State -1, AI] ;strong punch
type = ChangeState
value = 220
triggerall = roundstate = 2
triggerall = var(59) = 1
triggerall = statetype != A
triggerall = p2bodydist x < 45
triggerall = random < 300 <------------------- slightly higher number
triggerall = p2statetype != L
trigger1 = stateno = 400 && movecontact
Keep in mind that the AI checks the CPU commands In order from top to bottom, So in the code above the AI will check the weak kick first then checks the strong punch, That's why if you want a 50% between them use a higher number for the random trigger in the second one(Trial and error for best results), Also if you want to chain special or super moves you better use "movehit" to ensure that the opponent is open for that kind of move cause using only "movecontact" may result in your character being open after the opponent blocked the first move (but it's your own preferance) Also if you want to code super or dream cancels you have to use "animtime" like this:
[state -1, AI] ;Haoshikoken Weak
type = changestate
value = 3000
triggerall = roundstate = 2
triggerall = var(59) = 1
triggerall = statetype != A
triggerall = power >= 1000 <---------------------------------Trigger when char have one power stock
triggerall = movetype != H <---------------------------------You can't attack when you are being hit
trigger1 = stateno = 1400 && movehit && animtime = 0<------Trigger when in state 1400 & the move hit the opponent & the move just ended.
[state -1, AI] ;Reyuko ranbu
type = changestate
value = 2100
triggerall = var(59) = 1
triggerall = statetype != A
triggerall = power >= 1000
trigger1 = stateno = 1300 && movehit && animtime = -25 <----Trigger when in state 1300 & the move hit the opponent & there are 25 more ticks left in the animation.
Animetime is used for certain situations were you can super or dream cancel in, so you have to check those first in your character, but "animetime" could be used for normal and special moves too, also if you plan to make AI that is not cheap you might want to use the same triggers the Human only commands use to make sure the AI is like a very smart and quick human fighting you and not doing impossible moves like uppercuting while being hit or something like that(but this comes with experiance anyway)
As for the list of the most common triggers used in AI coding:
triggerall = roundstate = 2 <--------- Trigger during the fighting phase of the round "Round One...Fight" as you don't want your character to attack the opponent before the round start(or do you?)
triggerall = statetype = A <------------- Trigger when your char is in air(any trigger can have it's opposet by typing "!" next to the equal like the next one)
triggerall = statetype != A <-------------- Trigger when your char is not in air
triggerall = statetype = S <--------------- Trigger when your char is standing
triggerall = statetype = C <--------------- Trigger when your char is crouching
triggerall = statetype != L <--------------- Trigger when your char is not on the floor
triggerall = p2statetype = A <------------- Trigger when your opponent is in air(uppercuts dear friend)
triggerall = p2statetype != A <------------ Trigger when your opponent is not in air(this includes rolling back & forth)
triggerall = p2statetype != L <------------ Trigger when your opponent is not on the floor
triggerall = p2statetype = S <------------ Trigger when your opponent is standing(mainly for throwing ability)
triggerall = p2statetype = C <------------ Trigger when your opponent is crouching
triggerall = movetype != H <-------------- Trigger when your char is not being hit(anti cheap of course)
triggerall = movetype = A <--------------- Trigger when your char is in an attack state
triggerall = p2movetype = A <------------- Trigger when your opponent is attacking(good for counters)
triggerall = p2movetype != A <------------- Trigger when your opponent is not attacking
triggerall = p2movetype = H <------------- Trigger when your opponent is being hit(good for combos)
triggerall = p2bodydist x >= 150 <--------- Trigger when your opponent is more than 150 pixels away horizontaly(far)
triggerall = p2bodydist x <= 30 <---------- Trigger when opponent is less than 30 pixels away horizontaly(close)
triggerall = p2bodydist x = [30,80] <------- Trigger when opponent is between 30 to 80 pixels horizontaly
triggerall = p2bodydist x >= 0 <------------ Trigger when opponent is in front of your character
triggerall = p2bodydist y > 60 <------------ Trigger when opponent is 60 pixels under you(good for aerial attacks)
triggerall = p2bodydist y > -60 <------------ Trigger when opponent is more than 60 pixels above you
trigger1 = ctrl = 1 <------------------------ Trigger when your character can be controled
trigger1 = ctrl = 0 <------------------------ Trigger when your character cannot be controled(good for combos)
triggerall = random < 500 < ---------------- Randomize the move
trigger1 = stateno = 700 && movecontact<-- Trigger when in state 700 and move made contact whether hit or blocked
trigger1 = stateno = 700 && movehit <------ Trigger when in state 700 and the move hit
trigger1 = stateno = 700 && movehit && animtime = 0 <-same as above but trigger after the move animation ends
trigger1 = stateno = 700 && movehit && animtime = -25 <-same as above but there are 25 more ticks left in the animation
triggerall = power >= 1000 <----------------- Trigger when you have one or more than one power stock
triggerall = backedgedist < 5 <--------------- Trigger when your character is cornered
triggerall = life < 250 <---------------------- Trigger when you have less than 1/4 of your health
triggerall = p2life > 500 <-------------------- Trigger when opponent have more than 1/2 of his health
triggerall = enemy,NumProj = 0 <------------- Trigger when there is no projectile attack from opponent
triggerall = enemy,NumProj = 1 <------------- Trigger when opponent has fired a projectile
triggerall = p2stateno = 700 <--------------- Trigger when opponent is in state no.700(use wisely)
Ok now i guess i'm finished, So if any one have questions about any thing please go ahead, And if someone has any amendments please do tell me...
First, add this :
[State -1]
Type=Changestate
Triggerall=Ingurddist
Triggerall=var(59)>0
Triggerall=ctrl
Trigger1 = random< (var(50)*2+(AiLevel>=3)*30)
value=120
[State -1]
Type=Assertspecial
Triggerall=StateNo!=[120,160]
Trigger1=var(59)>0
flag=noairguard
flag2=nocrouchguard
flag3=nostandguard
(Where var(59) is the AI on/off variable and the random line decides how quickly/often it'll guard.)
Then add !inguarddist triggers to most attacks moves (except the fastest ones) to make sure your AI is not starting to use slower attacks while being attacked, giving the guarding more chance to trigger.
Additionally, you'll need to edit all your common guard states to make sure the AI is not stopping guarding too early.
My complete detailed AI guide (based on AIlevel, including guarding) is here : http://elecbyte.com/wiki/index.php/AI#Guarding (http://elecbyte.com/wiki/index.php/AI#Guarding)