Variables and Properties
DescriptionEdit
Variables and Properties are similar things, they both "hold" values and objects. A variable is "private" meaning that only that script is aware of them, can set their contents, and get their contents. A Property is essentially a variable that other scripts can access, their contents can be set and get by a other scripts.
If a variable or property holds a numeric value, like an integer, get/set returns it's value. If a variable or property holds an object, you can access that object's properties and functions. (This is analogous to a reference variable from the old scripting system.)
Declaring VariablesEdit
float myFloat
float myOtherFloat = 13.5
MyFloat starts at 0, myOtherFloat starts at 13.5 and can be set by scripting in it's own script, but nothing else.
float myConstValue = 1.0 const
MyConstValue starts at 1.0 and cannot be changed at any time. This is useful for anything you don't want to accidentally change, like timer ID numbers, or perhaps for values that your script doesn't change but you may want to tweak later for balancing purposes (especially because const variables are ignored in save games!)
Declaring PropertiesEdit
Full PropertyEdit
To define a property, you first write the type, then "property", then the name of the property. You then define two functions, a get which returns the property's value, and a set which takes a new value for the property. And then you cap it off with "EndProperty"
Example:
int myInt_Var = 0 ; Where the property's value is stored
int property myInt
int function get()
return myInt_Var
endFunction
function set(int value)
myInt_Var = value
endFunction
endProperty
If you leave out the get function, the property is write-only - other people can set the value, but no one can read it. The local script, of course, can read the actual variable the property works with. If you leave out the set function, the property is read-only - other people can see the value, but they can't change it. Again, the local script can always set the variable that the property returns.
You don't have to just have the property return and set the value like you see above - the functions could do anything. You could, for example, put some if statements in the set function to ensure that the value is never outside a certain range. Or you could even play an animation when a certain value is set. Heck, you don't have to actually have a variable at all - it could be a calculated value or a constant.
Example:
bool property Locked
bool function get()
return IsLocked()
endFunction
function set(bool value)
Lock(value)
endFunction
endProperty
The above property hides the Lock and IsLocked ObjectReference functions so that you can lock and unlock this object by simply setting Locked to true or false.
Example:
int myVar = 5
int property ReadOnly
int function get()
return myVar
endFunction
endProperty
The above property is read-only. Scripts outside of this one cannot change the value, but the script itself can change the value of the variable.
Example:
int myVar = 5
int property WriteOnly
function set(int value)
if value >= 0
myVar = value
else
myVar = 0
endIf
endFunction
endProperty
The above property is write-only. Scripts outside of this one cannot read the value. It also uses an if to make sure the value is never below 0.
Auto PropertiesEdit
An auto property is one that writes the above get and set functions for you, behind the scenes. There are also some minor optimizations in the VM that speed up auto properties slightly. To make an auto property, simply omit the functions and endProperty and add "auto" to the end of the property definition. You can set the property's initial value using the "= <value>" syntax.
Example:
int property myInt = 5 auto
Const PropertiesEdit
A const property is similar to an auto read-only property in that its value cannot be changed by the script, and the value is ignored in save games. However, unlike auto read-only properties the value of the property can be changed by the editor in a plugin, making it ideal for times when you want to point at a form or other object that the script will not be changing. Simply adding "const" on the end of the definition will make the property const.
Example:
int property myConstProperty auto const
Mandatory PropertiesEdit
A mandatory property is one where the editor will complain with a warning (or with a dialog box if you're editing the properties) if the property is not filled. You'll want to set a property as mandatory if leaving it empty does not make sense for your script (for example, you want to point at a quest in the game). Note that the mandatory flag can be mixed and matched with the const and conditional flags.
Example:
quest Property MQ101 auto const mandatory
Auto Read-only PropertiesEdit
An auto read-only property is an auto property that can never have its value changed. This can be convenient if certain numbers mean different things in your script and you want to use a name instead of a number to represent it. You specify these by using "AutoReadOnly" instead of "Auto". These properties must have their initial value set using "= <value>" syntax.
Example:
int property myReadOnlyInt = 20 autoReadOnly
Conditional PropertiesEdit
Properties cannot be declared as conditional. Auto properties can be defined as conditional because what they actually do is define the hidden variable they create as conditional. This is why you see mangled auto property names when you select a Papyrus variable in the condition system - you're selecting from a list of hidden variables.
Example:
int property myVar auto conditional
Grouping PropertiesEdit
You can group properties together in the order you want using groups. Groups remember the order they were in in the script source code, and the order the properties were in inside them as well. (Any properties outside of groups do not remember their order) They will be shown grouped together in the editor, and in the game (via SV or SQV console commands). You can give groups documentation strings, which are visible in the editor under the group name, and flags to control whether they start collapsed or not (CollapsedOnRef, CollapsedOnBase, Collapsed).
Groups that have the same name in a child script as in a parent script will be merged together. By default, parent groups and properties sort before child groups and properties. (This order can be changed by the user in the editor, as well as having the option to turn off group view entirely)
Defining a group is simple, just surround the properties you want grouped with "Group <name> ... EndGroup". Putting properties in a group does not affect the script in any way, it is purely a visual/organizational option.
Example:
int Property UngroupedProperty auto
Group MyFirstGroup
{My first group, contains commonly used properties, so it's at the top and not collapsed}
int Property AProperty auto
int Property ZProperty auto
int Property BProperty auto
EndGroup
Group ASecondGroup CollapsedOnRef
{This group has properties that are set on the base object most of the time, so collapse it on the reference}
int Property CoolProperty auto
EndGroup
Order and grouping in the game and editor:
MyFirstGroup AProperty ZProperty BProperty ASecondGroup CoolProperty Ungrouped Properties UngroupedProperty
Getting Properties of a quest scriptEdit
From result script owned by the same questEdit
Often you will need to get a property of a quest script, and use it in a result script somewhere else. This is one of the more tricky things, but once you understand what's happening, it makes sense. First look at the example, then we'll describe what's happening.
;I have a quest script with this in it:
scriptName MQ01Script extends Quest
int property deadCount auto
;I have a result script (OWNED by MQ01) with this in it:
MQ01Script myQuest ;declares a variable "myQuest" which is a TYPE of MQ01Script
myQuest = GetOwningQuest() as MQ01Script ;sets the myQuest variable to it's owning quest as the type MQ01Script
float myDeadCount ;declaring the variable "myDeadCount"
myDeadCount = myQuest.deadCount ;setting local variable to be the quest's property value
;you can also set the quest property thusly:
myQuest.deadCount = 10
What's happening here is that we have a property "deadCount" that is in the script "MQ01Script" attached to MQ01. We also have a script that is owned by MQ01 (could be dialogue result, package result, or script attached to an alias).
In the result script, we create a variable that represents the quest script that has the property we want (in this case MQ01Script's "DeadCount" property). Note our variable myQuest is declare as MQ01Script. This is because when we made our quest script "scriptName MQ01Script extends Quest" we've essentially created a new type of object... a MQ01Script object. GetOwningQuest returns a quest object (before we extended it). So we also need to cast the quest returned by GetOwningQuest AS that new object "myQuest = GetOwningQuest() as MQ01Script" so we have access to it's extended properties. If we didn't cast it as a MQ01Script it would only have the functions and properties of a Quest object, which wouldn't contain our deadCount property.
In otherwords, when we created MQ01Script which extended the Quest script, unless we cast the object returned by GetOwningQuest AS our new script, it won't have our new properties declared in our new script.
With kmyQuestEdit
If the fragment you are using has a "kmyquest" drop down, you can select a script attached to the quest owning that fragment, and then use the kmyQuest "magic variable" to refer to quest script without casting it.
the above would be simplified to just:
float myDeadCount
myDeadCount = kmyQuest.deadCount ;getting property
kmyQuest.deadCount = 5 ;setting property
From a non-owned fragment / other quest scriptEdit
Need to write this... basic gist: You need to define a property in your script (and set it through the editor interface to be the other quest whose properties you want access to), then you can access that property's properties. In other words, your script has a property of the other quest; then you access that property's properties.
WarningsEdit
Be careful with variables and auto properties on scripts that are extended by other scripts - especially where some script somewhere else may have a property pointing to the base script, or trying to cast to the base script. This is because it would be possible to have two copies of a script attached to the same object, thereby creating two copies of the variable/auto property - and the other scripts that refer to the base script may randomly pick which one to talk to.
Native scripts may not have any variables or auto properties.