Tutorial #1: state and flow control
In FPSC script, which I'll just call script from now on, there is a value called state. While it is listed as a "variable" in the manual it is only useful for managing flow control and acts as a true only test condition for executing the current line of code. Something like:
IF state = 1 THEN do whatever you want
Script DOES NOT need to use state as the condition for a line of code. It is very common to see scripts start with a state comparison, but is by no means mandatory.
For example, if I wanted a line of code to execute no matter what I could use the "always" condition like so:
;Tell the NPC to run every time no questions asked
:always:animate=5
I could also use one or more of the built in functions to do a condition test that would execute the line without needing the state value. For example, I could tell the AI for the NPC to lay down prone every time the divine presence of the player gets within 200 units in the following code:
;get down and worship me!
:PLRDISTWITHIN=50:animate=61
NOTE: When you have more than one in a condition they are "and'ed" together (as opposed to being "or'ed"). Example:
:PLRDISTWITHIN=80,PLRDISTFURTHER=50:rotatetoplr
In this statement we are saying:
IF PlayerDistance > 50 AND PlayerDistance <= 80 THEN ...
The point here being that the conditions are ANDed together to decide if the result is true.
So what good is the state variable then? It is very useful for creating execution forks in your script function. Here's how it works:
Each time the game engine loops it does a pass through the AI function for each active entity. When the engine enters the "function" it initializes the state value to 0. It doesn't matter what the value was when you exited from the last loop, when you enter again the value will be 0. Each line of code is processed in order and you cannot go backwards nor jump forwards. The lines execute one after another processing the "action" (or right) side of the line of code if the specified condition (left side) is true.
Setting the state value to a specific number and then checking for that value in later lines before executing allows you to use the state value for forking. For example, if I wanted to do the following:
IF PlayerDistance < 50
"punch the player"
ELSE IF PlayerDistance >= 50 AND PlayerDistance =< 80 THEN
"stop and look at the player"
ELSE IF PlayerDistance > 80 AND PlayerDistance <= 600
"run around"
ELSE
"lay down and sleep"
ENDIF
I could code it as follows (code without comments included below):
;this is the "else" condition. If none of the following lines match then
;the state value will still be 50 at the end so any lines where state=50 will
;execute as the else clause
:ALWAYS:state=50
;If the player is 81 to 600 units from the NPC then - state is 10
;NOTE: We could just execute the code we want right here and now, but if we move
;execution to the "state=#" lines then we can have nested logic within the matches
;as well.
:PLRDISTWITHIN=600,PLRDISTFURTHER=80:state=10
;If the player is 50 to 80 units away set the state to 20. Could be any number.
:PLRDISTWITHIN=80,PLRDISTFURTHER=49:state=20
;If the plyaer is within 49 units of the NPC then it's 30
:PLRDISTWITHIN=49:state=30
;run the true branch for "player between 81 to 600 units away"
:state=10:animate=5,pivotrandom=10
;NOTE: there could be multime state=10 lines for more functions to run or all of the
;required functions could be put on a single line separated by a ','
;run the true branch for "player between 50 to 80 units away"
:state=20:animate=1,rotatetoplr
;run the true branch for "player less than 50 units away"
:state=30:animate=7,plraddhealth=-1
;run the the "else" meaning that the player is not within the range
;of anything specified. In our case it is anything greater than 600 units, but
;it could be anything that is not specified inside the range.
:state=50:animate=61
Here's this code without all the comments.
:ALWAYS:state=50
:PLRDISTWITHIN=600,PLRDISTFURTHER=80:state=10
:PLRDISTWITHIN=80,PLRDISTFURTHER=49:state=20
:PLRDISTWITHIN=49:state=30
:state=10:animate=5,pivotrandom=10
:state=20:animate=1,rotatetoplr
:state=30:animate=7,plraddhealth=-1
:state=50:animate=61
At this point it might seem easier to just do the following:
:ALWAYS:state=50
:PLRDISTWITHIN=600,PLRDISTFURTHER=80:animate=5,pivotrandom=10,state=99
:PLRDISTWITHIN=80,PLRDISTFURTHER=49:animate=1,rotatetoplr,state=99
:PLRDISTWITHIN=49:animate=7,plraddhealth=-1,state=99
:state=50:animate=61
This would certainly work. However, there are a couple of advantages to doing it the first way specified above. It allows the code to be broken up somewhat making it a little easier to manage and expand. Also, if you decide that you need to nest another branch within a current branch this can by done using the first method.
For example, in the current AI script we want the NPC to lay down if the player is farther than 600 units away. This is specified using the "ELSE" section of our block. However, what if we wanted the PC to lay down and relax only if the player is 600+ units away AND the NPC has a health lower than 50, Otherwise you want the NPC to keep on roaming? We could do it like this:
:ALWAYS:state=50
:PLRDISTWITHIN=600,PLRDISTFURTHER=80:state=10
:PLRDISTWITHIN=80,PLRDISTFURTHER=49:state=20
:PLRDISTWITHIN=49:state=30
:state=10:animate=5,pivotrandom=10
:state=20:animate=1,rotatetoplr
:state=30:animate=7,plraddhealth=-1
:state=50,HEALTHLESS=50:state=51
:state=50:animate=5,pivotrandom=10
:state=51:animate=61
IF PlayerDistance < 50
"punch the player"
ELSE IF PlayerDistance >= 50 AND PlayerDistance =< 80 THEN
"stop and look at the player"
ELSE IF PlayerDistance > 80 AND PlayerDistance <= 600
"run around"
ELSE
IF Health < 50
"lay down and sleep"
ELSE
"run around"
ENDIF
ENDIF
I hope this helps. Any questions please let me know.
Thanx