Extending Scripts (Papyrus)

OverviewEdit

Script extending is the act of taking a script that almost does what you want, and then modifying some of its events or functions to do something different without editing the original script.

TerminologyEdit

Child
The script that is extending its parent.
Inherit
When a child script gains something from its parents without modifying it, it "inherits" these things.
Override
When a child script changes the behavior of something in its parent by making a new version, it "overrides" the parent's version.
Parent
The original script that is being extended.

How to extendEdit

Extending a script is easy: At the top of the script after "scriptname x", you add "extends y" where Y is the name of the script you want to extend.

Example:

Scriptname SpikeTrap extends Trap

In this case, the "SpikeTrap" script is extending the "Trap" script. You can kind of think of this as an "is a" relationship. A SpikeTrap "is a" Trap.

What an extended script can doEdit

Functions and EventsEdit

An child script has access to all the functions and events from its parent. Any function or event that you do not implement in the child script is inherited by the child. If a function or event is implemented in the parent, and you implement it in the child as well, then the child one overrides the parent, and any calls to that function or event will use the child's version instead. Note that when you implement the function or event in the child you must match the parameters and the return type, along with the name. If you don't the script will not compile.

Example:

Scriptname ParentScript

Function MyFunction()
  Debug.Trace("Parent MyFunction!")
EndFunction

Function MyOtherFunction()
  Debug.Trace("Parent MyOtherFunction!")
EndFunction


Scriptname ChildScript extends ParentScript

Function MyFunction()
  Debug.Trace("Child MyFunction!")
EndFunction
  • Attaching the child script to something and then calling MyFunction on it:
Child MyFunction!

The child here is overriding and replacing the parent's function with its own.

  • Attaching the parent script to something and then calling MyFunction on it:
Parent MyFunction!

The child script doesn't exist in this case, so the old parent functionality stays intact.

  • Attaching either script to something and then calling MyOtherFunction on it:
Parent MyOtherFunction!

The child doesn't override this function, so the original parent function is used.

Calling a function on your parentEdit

If you want to actually call a function on your parent script and ignore the function in your own script, you can use the special "parent" variable. Note that this variable only works on yourself - you cannot use it to call something on a different script. This is most commonly used if you want your script to only make a minor tweak to something before letting the parent do all the heavy lifting.

Example:

Scriptname ChildScript extends ParentScript

Function MyFunction()
  Debug.Trace("Child MyFunction!")
  parent.MyFunction()
EndFunction
  • Calling MyFunction on this child script attached to an object:
Child MyFunction!
Parent MyFunction!

Note that the parent variable forced the parent version of the function to be called, rather then calling the child version.

StatesEdit

States in a child script are merged with states from the parent script. Functions in the same state in the child will override functions in the parent - and all states will be merged.

  • Parent has state, child does not: Parent functions in that state will be used.
  • Child has state, parent does not: Child functions in that state will be used.
  • Parent has function in state, child does not: The parent version will be used.
  • Child has function in state, parent does not: The child version will be used.
  • Both have function in the same state: The child version will be used.

PropertiesEdit

Properties in a parent script behave just as if they were properties on the child script. However, you cannot override them in the child to do something different. If you attempt to do so the compiler will complain.

Example:

Scriptname ParentScript

int Property MyProperty Auto


Scriptname ChildScript extends ParentScript

Function MyFunction()
  Debug.Trace("MyProperty = " + MyProperty)
EndFunction

Note that MyProperty is used in the child script as if it existed there, even though it exists in the parent.

Property bugsEdit

  If the child includes new functions, and both the parent and child are listed as scripts in a Quest, the parent will not be able to access its own filled properties.

VariablesEdit

Variables are private and can not be seen by any other script, including your parent or child scripts. Having a variable with the same name as one in your parent script will completely hide the parent variable when accessed in a child function. The function on the parent will use its own variable.

Example:

Scriptname ParentScript

int MyVar = 1

Function MyFunction()
  Debug.Trace("Parent MyVar = " + MyVar)
EndFunction


Scriptname ChildScript extends ParentScript

string MyVar = "Hello World!"

Function MyFunction()
  Debug.Trace("Child MyVar = " + MyVar)
  parent.MyFunction()
EndFunction
  • Calling MyFunction on a child script attached to an object:
Child MyVar = Hello World!
Parent MyVar = 1

Note that both scripts can only see their local copy of the variable.


A note on casting objects as their parent typesEdit

Lets say I've got a script Horse and I've got a script which extends Horse called WarHorse. In general, a horse would probably run away when seeing a wolf, but a warhorse may attack it instead:

Scriptname Horse extends Actor

Event OnSeeWolf()
  Debug.Notification("Horse runs away.")
EndEvent
Scriptname WarHorse extends Horse

Event OnSeeWolf()
  Debug.Notification("Horse attacks wolf.")
EndEvent

I have an actor MeanWarhorse with the script WarHorse attached. Consider the following block of script:

(MeanWarhorse as Horse).OnSeeWolf()

When I call this which message am I going to see, "Horse runs away" or "Horse attacks wolf"?:

It turns out that even if you have cast an object to a parent type (Horse), the object will still behave according to the script attached to it in the CK. Thus in this case, the message you would see is "Horse attacks wolf.". This is a very useful thing to know if you need to send messages to an object, but don't know exactly what type of object it is.

Lets say I've got another type of Horse called a CartHorse. A carthorse can't run away because it's attached to a cart, so it just stands there looking twitchy.

Scriptname CartHorse extends Horse

Event OnSeeWolf()
  Debug.Notification("Horse gets twitchy.")
EndEvent

I have a cloak style enchantment effect on the wolf which does the following:

Scriptname WolfFearEffect extends ActiveMagicEffect

Event OnEffectStart(Actor akTarget, Actor akCaster)
  (akTarget as Horse).OnSeeWolf()
EndEvent

With this setup, when a warhorse comes under the wolf's fear effect, it will attack the wolf. When a carthorse is affected, it will stand there getting twitchy, and when an other kind of horse comes under affect, it will run away.

Note: If in this example I had both WarHorse and CartHorse attached to the same actor, it would be treated as one or the other picking one type seemingly at random.


Common Functions and EventsEdit

The functions and events listed here are on all Papyrus scripts, and do not require you to extend another one.

Member FunctionsEdit

  • Function GotoState(string asNewState)
    • Changes this script state to the passed-in state
  • string Function GetState()
    • Returns the state the script is currently in

EventsEdit

  • Event OnBeginState()
    • Called when a state has just been switched to
  • Event OnEndState()
    • Called when a state is about to be switched away from



Language: English  • français