Skip to content

Papyrus Types

Scrivener07 edited this page Jun 27, 2019 · 10 revisions
Table of Contents

Literals

Papyrus supports five kinds of literal values: boolean, integer, float, string, and none.

Boolean Literals

Boolean literals are simple, they are just true or false values.

Specification
<boolean> ::= 'true' | 'false'
Usage
bool myBoolean
If (myBoolean == true)
	; do something
EndIf

Integer Literals

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.

Specification
<integer> ::= (['-'] <digit>+) | ('0x' (<digit>|<hex digit>)+)
Usage
int count
If (count < 5)
	; do something
	count +=1
EndIf
Examples
10
-15365
0x0001F2C8

Float Literals

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.

Specification
<float> ::= ['-'] <digit>+ '.' <digit>+
Usage
float myValue
If (myValue ==  5.123)
	; do something
EndIf
Examples
1.5234
-125412.0

String Literals

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.

Specification
<string> ::= '"' <anything but another ", \, newline, or linefeed> '"'
Escape Codes
Escape Code
\n Newline
\t Tab
\\ Backslash
\" Double quote
Usage
string myText = "Hello World."
Debug.Trace(myText)
Examples
"Hello, World!"
"" ; Empty string
"\\\n" ; a string with a backslash followed by a new line
Character Casing

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.

Notes
  • 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.

None Literal

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.

Specification
none
Usage
If (myValue == none)
	; The value is none.
EndIf

Default Values

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

Variables

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.

Defining Variables

  • 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.
Examples
; 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!

Value Variables

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.

Examples
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

Reference Values

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.

Examples
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!

Var Variables

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.

Examples
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

Properties

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.

Full Property

<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.

Examples
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

<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.

Examples
; 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

<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.

Examples
; Make an auto read-only property that exposes a string and cannot be changed
string Property Hello = "Hello world!" AutoReadOnly

Property Groups

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 Definition

 <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.

Examples
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

Ordering

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

Flags control behavior of groups in the properties window of the editor.

Examples
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

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.

Defining a Struct

<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.

Examples
; 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

<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.

Examples
; 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

Accessing Struct Types in Other Scripts

<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.

Examples
; Assume Point structure is defined in MyScript
MyScript:Point myPoint = new MyScript:Point

Struct Members

<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.

Examples
; 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

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

<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.

Examples
; An array of integers
int[]
; An array of MyObjects
MyObject[]

Array Creation

<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.

Examples
; 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

<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.

Examples
; 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 Elements

<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.

Examples
; get the first element in the array
x = myArray[0]
; get the last element of the array
x = myArray[myArray.Length - 1]

Array Functions (FO4)

Several additional functions are available for arrays, listed below.

Casting

<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.

Cast to Boolean

  • 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.
Examples
; x gets true
x = 1 as bool
; x gets false
x = "" as bool
; x gets true
x = new int[5] as bool

Cast to Int

  • 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.

Examples
; x gets 10
x = 10.5 as int
; x gets 5
x = "5 little dwarves!" as int

Cast to Float

  • 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.

Examples
; x gets 15.0
x = 15 as float
; x gets 10.34
x = "10.34" as float

Cast to String

  • 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
Examples
; x gets "15"
x = 15 as string
; x gets "[0, 0, 0]"
x = new int[3] as string

Cast to Object

  • 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.

Examples
; 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.

Cast to Array

  • 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.

Examples
; 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[]

Cast to Struct

  • Compiler auto-cast from: Nothing

Nothing can be cast to a structure.

Cast to Var

  • 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.

Examples
; x gets the value of myInt, and will return that value when cast back to an int
x = myInt as var
Clone this wiki locally