States (Papyrus)

From the CreationKit Wiki
Jump to navigation Jump to search

Overview[edit | edit source]

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[edit | edit source]

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'[edit | edit source]

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[edit | edit source]

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[edit | edit source]

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.

For example:

Scriptname ParentScript extends Quest 

Function MySuperCoolFunction()
  Debug.Notification("HELLO WORLD!")
endFunction 

State StateThree
  Function MySuperCoolFunction()
    Debug.Notification("I saw a mudcrab the other day...")
  endFunction 
endState


Scriptname ChildScript extends ParentScript

State StateOne
  Function MySuperCoolFunction()
    Debug.Notification("STOP RIGHT THERE CRIMINAL SCUM!")
  endFunction 
endState 

State StateTwo
  Function MySuperCoolFunction()
    ;do nothing 
  endFunction 
endState 

State StateThree
  ;MySuperCoolFunction() not defined
endState 
;
State StateFour
  ;MySuperCoolFunction() not defined
endState

If we call MySuperCoolFunction() on ChildScript, here are the results of what notification is printed in the four defined states.

  • StateOne - "STOP RIGHT THERE CRIMINAL SCUM!" - Priority 1.
  • StateTwo - Nothing happens - Also Priority 1.
  • StateThree - "I saw a mudcrab the other day..." - Priority 2
  • StateFour - "HELLO WORLD!" - Priority 4 - If we had a function defined in our empty state of ChildScript, then that function will run instead per Priority 3.

How to Set a Script's State[edit | edit source]

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. OnEndState is sent to the script for the state that they were in before.
  2. The state is switched to the requested state
  3. 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[edit | edit source]

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. Please note that the case of the state name depends on the call that set that state in the first place, and that string compares are case-sensitive, so be careful when doing compares. (In the future we may have ways to compare strings that ignores case to make this easier - for now, be careful)

Tricks and Tips[edit | edit source]

  • 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, separate 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:
Function GotoNumberedState(int number)
  GotoState("State" + number)
EndFunction

Conclusion[edit | edit source]

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!


Language: [[::States (Papyrus)|English]]  • [[::States (Papyrus)/fr|français]]