Mod:Creation Kit/States (Papyrus)

=Overview= Scripts operate in varying states - but a script can only be in one state at a time. What code is run when a function is called or an event occurs depends on the state the script is in.

=Defining States= Defining a state in a script is very simple, just set up a state block as follows: state MyState ; Various functions here endState

If you want your script to start in a particular state, put "auto" in front of the state block: auto state StartHere ; Functions here endState

The 'Empty State'
The 'empty state' is the implicit state that every function not inside a state block is in. function MyFunction ; This function is in the 'empty state' endFunction

state MyState function MyFunction ; This function is in the MyState state endFunction endState

=Defining Functions/Events Inside States= To define a function or event inside a state, simply put the function or event inside the state block. state MyState function SpecialFunction ; This function is in the MyState state endFunction endState

Please note: the function or event must be defined in the 'empty state' with the same return type and the same parameter types or the compiler will complain at you. This is because of the way the game resolves function and event calls at run-time. function SampleFunction(int myParameter) ; Code here endFunction

state SampleState function SampleFunction(int aParameter) ; This function is fine - the type of parameter is the same as in the 'empty state' endFunction

function OtherFunction ; Error! OtherFunction does not exist in the 'empty state' endFunction endState

state OtherState int function SampleFunction(int myParameter) ; Error! SampleFunction return type doesn't match the one in the 'empty state' endFunction endState

state YetAnotherState function SampleFunction(int myParameter, float parameter2) ; Error! SampleFunction parameters don't match the one in the 'empty state' endFunction endState

For your convenience, events that various kinds of scripts can receive are already defined for you in the empty state inside the scripts that you extend (i.e. ObjectReference, Actor, etc). So you do not have to define events in the empty state when all you want to do is modify the event in one particular case.

=How Functions Are Picked= When someone calls a function, or the object receives an event, the one that is picked is resolved as follows:
 * 1) If the script has the function in its current state, call that one
 * 2) If the script extends another script that has the function in its current state, call that one
 * 3) If the script has the function in the 'empty state', call that one
 * 4) If the script extends another script that has the function in the 'empty state', call that one

In short, functions inside states override functions inside the 'empty state', and functions inside derived scripts override the ones in the scripts they extend.

=How to Set a Script's State= Setting a script's state is simple, just call GotoState(string asStateName) with the name of the state you want it to go to as a string. The following sequence of events will then take place:
 * 1) Mod:Creation Kit/OnEndState is sent to the script for the state that they were in before.
 * 2) The state is switched to the requested state
 * 3) Mod:Creation Kit/OnBeginState is sent to the script for the state it just entered.

The name of the state to enter is not case-sensitive. If you want to go to the 'empty state', simply pass in the empty string: "".

Also - calling GotoState will not end your function - it will still continue to run, so it's perfectly acceptable to just change your object's state during a certain piece of code and to change it back when you're done.

function MyFunction GotoState("TurnOffActivate") ; Do a bunch of long-running stuff GotoState("") endFunction

state TurnOffActivate event OnActivate(ObjectReference akTriggerRef) ; Do nothing endEvent endState

=How to Get a Script's State= Getting a script's state is also simple, call GetState which will return the state name, as a string, that the script is currently in.

=Tricks and Tips= Function GotoNumberedState(int number) GotoState("State" + number) EndFunction
 * If you want to disable an event or function when you are in a particular state, simply define that event or function in the state and leave it empty. This is especially useful if you want to, say, ignore any activate events while you are in the middle of doing something.
 * If only having one state at a time is a bit too restricting for you, remember that you can always attach multiple scripts to the same object - and each script can have its own, seperate state! This may help you with more complex operations. Of course you can also fall back on just using if statements as well with your own state variables.
 * Because state names are simply strings, you can construct them at run-time using the "+" operator. For example, you can define "State1", "State2", and so on, and pick them via the following:

=Conclusion= States are a powerful and reasonably easy way to modify the behavior of your script without having to write overly large if statements or other complicated code. Sometimes problems that seem overly complex or troublesome may become easier with creative use of states!