-
Notifications
You must be signed in to change notification settings - Fork 8
Registering Functions
Once you've written a function in SKSE, you'll have to register it if you want to access it from within Papyrus. Because every function you might want to register will have a different number of parameters, SKSE has several different functions to register functions with various numbers of parameters.
Function with 0 parameters
float myFunction(StaticFunctionTag *base);
// ... Register function elsewhere in code:
registry->RegisterFunction(
new NativeFunction0<StaticFunctionTag, float>("myFunction", "MyClass", MyClassNamespace::myFunction, registry));In Papyrus:
float Function myFunction() global nativeFunction with 1 parameter
void my1ParamFunction(StaticFunctionTag *base, float f);
registry->RegisterFunction(
new NativeFunction1<StaticFunctionTag, void, float>("my1ParamFunction", "MyClass", MyClassNamespace::my1ParamFunction, registry));In Papyrus:
Function my1ParamFunction(float f) global nativeFunction with 2 parameters
void my2ParamFunction(StaticFunctionTag *base, float f, double d);
registry->RegisterFunction(
new NativeFunction2<StaticFunctionTag, void, float, double>("my2ParamFunction", "MyClass", MyClassNamespace::my2ParamFunction, registry));In Papyrus:
Function my2ParamFunction(float myFloat, double myDouble) global nativeFunction with 3 parameters
void my3ParamFunction(StaticFunctionTag* base, UInt32 n, float f, double d);
registry->RegisterFunction(
new NativeFunction3 <StaticFunctionTag, void, float, double, UInt32>("my3ParamFunction", "MyClass", myClassNamespace::my3ParamFunction, registry));Function my3ParamFunction(int n, float myFloat, double myDouble) global nativeFunction with 4 parameters
void my4ParamFunction(StaticFunctionTag *base, float f1, double d, UInt32 n, float f2);
registry->RegisterFunction(
new NativeFunction4 <StaticFunctionTag, void, float, double, UInt32, float>("my4ParamFunction", "MyClass", MyClassNamespace::my4ParamFunction, registry));Function my4ParamFunction(float f1, double d, int n, float f2) global nativeAt this point, the pattern should be clear: Use the NativeFunction with the number of parameters that you want your function to have in Papyrus. The NativeFunction methods provided by SKSE go up to 10 parameters (NativeFunction10). Then, inside the <>, the first parameter will be StaticFunctionTag, the second parameter will be your function's return type, and every parameter after will be the types of your function's parameters.
- The first parameter in your function must be the base class that your function belongs to. In all of the above examples,
StaticFunctionTagrepresents a function that has no base class. In the SKSE functions, you can see examples of functions that have a base class ofTESObjectREFRorTESSoulGem. A function with base typeStaticFunctionTagwill be aglobalfunction in Papyrus. It will be called stand-alone:MyGlobalFunction(param1, param2, ...). A function that has a base class will be alocalfunction in Papyrus, meaning it's called likeMyCustomObject.MyLocalFunction(param1, param2, ...) - The above code examples go in a dedicated function with signature
void papyrusObjectReference::RegisterFuncs(VMClassRegistry* registry)inside your class. This function is called in your main.m to register the functions when SKSE loads your plugin. See SKSE: Getting Started. - In the above code examples,
MyClassis the name of the class that these functions will be defined inside in Papyrus.MyClassNamespaceis the namespace you defined in C++ that contains the chosen function. Example:
namespace myClassNamespace {
void myFunction () {
}
}Sometimes you need to send back to your Papyrus script more than just one piece of information. Although it isn't possible to return an array from an SKSE function that's exported to Papyrus, you can actually change the values of an array or FormList that Papyrus passes into your SKSE function. Then inside the SKSE function you can edit the values of that array or FormList and . An example in a Papyrus script:
Form[] myReturnValues = new Form[2]
mySKSEMethod(param1, param2, myReturnValues)
; Now use the return values that were set inside the SKSE function:
Form returnValue1 = myReturnValues[0]
Form returnValue2 = myReturnValues[1]In your SKSE's implementation for mySKSEMethod, simply set the values of the myReturnValues array that's passed in and those values will be set later in your Papyrus script.
In your SKSE plugin, you'll need to declare your function that takes a parameter like the following example that takes a Papyrus Form array:
void myFunction(StaticFunctionTag *base, float myParam1, VMArray<TESForm*> returnValues) {
// My implementation
}For this to link properly, you'll need to define an UnPack template like the one's SKSE already defines in PapyrusArgs.cpp. In the case of TESForm, the template you can add to your SKSE class will be:
template <> void UnpackValue(VMArray<TESForm*> * dst, VMValue * src) {
UnpackArray(dst, src, GetTypeIDFromFormTypeID(TESForm::kTypeID, (*g_skyrimVM)->GetClassRegistry()) | VMValue::kType_Identifier);
}TODO
- Make sure that your function's declaration in your .h file matches the definition in your .cpp file. Otherwise, compilation will fail.
- If you're always getting a return value of None for your function, look in the Papyrus logs for mention of your function. It might say, "Native static function MyFunction does not match existing signature on linked type MyPluginScript. Function will not be bound." This indicates that the function declaration you wrote in your Papyrus script does not match how you declared it in your SKSE plugin. Double check them.