Papyrus Syntax¶
Papyrus is a statically typed, object-oriented scripting language. Scripts are written in .psc source files and compiled to .pex bytecode for execution by the engine's virtual machine.
Script Structure¶
Every .psc file defines exactly one script. The first line declares the script name and optionally its parent:
A script with no Extends clause implicitly extends the built-in ScriptObject base class. Scripts can also be marked as Const, Native, Hidden, or Default:
| Flag | Meaning |
|---|---|
Native |
Script has engine-bound native functions |
Hidden |
Hidden from the Creation Kit UI |
Const |
Script has no mutable state (FO4 only) |
Default |
Used for default scripts on base types |
Types¶
Primitive Types¶
| Type | Description | Default Value |
|---|---|---|
Bool |
True or False |
False |
Int |
32-bit signed integer | 0 |
Float |
32-bit IEEE float | 0.0 |
String |
Text string | "" |
Object Types¶
Any script name can be used as a type. Object variables hold references to game objects:
The special value None represents a null reference and is the default for all object types.
Arrays¶
Arrays are declared by appending [] to any type:
Arrays are 0-indexed and must be created before use with new:
Structs (FO4)¶
Structs are value types defined inside a script:
Struct members are accessed with dot notation:
Structs are always referenced by their containing script: MyScript:MyData.
Variables¶
Variables are declared at script scope or function scope:
; Script-scope variable
Int MyCounter = 0
Function Example()
; Function-local variable
String Message = "hello"
EndFunction
Script-scope variables persist across save/load. Local variables exist only for the duration of the function call.
Const Variables (FO4)¶
Variables can be marked Const to prevent reassignment after initialization:
Properties¶
Properties are the primary way to expose script data to the Creation Kit editor. They come in two forms.
Auto Properties¶
Auto properties generate a backing variable and getter/setter automatically:
The = True sets the default value visible in the Creation Kit.
Full Properties¶
Full properties define explicit getter and/or setter functions:
Int _health = 100
Int Property Health
Function Get()
Return _health
EndFunction
Function Set(Int value)
If value < 0
_health = 0
Else
_health = value
EndIf
EndFunction
EndProperty
Auto Read-Only Properties¶
Read-only properties can only be set in the Creation Kit, not at runtime:
Conditional Properties¶
Conditional properties are accessible from the game's condition system:
Functions¶
Instance Functions¶
Parameters can have default values. Functions with no return type return None implicitly.
Return Values¶
Int Function CalculateValue(Int base, Float multiplier)
Return (base * multiplier) as Int
EndFunction
Global (Static) Functions¶
Global functions belong to the script type, not to an instance:
Called as MyScript.GetMax(10, 20).
Native Functions¶
Native functions are implemented in engine code (C++) and declared without a body:
Calling Functions¶
; Instance method
MyActor.AddItem(HealthPotion, 3)
; Global/static function
Int Result = Math.Sqrt(16.0) as Int
; Parent class method
Parent.OnInit()
; Chained property access
Game.GetPlayer().AddItem(HealthPotion)
Events¶
Events are callback functions triggered by the engine. They use the Event keyword:
Event OnInit()
; Runs when the script initializes
EndEvent
Event OnActivate(ObjectReference akActionRef)
; Runs when the object is activated
EndEvent
Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, \
Bool abPowerAttack, Bool abSneakAttack, Bool abBashAttack, Bool abHitBlocked)
; Runs when hit by an attack
EndEvent
Common events include OnInit, OnActivate, OnHit, OnDeath, OnLoad, OnUnload, OnUpdate, OnItemAdded, OnItemRemoved, OnTriggerEnter, and OnTriggerLeave.
Remote Events (FO4)¶
FO4 added the ability to register for and receive events from other scripts:
Event OnInit()
RegisterForRemoteEvent(MyQuest, "OnStageSet")
EndEvent
Event Quest.OnStageSet(Quest akSender, Int auiStageID, Int auiItemID)
; Handle quest stage change
EndEvent
States¶
States allow a script to change its behavior at runtime. Each state can override functions and events:
Auto State Waiting
Event OnActivate(ObjectReference akActionRef)
Debug.Notification("Starting...")
GoToState("Running")
EndEvent
EndState
State Running
Event OnActivate(ObjectReference akActionRef)
Debug.Notification("Already running!")
EndEvent
Event OnUpdate()
; Do work
EndEvent
EndState
The Auto keyword marks the initial state. GoToState("") returns to the default (empty) state. Functions defined outside any state block belong to the default state and act as fallbacks.
OnBeginState / OnEndState¶
State Active
Event OnBeginState()
RegisterForUpdate(1.0)
EndEvent
Event OnEndState()
UnregisterForUpdate()
EndEvent
EndState
Control Flow¶
If / ElseIf / Else¶
If Health < 25
Debug.Notification("Low health!")
ElseIf Health < 50
Debug.Notification("Moderate health")
Else
Debug.Notification("Healthy")
EndIf
While Loop¶
Papyrus has only While loops — there is no For loop:
Operators¶
Arithmetic¶
| Operator | Description |
|---|---|
+ |
Addition (Int, Float, String concatenation) |
- |
Subtraction |
* |
Multiplication |
/ |
Division (integer division truncates toward zero) |
% |
Modulo (C-style, truncation toward zero) |
Comparison¶
| Operator | Description |
|---|---|
== |
Equal |
!= |
Not equal |
< |
Less than |
<= |
Less than or equal |
> |
Greater than |
>= |
Greater than or equal |
Logical¶
| Operator | Description |
|---|---|
&& |
Logical AND |
\|\| |
Logical OR |
! |
Logical NOT |
Assignment¶
| Operator | Description |
|---|---|
= |
Assign |
+= |
Add and assign |
-= |
Subtract and assign |
*= |
Multiply and assign |
/= |
Divide and assign |
%= |
Modulo and assign |
Casting¶
Use the as keyword to convert between compatible types:
Float F = 3.7
Int I = F as Int ; Truncates to 3
Int N = 42
Float G = N as Float ; 42.0
String S = N as String ; "42"
Int M = "123" as Int ; 123
Bool B = N as Bool ; True (non-zero)
Object types can be cast up or down the inheritance chain:
ObjectReference Ref = SomeActor as ObjectReference ; upcast (always safe)
Actor A = Ref as Actor ; downcast (None if wrong type)
The is keyword (FO4) checks type without casting:
Arrays¶
Creation¶
Access¶
Search¶
Int Index = Numbers.Find(42) ; returns -1 if not found
Int RIndex = Numbers.RFind(42) ; reverse search
Modification (FO4)¶
Numbers.Add(99) ; append
Numbers.Insert(99, 3) ; insert at index 3
Numbers.Remove(3) ; remove at index 3
Numbers.RemoveLast()
Numbers.Clear()
Struct Arrays (FO4)¶
MyData[] Entries = new MyData[0]
MyData Entry = new MyData
Entry.Count = 5
Entries.Add(Entry)
Int Found = Entries.FindStruct("Count", 5)
Guards (FO4)¶
Guards provide mutex-like synchronization for scripts that run on multiple threads:
Guards prevent concurrent execution of the guarded block across all threads referencing the same guard variable. TryLockGuard is a non-blocking variant that skips the block if the guard is already held.
Inheritance¶
Scripts inherit all properties, functions, events, and states from their parent:
ScriptName ChildScript Extends ParentScript
; Override a parent function
Function DoSomething()
Parent.DoSomething() ; call parent implementation
; additional behavior
EndFunction
The Parent keyword accesses the parent class implementation of any function.
Comments¶
Script Fragments¶
The Creation Kit generates fragment scripts for quests, scenes, dialogue, packages, perks, and terminal entries. These are auto-generated .psc files with naming conventions like:
| Type | Prefix | Example |
|---|---|---|
| Quest stage | QF_ |
QF_MQ101_00000001.psc |
| Scene | SF_ |
SF_MQ101Scene01_00000002.psc |
| Dialogue | TIF_ |
TIF__00000003.psc |
| Package | PF_ |
PF_SleepPackage_00000004.psc |
| Perk | PRKF_ |
PRKF_GunNut01_00000005.psc |
| Terminal | TerminalScript |
TerminalScript.psc |
Fragment scripts extend system base classes (Quest, Scene, TopicInfo, Package, Perk, Terminal) and contain auto-generated entry point functions.