Home
This is a terse guide on creating your own console commands using Papyrus and Custom Console (CC). This guide assumes experience with the Papyrus programming language and Unix-style shell commands i.e. ls
, cat
, git
.
On game start, CC will generate a list of custom console commands using config files within Data/SKSE/CustomConsole. Each YAML configuration file in this folder contains one command. Each command can contain as many subcommands as the author wants which prevents potential name conflicts between different authors.
When the user types something into the console, CC will determine whether or not it is a valid custom command. If it is, it will try to parse the arguments and if everything is valid, invoke the function provided in the configuration file for the given subcommand. If it is invalid, it will print an error in the console and not invoke the function. If your function returns a value (float, string, form), CC will attempt to format it and print the value to the console.
Let's say we want to create a command we can use to determine whether or not a particular actor is being detected by anyone. Luckily, a function for this can be found in PO3's Papyrus Extender: Bool Function IsDetectedByAnyone(Actor akActor) global native
so all we need to do is create a config file:
name: det-utils
alias: det
script: PO3_SKSEFunctions
help: utilities for testing detection
subs:
- name: is-detected
alias: idet
func: IsDetectedByAnyone
help: is the selected actor being detected by anyone
args:
- name: --target
alias: -t
type: actor
selected: true
required: true
help: the actor to check detection for
This creates a new command called det-utils
with the alias (shortened name) det
.
It has one subcommand is-detected
that takes one argument --target
.
selected: true
on this argument means it can be filled in using the currently selected object reference (by opening the console and clicking on something). The prefix --
in the name means its a flag argument rather than a positional argument. required: true
means that an error will be printed to the console if the user has not clicked on a valid actor and has not manually passed in an actor using the flag. This field is necessary because flag arguments are usually optional. If not required and not provided by the user, CC will invoke the function with the actor argument being none
. Finally, -t
is a shortened way to write --target
. The help fields will automatically be printed out if the user asks for help i.e. det-utis --help
and allows command config files to be self-documenting.
A user can then use the console command as follows:
After clicking on an actor with the console open:
det-utils is-detected
Manually passing in a specific actor instead of using the currently selected one:
det-utils is-detected --target HuldaREF
Shortened form:
det idet
det idet --target HuldaREF
These commands will print true
if the target is detected and false
if not.
name: form-utils
alias: futil
script: PO3_SKSEFunctions
help: utilities for reading and manipulating forms
subs:
- name: add-keyword
alias: ak
func: AddKeywordToForm
help: add the provided keyword to selected form
args:
- name: form
type: form
help: the form the keyword will be added to
- name: keyword
type: keyword
help: the keyword to add to the provided form
- name: remove-keyword
alias: rk
func: RemoveKeywordOnForm
help: remove the provided keyword to selected form
args:
- name: form
type: form
help: the form the keyword will be removed from
- name: keyword
type: keyword
help: the keyword to remove from the provided form
In this example, we use positional arguments (there are no leading dashes). Positional arguments are usually best for arguments you expect to be required, although CC will still supply default values if you don't add required: true
.
Usage:
Add the valuable keyword to gold: form-utils add-keyword Gold001 ValuableKwd
Remove the heavy armour keyword from Dragonscale Armor: futil rk 0x13940|Skyrim.esm ArmorTypeHeavy
Custom types may or may not be supported. If a function you're using takes a custom type, you may need to alias the function with another that takes a regular type like Form, Armor, etc.
CC only supports static functions i.e. those that have the global
specifier. Workarounds are discussed in the Best Practices section at the end.
Form arguments can be passed to functions as either editor or form IDs. CC will attempt to convert the string into an actual object and cast it to the correct type prior to calling the command's function. For example, the gold form can be provided to a command as either 0xF|Skyrim.esm
or Gold001
.
The functions being invoked must be global. Workaround for executing functions on attached scripts i.e. quests can be found below. Sometimes turning a form into a specific type (Keyword, Quest, Armor) can fail. In these cases, it might be better to change the argument type to a Form and cast it yourself within the function.
Array arguments and return values are unsupported. I intend to eventually add support for both.
While CC will attempt to make return values from functions user-readable, its best to simply return a string that CC can print out or to print to the console yourself using ConsoleUtilSSE to be more user-friendly. Even if you don't want to change a function signature, you can simply create an alias that creates a string using the original function's return value. For example, I can create an alias for Bool Function IsDetectedByAnyone(Actor akActor) global native
in my own separate script as follows:
Scriptname PO3_ExtenderCommands Hidden
String Function IsDetectedByAnyone(Actor akActor) global
string name = akActor.GetActorBase().GetName()
if PO3_SKSEFunctions.IsDetectedByAnyone(akActor)
return name + " is currently detected by someone"
else
return name + " is not being detected by anyone"
endIf
EndFunction
My config file would then become:
name: det-utils
alias: det
script: PO3_ExtenderCommands # changed from PO3_SKSEFunctions
help: utilities for testing detection
subs:
- name: is-detected
alias: idet
func: IsDetectedByAnyone
help: is the selected actor being detected by anyone
args:
- name: --target
alias: -t
type: actor
selected: true
required: true
help: the actor to check detection for
Similarly, if you want to invoke a function in a script attached to a specific object (which CC does not currently support), you can simply alias the original. For instance, if I want to call a function named KnockOutNazeem
in one of my quest scripts named KnockOutNazeemScript
I can do this:
Scriptname Py_Commands Hidden
String Function KnockoutNazeem() Global
(Quest.GetQuest("KnockOutNazeemQuest") as KnockOutNazeemScript).KnockoutNazeem()
return "You've done Whiterun a great service."
EndFunction
name: py-commands
alias: pyc
script: Py_Commands
help: have you been to the cloud district?
subs:
- name: knockout
alias: kout
func: KnockoutNazeem
help: knock that sucker out
Usage: pyc kout
.