Yes. You can mix “simple” registrations for your non-latent functions and the NativeFunction<…>→SetLatent() form for only the one you want to be latent. In practice, it looks like this:
#include "PCH.h" #include "SKSE/PapyrusNativeFunctions.h" using namespace RE; // for BSFixedString, StaticFunctionTag, VMClassRegistry static BSFixedString CheckPath(StaticFunctionTag*, BSFixedString basePath) { // …non-latent path‐validation logic… } static BSFixedString GetResult(StaticFunctionTag*) { // …non-latent “what did the capture return?” logic… } static BSFixedString Cancel(StaticFunctionTag*) { // …non-latent “cancel the pending capture” logic… } static bool Papyrus_TakePhoto( StaticFunctionTag*, BSFixedString basePath, BSFixedString imageType, float GIF_Duration, float Compression, BSFixedString DDS_Mode ) { // …latent capture logic (spawns a thread, returns immediately)… } static bool RegisterPapyrusFunctions(VMClassRegistry* registry) { // // 1) Non-latent registrations can use the simple overload: // registry->RegisterFunction( "CheckPath", // Papyrus name "Printscreen_Formula_script", // Papyrus script it lives in CheckPath // C++ function pointer ); registry->RegisterFunction( "GetResult", "Printscreen_Formula_script", GetResult ); registry->RegisterFunction( "Cancel", "Printscreen_Formula_script", Cancel ); // // 2) Latent “TakePhoto” must use NativeFunction<…> + SetLatent(): // registry->RegisterFunction( new BSScript::NativeFunction< StaticFunctionTag, // owner type always StaticFunctionTag for static Papyrus calls bool, // return type BSFixedString, // basePath BSFixedString, // imageType float, // GIF_Duration float, // Compression BSFixedString // DDS_Mode >( "TakePhoto", // Papyrus name = TakePhoto "Printscreen_Formula_script", // script name Papyrus_TakePhoto, // C++ function pointer registry // VMClassRegistry* )->SetLatent() // ← this makes it latent ); return true; } extern "C" DLLEXPORT bool SKSEAPI SKSEPlugin_Load(const SKSE::LoadInterface* skse) { SKSE::Init(skse); SetupLog(); SKSE::GetPapyrusInterface()->Register(RegisterPapyrusFunctions); return true; }
-
Non-latent (
CheckPath,GetResult,Cancel):
You simply callregistry->RegisterFunction("NameInPapyrus", "YourScriptName", YourCxxFunc);and Papyrus treats them as immediate (non-blocking) calls.
-
Latent (
TakePhoto):
You construct aBSScript::NativeFunction<…>(templated on owner/return/argument types), call.SetLatent(), then hand it toRegisterFunction(...). That tells SKSE “this one returns immediately, and we’ll fire the Papyrus continuation later.”
So yes: it’s perfectly valid (and common) to mix both registration formats in the same RegisterPapyrusFunctions(...) method.