-
Notifications
You must be signed in to change notification settings - Fork 18
Papyrus Types
Papyrus supports five kinds of literal values: boolean, integer, float, string, and none.
Boolean literals are simple, they are just true
or false
values.
<boolean> ::= 'true' | 'false'
bool myBoolean
If (myBoolean == true)
; do something
EndIf
Integer literals are sequences of digits (0 though 9) optionally prefixed by a minus sign.
If you want a hex number, prefix it with 0x
.
Valid hex digits are A through F (case-insensitive).
Integers are 32-bits in size, signed, which means their valid range is −2,147,483,648
to 2,147,483,647
.
<integer> ::= (['-'] <digit>+) | ('0x' (<digit>|<hex digit>)+)
int count
If (count < 5)
; do something
count +=1
EndIf
10
-15365
0x0001F2C8
Float literals are sequences of digits (0 through 9) optionally prefixed by a minus sign, and followed by a dot and another sequence of digits.
Floats are 32-bits in size, and have a range of 1.175494351 E – 38
to 3.402823466 E + 38
with 7 significant digits.
<float> ::= ['-'] <digit>+ '.' <digit>+
float myValue
If (myValue == 5.123)
; do something
EndIf
1.5234
-125412.0
String literals are simply text surrounded by double quotes. Newlines, line feeds, quotes, and back slashes are not allowed in the string. If you want one of these special characters, or a tab, then you can use the following escape codes below.
<string> ::= '"' <anything but another ", \, newline, or linefeed> '"'
Escape Code | |
---|---|
\n |
Newline |
\t |
Tab |
\\ |
Backslash |
\" |
Double quote |
string myText = "Hello World."
Debug.Trace(myText)
"Hello, World!"
"" ; Empty string
"\\\n" ; a string with a backslash followed by a new line
In practice, Papyrus strings are case-insensitive due to the way the game engine's string pool operates for efficiency reasons. Most strings in the engine come from this same case-insensitive string pool, with a few exceptions. The pool works by caching a unique occurrence of a string whenever one is encountered by the game engine for the first time. The pool will reuse the cached version of a string for any following occurrences. This means the distinction between uppercase and lowercase may not exist.
For example, the strings "MyString
" and "mYsTrInG
" are technically equivalent.
Which string is used depends on which version of the string is encountered by the game engine first.
The game's load order has an influence on this, among other things.
Following the example, if "mYsTrInG
" is cached first then any following occurrence of "mystring
" will always be "mYsTrInG
".
This is potentially a problem for some Game Systems which require a case-sensitive string.
In general the workaround to creating a string with any preferred casing would be to use a pseudo uniqueness. For example the string "raider" is cached but you want "Raider". You can use a pseudo uniqueness by prefixing or suffixing the string with a space character (" "). Sticking with the example, the user would be none the wiser if you used "Raider " for UI purposes. This is not a suitable workaround for all cases. For other cases you should make your strings more unique if possible.
- The game engine internally uses a data structure called
BSFixedString
for handling string data. - Localized strings, internally
BGSLocalizedString
, are also vulnerable to string pool limitations. - UI Script function calls to Scaleform are affected by this string casing limitation.
- Initialization File related function calls are affected by this string casing limitation.
The none
literal simply represents 'nothing' for object types.
This is similar to NULL in the C language.
If you want to know if an object variable contains a valid object, just compare it to none
.
none
If (myValue == none)
; The value is none.
EndIf
Every variable in Papyrus starts with a default value, based on its type. The default values for each type are listed below:
Type | Default Value |
---|---|
Bool | false |
Int | 0 |
Float | 0.0 |
String | "" |
Objects | none |
Arrays | none |
Structs | none |
Var | none |
A variable is storage for a single value in Papyrus. Depending on its type, it may be a value or a reference to a value. Variables may be defined in the Script, or inside a specific Function or block.
- In script:
<variable definition> ::= <type> <identifier> ['=' <constant>] (<flags>)*
- In struct:
<variable definition> ::= <type> <identifier> ['=' <constant>] (<flags>)* (<terminator> <docstring>)?
- In function:
<variable definition> ::= <type> <identifier> ['=' <expression>]
Variables are defined simply by writing the type, followed by the Identifier to use for the variable.
You may optionally follow the variable with an equals sign and an expression (in a function) or value (in a script) to initialize the variable with that value on the same line. If you do not give it an initial value, then it will start with a Default Value.
Variables in scripts do not have to be defined before use, but variables inside functions do. Variables defined in scripts may have Flags.
The identifier must be unique within the block it is defined.
In other words, if your script has a variable named "foo
", then none of the functions may have a local variable named "foo
" defined in them.
However, if one function has a variable named "foo
", then a different function in the same script can also have a "foo
" variable.
- Variables defined inside a block (like an "
If
" or "While
") are local to that block. - Non-constant variables may not be defined in native or constant scripts (though they can be defined inside functions in these scripts)
- Only variable definitions in structures may have documentation strings.
; Defines a variable named "foo" that is an integer
int foo
; Defines a variable named "bar" that starts with a value of 10.5 times x (must be in a function)
float bar = 10.5 * x
; Defines to variables, both named foo, that are distinct
if x
int foo = 10
else
float foo = 10.5
endIf
; Can't access foo out here!
foo = 10 ; fails to compile!
Variables that are of type int
, float
, bool
, or string
are referred to as "value variables".
These variables simply store a value, and when you assign them to each other, or pass them off to a function, they copy their value.
x = 5
y = x ; Copies the value of x and puts it into y
y = 6 ; Changes the value of y to 6, but doesn't touch x, which is still 5
Variables that are objects, arrays, or structures are referred to as "reference variables". These variables point at a value instead of storing it, so if you assign one variable to another, both point at the same value and changes made to one will reflect in the other.
x = MyObject
x.SetCoolValue(5) ; Sets the cool value of the object pointed to by x to 5
y = x ; Y now also points at MyObject
y.SetCoolValue(10) ; Sets the cool value of the object pointed to by both x and y to 10
a = x.GetCoolValue() ; Returns 10!
b = y.GetCoolValue() ; Also returns 10!
Variables of type "var" can hold any value, and remember the type of the object they are holding. You can't do anything with a var variable except ask it what type it is or cast it to a type.
var x = 1 ; x is now an int and contains 1
x = 1.5 ; x is now a float and contains 1.5
x = "Hello World" ; x is now a string and contains "Hello World"
x = akActivator ; x is now whatever akActivator is and contains it
if x is int
Debug.Trace("x is an integer: " + x as Int)
elseIf x is float
Debug.Trace("x is a float: " + x as Float)
endIf
A property is a pair of Functions (or, sometimes, only one function) that looks and behaves kind of like a Variable. You can read a value from it, or assign a new one, just like a variable, assuming that it's allowed. There are three kinds of property, the full property, the auto property, and the auto read-only property. All three of them appear the same to an external script. You can kind of think of them as public variables, but where the owning script can control how they are used.
<property> ::= <type> 'Property' <identifier> <flags>*
<function>
[<function>]
'endProperty'
The full property gives you full control over exactly what the property does and what it does or does not allow. The functions inside the property definition are normal functions.
The exception being that they must be either a "Get
" function or a "Set
" function
A "Get
" function returns the same type as the property and takes no parameters.
A "Set
" function returns nothing and takes a single parameter of the same type as the property.
You may omit either one of them, but at least one must exist.
When someone assigns a value to the property, it will call the set function, passing in the desired value. When someone tries to get the value a property holds, it will call the get function and take the result. If the set function does not exist, then no one can write to the property. If the get function does not exist, then no one can read from it.
If you want the property to show up in the editor, it should at least have a set function, and not have the hidden Flag.
int myValue = 0 ; Private, like all variables
int Property ValueProperty ; Publicly accessible, but won't let you set a value less then 0
Function Set(int newValue)
if newValue >= 0
myValue = newValue
endIf
EndFunction
int Function Get()
return myValue
EndFunction
EndProperty
int myValue = 0 ; Private
int Property ReadOnlyValue ; Publicly accessible, but this one won't let anyone set it
int Function Get()
return myValue
EndFunction
EndProperty
<auto property> ::= <type> 'Property' <identifier> ['=' <constant>] 'Auto' <flags>*
An auto property is, for all intents and purposes, a public variable. It will create a hidden variable and hidden get and set functions that manipulate it, so you don't have to. It may optionally be initialized with a value using the same syntax as an object variable. There are also internal optimizations that make these properties more efficient than the "full" type.
Auto properties may not be defined in native scripts.
An auto property is made immutable by adding "Const" to the end of the definition line. The value of a "Const" property can only be set in the editor, so any value recorded in a save game will be ignored, allowing changes to the master file to override save game data.
; Make an auto property that exposes a float value, and initialize it with 1.0
float Property MyProperty = 1.0 Auto
<auto read-only property> ::= <type> 'Property' <identifier> '=' <constant> 'AutoReadOnly' <flags>*
An auto read-only property is simply a publicly accessible value that cannot be changed. For obvious reasons, it must be assigned a value when declared. There are also internal optimizations that make these properties more efficient than other properties.
; Make an auto read-only property that exposes a string and cannot be changed
string Property Hello = "Hello world!" AutoReadOnly
A group is a collection of Properties that show up in the Creation Kit and in-game collectively for organizational purposes. They have no effect on how the script is written or behaves. Groups and properties in groups are the only time order of items in the source code is preserved in Papyrus.
<group> ::= 'Group' <identifier> <flags>
(<property>)+
'endGroup'
A group must contain at least one property, and its name must be unique in the same source file. If its name matches a group in a parent script, then the contents of the two groups will be merged. The order of groups in the source file is preserved when displayed in the editor and game, as well as the order of properties inside the groups.
Group MyGroup
{A group containing properties}
int Property FirstProperty auto
float Property SecondProperty auto
EndGroup
Group MyOtherGroup CollapsedOnRef
{Another group, which will appear collapsed by default when viewed on a ref in the editor}
int Property TheFirstOne auto ;Will appear first, even though it alphabetically sorts second
int Property SecondOne auto
EndGroup
The order of groups in the source file will be preserved, as will the order of properties inside the group. Any properties outside a group will not have their order preserved (and are implicitly in the "ungrouped group" with no name). By default, the Game and Creation Kit will sort groups appearing in the parent script first, then in the child script. And if two groups are merged (due to having the same name), again the parent properties will appear first.
Flags control behavior of groups in the properties window of the editor.
- See also the Group Flag.
Scriptname ParentScript
Group ParentGroup
int Property ParentProperty Auto
EndGroup
Scriptname ChildScript extends ParentScript
Group ChildGroup
int Property ChildProperty Auto
float Property AProperty Auto
EndGroup
Group ParentGroup
int Property ChildProperty1 Auto
EndGroup
In the Creation Kit and Game, they will appear sorted by parent first.
ParentGroup
ParentProperty
ChildProperty1
ChildGroup
ChildProperty
AProperty
Structures are basically mini-objects that can hold several variables of different types, but cannot contain functions or events, and cannot extend any objects or other structures. However, like objects (and unlike structures in other languages) they are passed and held onto by reference, not by value. Structures may be used in Arrays.
<Struct> ::= 'Struct' <identifier>
<variable definition>+
'Endstruct'
A structure is defined by having its name on the line with the structure, followed by the list of variables the structure contains, and terminated with 'EndStruct
'.
The variables inside a structure can be anything except another structure, an Array, or a var
.
Structure variables also may not be defined as constant.
Unlike object variables, structure variables may have documentation strings attached.
; A structure containing two floats.
Struct Point
float X
float Y
EndStruct
; A structure containing a quest and a stage to set.
Struct QuestStage
Quest QuestToSet
{The quest whose stage is to be set}
int StageToSet
{The stage to set on the quest}
EndStruct
; A structure that will FAIL to compile.
Struct BadStruct
var MyVariable ; BAD - cannot contain var type variables
int[] MyArray ; BAD - cannot contain arrays
Point MyPoint ; BAD - cannot contain other structures
float MyFloat const ; BAD - cannot have const variables
EndStruct
<struct creation> ::= 'new' <struct type>
To create a structure, use the "new
" keyword, followed by the type.
The initial value of each variable in the structure will be the default value specified for each member in the structure definition.
If you make a structure property, then the Creation Kit will create and set up the structure for you if the Creation Kit user gives it a value. Note that this cannot appear in the script outside of a function.
; Create a new point structure.
Point myPoint = new Point
; Create an array of five new point structures.
Point[] array = new Point[5]
array[0] = new Point
array[1] = new Point
array[2] = new Point
array[3] = new Point
array[4] = new Point
<struct type> ::= <script name> ':' <struct name>
You can access a structure type defined in another script by prefixing the name of the structure with the name of the script containing the structure, separating them with a colon. If you don't want to type in the script name all the type, you can use Import, just like with global functions.
; Assume Point structure is defined in MyScript
MyScript:Point myPoint = new MyScript:Point
<struct access> ::= <expression> '.' <member name>
To get a specific structure member, put its name after a dot, just like accessing a property on a script.
; get X element of a point
x = myPoint.X
; Set the quest stage contained in the "questStage" structure defined above
myQuestStage.QuestToSet.SetStage(myQuestStage.StageToSet)
Arrays are values that contain several other values, indexed by a 0-based number. For example index zero is the first element, index one in the second, and so on.
<array type> ::= <element type> '[' ']'
An array type is denoted by adding a pair of empty brackets after the type of the elements. The element type may only be a non-array type, so multi-dimensional arrays are not allowed. These types can be used anywhere else other types are used - as parameter, return, Variable, and Property types.
; An array of integers
int[]
; An array of MyObjects
MyObject[]
<array creation> ::= 'new' <element type> '[' <expression> ']'
To create an array, use the "new
" keyword, followed by the type and size.
The Size is denoted by the integer between the two square brackets, but that number may come from an expression or variable.
The initial value of each element will be the Default Value for the type.
If you make an array property, then the Creation Kit will determine the size of the array by how many elements are put into it.
Note that this cannot appear in the script outside of a function.
; Create an array of 20 floats
float[] x = new float[20]
; Create an array size of (5*count) MyScripts
MyScript[] x = new MyScript[5 * count]
<array length> ::= <expression> '.' 'Length'
To get the length of an array, you can access the read-only length property on the Variable holding the array.
If the variable is none
, the length will still succeed, but return 0.
Note that the last valid index in an array is the length of the array, minus one.
; Get the length of our array
int[] myArray = new int[10]
x = myArray.Length ; x will get 10
; Get the length of an uninitialized array (None)
float[] myArray
x = myArray.Length ; x will get 0
<array access> ::= <expression> '[' <expression> ']'
To get a specific array element, just put the index of the element you want between two square brackets. This value can also come from an Expression, or a return value of a Function, or something else. The valid elements in an array are from zero to the length minus one.
; get the first element in the array
x = myArray[0]
; get the last element of the array
x = myArray[myArray.Length - 1]
Several additional functions are available for arrays, listed below.
- Add - Array
- Clear - Array
- Find - Array
- FindStruct - Array
- Insert - Array
- Remove - Array
- RemoveLast - Array
- RFind - Array
- RFindStruct - Array
<cast> ::= <expression> 'as' <type>
<type check> ::= <expression> 'is' <type>
Casting an expression from one type to another allows you to perform additional manipulations on the object. This includes handing it off to a function or property that expects a different type.
The "is
" operator checks to see if the expression to the left is the type requested.
The check is strict for base types (bool
, int
, etc) but loose for object types (Form
, Alias
, etc).
The various types of casts are listed below.
- Compiler auto-cast from: Anything
Anything in Papyrus can be cast to a boolean. The result that you get depends on the type of object that you are casting from, according to the following table.
Expression Type | Resulting Boolean |
---|---|
Int | True if the integer is non-zero. |
Float | True if the float is non-zero (accounting for a small epsilon). |
String | True if the string isn't empty. |
Objects | True if the object isn't None. |
Arrays | True if the array is 1 element or longer in size. |
Structs | True if the structure isn't None. |
Var | Whatever the contents of the variable would cast to. |
; x gets true
x = 1 as bool
; x gets false
x = "" as bool
; x gets true
x = new int[5] as bool
- Compiler auto-cast from: Nothing
Floats, strings, and vars can be cast to integers. Floating point values will be truncated, and strings will convert to their integer representation, if it has one (otherwise 0). Vars will convert to whatever their contents would convert to, or 0 if their contents cannot be cast.
; x gets 10
x = 10.5 as int
; x gets 5
x = "5 little dwarves!" as int
- Compiler auto-cast from: Int
Integers, strings and vars can be cast to floats. Integers will simply be their floating point equivalents, and strings will convert to their float representation, if it has one (otherwise 0.0). Vars will cast to whatever their contents would have cast to, or 0.0 if their contents won't cast to floats.
; x gets 15.0
x = 15 as float
; x gets 10.34
x = "10.34" as float
- Compiler auto-cast from: Anything
Anything can be cast to strings. The result that you get depends on the type of object that you are casting from, according to the following table.
Expression type | Resulting string |
---|---|
Bool | "True" or "False" depending on the value |
Int | The string version of the integer |
Float | The string version of the float |
Objects | A string representing the object in the format: [ScriptName <EditorID (FormID)>]
|
Arrays | A list of elements in the array separated by commas, formatted as above, and possibly truncated with a "..." if too long for the internal string buffer. |
Structs | A list of the values in the struct separated by commas, and possibly truncated with a "..." if too long for the internal string buffer. |
Var | Whatever the contents of the var would cast to |
; x gets "15"
x = 15 as string
; x gets "[0, 0, 0]"
x = new int[3] as string
- Compiler auto-cast from: Child object
Only other objects or vars can be cast to objects, and only if that object is a direct child or parent of the one being cast.
If you are casting from the parent object to the child one, the cast may fail if it isn't actually an instance of the child object, in which case the result will be none
.
If you attempt to cast a non-object var
to an object, the result will also be none
.
; x gets the parent object.
x = ChildVariable as ParentObject
; x gets the child object if it is one, otherwise None.
x = ParentVariable as ChildObject
; If the alias is an actual actor, you can cast the alias to actor to with functions like StartCombat().
; Functions that expect actors to be called on them.
MyAlias.GetReference() as Actor
; When needing to cast a Quest to its quest script, first make a property.
Quest property MyQuest auto
; Then use this line:
MyQuest as MyQuestScript
; Same can be done with GetOwningQuest() for objects directly attached to the Quest.
GetOwningQuest() as MyQuestScript
; When calling them, they must be placed in parenthesis.
- Compiler auto-cast from: Nothing
Arrays can cast to other arrays, but only explicitly, and only if their elements could be cast.
Casting arrays makes a copy of the array, and elements that fail to cast will end up as none
, 0, or equivalent.
; x gets a copy of myArray cast as an array of forms - elements that don't cast are converted to None
x = myArray as Form[]
- Compiler auto-cast from: Nothing
Nothing can be cast to a structure.
- Compiler auto-cast from: Everything but arrays
Everything except arrays can be cast to the var
type.
The variable will then internally hold that value and remember what type it is.
; x gets the value of myInt, and will return that value when cast back to an int
x = myInt as var
Extension
Features
- Language Definition
- IntelliSense
- Code Navigation
- Refactoring
- Compilation
-
Debugging
- Debug View
- [Debug Console](Debug Console)
- [Assembly View](Assembly View)
Creation Engine
Language
Help