Papyrus FAQs
Resources
Why doesn't the Creation Kit's script editor have syntax highlighting?
The Creation Kit allows you to open scripts with an external text editor from the context menu, so you should use an external text editor if you want syntax highlighting and other features.
Where can I find a list of functions and their syntax that I can use in my scripts?
You should refer to the wiki and ultimately the source scripts, like Actor.psc
, ObjectReference.psc
, and Utility.psc
. These are the "classes" that your scripts typically "extend."
Where can I find examples of how to accomplish <something> or how to use a function?
Using a free third-party program named Agent Ransack, you can search through all of the base scripts almost instantly for examples, especially with a SSD.
To whom do I send fan/hate mail about Papyrus?
SmkViper created Papyrus and actively helps scripters in the official Fallout 4 Creation Kit forum.
First Steps
Where do I find the source scripts?
- In the
Data\Scripts
directory, there is a ZIP archive named Base.zip. - Extract that file using 7-Zip, WinZip, or Windows' native ZIP handler to the same folder with paths.
Why can't the Creation Kit find the source scripts?
You should have the following folders in the Data\Scripts
directory:
Data\Scripts\Source\Base
Data\Scripts\Source\User
In the Base folder, you should find all of the scripts you've extracted from Base.zip. The User folder is where all of your source scripts should and will be placed.
Where should compiled scripts be placed?
Compiled scripts do not belong anywhere in the Fallout 4\Data\Scripts\Source folder or subfolders. All compiled scripts should be placed in one of the following directories:
Data\Scripts
Data\Scripts\Fragments\*
Data\Scripts\<namespace>\*
Compiling
How else can I compile scripts?
- You can compile scripts through the Script Manager. In the Script Manager, right-click the script and compile only that script.
- You can compile scripts outside the Creation Kit by calling the compiler directly from the command shell.
If I change the source script, do I need to recompile that script?
Yes. If you make changes to a .psc
(Papyrus Source) file and never recompile, the game will always use the older .pex
(Papyrus Executable) file, and your changes will never take effect.
When I try to compile scripts outside the Creation Kit, my scripts always fail to compile. I'm sure my syntax is correct!
For the sake of simplicity, use fireundubh's Windows batch script (.bat file) for compiling scripts outside the Creation Kit.
- Save compile_fo4.bat to
C:\Scripts
. Create the directory if needed. - Update the paths at the top of the .bat script as needed.
- Add
C:\Scripts
to your system's PATH environment variable. (Control Panel > System > Advanced System Settings > Environment Variables
)
With this script, you can run the following commands from the command shell:
compile_fo4 // compile all scripts in the current directory recursively and output the compiled scripts to the specified output directory compile_fo4 script.psc // compile a single script in the current directory and output the compiled script to the specified output directory compile_fo4 script.psc --test // the --test parameter will delete the compiled script after successfully compiling the script (useful for testing whether a script compiles) compile_fo4 C:\path\to\script.psc // compile a single script using the absolute path to the script and output the compiled script to the specified output directory compile_fo4 project.ppj // compile the specified project file in the Projects directory
What are all of the parameters for PapyrusCompiler.exe? What do they mean?
You can run PapyrusCompiler.exe
on the command line without any parameters to get a list of all of the parameters and what they mean. That list has been replicated here.
Papyrus Compiler Version 2.8.0.4 for Fallout 4 Copyright (C) ZeniMax Media. All rights reserved. Usage: PapyrusCompiler <object, folder, or project> [<arguments>] object Specifies the object to compile. (-all is not specified, with a .psc or no extension) folder Specifies the folder to compile. (-all is specified) project Specifies the project to compile. (Has a .ppj extension) arguments One or more of the following (overrides project, if any): -debug|d Turns on compiler debugging, outputting dev information to the screen. -release|r Turns on release-mode processing, removing debug code from the script. -final Turns on final-mode processing, removing beta code from the script. -optimize|op Turns on optimization of scripts. -output|o=<string> Sets the compiler's output directory. -import|i=<string> Sets the compiler's import directories, separated by semicolons. -flags|f=<string> Sets the file to use for user-defined flags. -all|a Invokes the compiler against all psc files in the specified directory (interprets object as the folder). -norecurse Used with -all to tell the compiler not to recurse into subdirectories. -ignorecwd Tells the compiler to ignore the current working directory when searching for files. Otherwise it is implied to be first in the import list. -quiet|q Does not report progress or success (only failures). -noasm Does not generate an assembly file and does not run the assembler. -keepasm Keeps the assembly file after running the assembler. -asmonly Generates an assembly file but does not run the assembler. -? Prints usage information.
What is "debug code" and "beta code"?
Two new properties have been introduced in the Fallout 4 version of Papyrus: DebugOnly
and BetaOnly
. These properties can be declared at the function and script levels.
- When release-mode processing is enabled, the compiler will remove
DebugOnly
functions from the compiled script. The source file will remain untouched. - When final-mode processing is enabled, the compiler will remove
BetaOnly
functions from the compiled script. The source file will remain untouched.
These properties are useful when you want to run code during development that you don't want to run when your mod is in the wild.
Why doesn't my script output any debug messages to the Papyrus log?
The compiler is processing your script in release mode. The Debug script has the DebugOnly
property, so all Debug functions (e.g., Debug.Notification
, Debug.Trace
) will be removed from your script when compiled.
The batch script provided above, and the Papyrus Project template provided below, enables release-mode and final-mode processing. To use DebugOnly
and/or BetaOnly
code, you'll need to modify these files as needed.
None of my scripts are running! / The Papyrus log indicates they're missing.
Add to %USERPROFILE%\Documents\My Games\Fallout4\Fallout4Custom.ini
:
[Archive] bInvalidateOlderFiles=1
This will allow the game to load "loose files," which includes your scripts. Also, ensure that the paths to your compiled scripts are correct.
What is a namespace? How do I configure my mod to use a namespace?
For our purposes, a namespace is just a subfolder where your scripts are located for a specific mod in the User folder. Namespaces help you stay organized.
To set up a namespace, follow these instructions:
- While in the Creation Kit: go to
File > Preferences
and find the Scripts tab. - In the box labeled
Default namespace
, enter the name of your namespace without any spaces or special characters. - Click Apply and then Close.
From now on, any scripts you create with the Creation Kit will be placed in that namespace folder, and the ScriptName
declaration in each script will be prefixed with the name of your namespace.
When I try to compile namespaced scripts outside the Creation Kit, my scripts always fail to compile. Help!
Namespaced scripts and scripts with paths, usually fragment scripts, should be compiled using a Papyrus Project file, which is a plain text XML file with the .ppj
extension that the Papyrus compiler natively understands.
PapyrusProject.xsd
in the Fallout 4\Papyrus Compiler
directory defines the XML schema for Papyrus Project files. Here is an example of a Papyrus Project file:
<PapyrusProject xmlns="PapyrusProject.xsd" Flags="Institute_Papyrus_Flags.flg" Output="D:\Steam\steamapps\common\Fallout 4\Data\Scripts" Optimize="true" Release="true" Final="true"> <Imports> <Import>D:\Steam\steamapps\common\Fallout 4\Data\Scripts\Source\Base</Import> <Import>D:\Steam\steamapps\common\Fallout 4\Data\Scripts\Source\User</Import> </Imports> <Scripts> <Script>AutoLoot\Fragments\Terminals\TERM_dubhAutoLootMenuAdvance_0100272C.psc</Script> ... </Scripts> </PapyrusProject>
Logging
How do I enable the Papyrus log?
You can enable the Papyrus log by adding the following lines to %USERPROFILE%\Documents\My Games\Fallout4\Fallout4Custom.ini
:
[Papyrus] fPostLoadUpdateTimeMS=500.0000 bEnableLogging=1 bEnableTrace=1 bLoadDebugInformation=1
Can the Papyrus log help me figure out why Fallout 4 is crashing?
The Papyrus log is not a crash log. There is a warning at the top of the log now that states this quite clearly. The Papyrus log is not likely to point at what was going on near the crash because if a crash occurs, the game will be unable to write to the log.
Advanced
Are While loops safe to use for persistent or continuous effects?
According to SmkViper:
Saves operate generally the same way they did with Skyrim. Any running threads are saved so they can be resumed when the save is loaded. This means any while loops running will be saved into the save game, because of course they need to continue running when the save is loading. However because they are saved, any patch to the function that you release in a mod can't be applied to that thread until it exits the function (which means the while loop has to end). While loops also keep the thread alive, which consumes some memory, as the thread and all its stack data and calling functions stack data need to stick around. If you end up with >100 threads running at once for over a few seconds, Papyrus assumes something is wrong and starts spitting out stack dumps to the log so you can find and fix the problem. If the while loops aren't sleeping (via Wait or some other latent call) they can contribute to script lag as the VM has to take time to process the thread in addition to everything else it's running. Timers replace OnUpdate partially in response to the problems with continual update event registrations (they are equivalent to single update registrations). And because it's sometimes really handy to have multiple timers going at once on different intervals. So yes, if you need something running over a long period of time, you should be using timer events. All that being said, F4's version of Papyrus is much more aggressive about cleaning out dead data if things go... missing. Not that any self-respecting modder would do that.
How do I write save-safe scripts then?
According to SmkViper:
Removing mods and continuing with a save that relies on that mod is not and never has been supported. Proceed at your own risk. Demons may fly out of your monitor and spray you with silly string. (You think I'm kidding - have you heard of Bob the modder? No? Exactly.)
However, here are some evolving guidelines that can be followed to reduce the incidence of uninstallation issues and/or shift the blame to user error:
- Instead of using a
While
loop, use anOnTimer()
event. TheOnTimer()
event is Fallout 4's version of Skyrim'sOnUpdate()
. Refer to the question and answer below for more information. - Avoid endless
While
loops that cannot terminate after the plugin has been removed. For example, aWhile Player.HasPerk(kPerk)
loop can terminate ifkPerk
references a perk unique to the plugin. Removing the plugin removes the perk, and so when the player loads a save, the thread can then exit. However, ifkPerk
references a vanilla perk,HasPerk()
will always evaluate toTrue
and theWhile
loop will continue iterating over instructions that may require objects which no longer exist, and therefore keep the thread alive, consuming memory, contributing to script lag, and producing errors. Refer to the above question for more information about howWhile
loops and saves interact. - Always finish your code as fast as possible. This ultimately means making optimizations to both your scripts and your systems. For example, if you need a
While
loop to iterate through a large formlist or array, chunk the formlist or array, and in between each chunk, give the loop an opportunity to exit. You might also want to ensure that there are enough conditions in place that your script can run only when needed, do its job, and get out when the job's done. - Provide the user with the ability to "uninstall" your mod while in the game. This can be done by offering users a "Shutdown" option in a holotape menu that forcibly exits all continuously running scripts. Some users may not take the time to take advantage of this feature, so it remains important to ensure that you've taken the necessary steps to write save-safe scripts.
- Finally, warn users that uninstalling mods and continuing with saves that rely on those mods is unsupported and unsafe.
How would I emulate a Break statement in a While loop?
You can emulate a Break
statement like so:
Bool bBreak = False While (<primary_condition>) && !bBreak // do stuff If <secondary_condition> bBreak = True EndIf EndWhile
The alternative is to use Orvid's Caprica compiler, which allows you to use the For
, ForEach
, Break
, and Continue
statements.
Please remember that Caprica is an unofficial, alternative compiler that is not supported by Bethesda Softworks. Any questions or issues regarding Caprica should be directed to Orvid.
Are scripts saved in save games?
According to SmkViper:
Scripts are not saved in your save game. Script variable values and currently running functions are saved so they can be resumed.
What's the alternative to OnUpdate, RegisterForUpdate, and RegisterForSingleUpdate?
You can use the OnTimer event alongside the StartTimer and CancelTimer functions.
Are arrays limited to 128 elements?
According to SmkViper:
There is still an internal 128 limit on array items. Attempting to make an array larger than that will spit out an error at runtime.
How do I create a dynamic array?
According to SmkViper:
Arrays are now dynamic. To create an empty array, pass 0 as the size. You are now allowed to pass in a variable or calculation for an array size, and you can call add and remove on the array to add and remove items.
How do I create key-value pairs?
According to SmkViper:
You can basically fake a map using structs and the new FindStruct function. Just make one struct variable your key, and the other the value, and then run Find on the key. (Unlike maps, duplicates are permitted since it's just an array.)
When I call Activate() on a stack of items dropped by the player, only one item in the stack is returned.
This is a known issue, but it is unknown whether there will be a fix. If there is a fix, there will be an alternate function.
According to SmkViper:
We don't add or change parameters to existing functions after they've been released (because it would require all scripts using that function to be changed or recompiled). That's not to say new functions cannot be added, but no promises. Because of the heavy use of Activate in scripts, I'm very wary of changing its behavior, but I'll look into it. An alternative solution might be to add an ActivateAll function instead of "fixing" Activate.