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; }

  1. Non-latent (CheckPath, GetResult, Cancel):
    You simply call

    registry->RegisterFunction("NameInPapyrus", "YourScriptName", YourCxxFunc);

    and Papyrus treats them as immediate (non-blocking) calls.

  2. Latent (TakePhoto):
    You construct a BSScript::NativeFunction<…> (templated on owner/return/argument types), call .SetLatent(), then hand it to RegisterFunction(...). 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.