You’re correct—simply calling
registry->RegisterFunction("TakePhoto", "Printscreen_Formula_script", Papyrus_TakePhoto);
will register TakePhoto as an ordinary (non‐latent) Papyrus function. In order for Papyrus to treat it as a latent function, you must use the NativeFunction‐based registration and call its SetLatent() method. In other words, instead of the “string+pointer” overload of RegisterFunction(...), you need to create a NativeFunction object, mark it latent, and hand that to the registry.
Below is exactly how you would change your registration code in plugin.cpp so that TakePhoto becomes a latent Papyrus call:
#include "PCH.h" #include "SKSE/PapyrusNativeFunctions.h" using namespace RE; // for BSFixedString, StaticFunctionTag, VMClassRegistry, etc. static bool Papyrus_TakePhoto( StaticFunctionTag*, BSFixedString basePath, BSFixedString imageType, float GIF_Duration, float Compression, BSFixedString DDS_Mode ) { // … your existing capture logic here … } //--------------------------------------------------------------------------- // Replace your old RegisterPapyrusFunctions(...) with this: static bool RegisterPapyrusFunctions(VMClassRegistry* registry) { // 1) Non‐latent functions can still use the simple form if you like: registry->RegisterFunction( new BSScript::NativeFunction<StaticFunctionTag, BSFixedString, BSFixedString>( "CheckPath", "Printscreen_Formula_script", CheckPath, registry ) ); // 2) **Latent** TakePhoto: use NativeFunction and call SetLatent() registry->RegisterFunction( new BSScript::NativeFunction<StaticFunctionTag, bool, BSFixedString, BSFixedString, float, float, BSFixedString>( "TakePhoto", // Papyrus name "Printscreen_Formula_script", // Papyrus script name Papyrus_TakePhoto, // C++ function pointer registry // VMClassRegistry* )->SetLatent() // ← marks it as latent ); // 3) Any other non‐latent registrations: registry->RegisterFunction( new BSScript::NativeFunction<StaticFunctionTag, BSFixedString>( "GetResult", "Printscreen_Formula_script", GetResult, registry ) ); registry->RegisterFunction( new BSScript::NativeFunction<StaticFunctionTag, BSFixedString>( "Cancel", "Printscreen_Formula_script", Cancel, registry ) ); return true; }
Explanation
-
BSScript::NativeFunction<Owner, Return, Args...>
This template (located inPapyrusNativeFunctions.h) constructs a new Papyrus‐callable function. The template parameters break down as:-
StaticFunctionTag⇢ “Owner” type (all of your functions are static, soStaticFunctionTag) -
bool⇢ return type (forTakePhoto—you wantboolback in Papyrus) -
BSFixedString, BSFixedString, float, float, BSFixedString
⇢ the Papyrus‐side parameter types, in exactly the same order you declared them.
-
-
->SetLatent()
By default, aNativeFunctionis treated like a normal Papyrus call (i.e. Papyrus will block until it returns). Calling.SetLatent()switches it into latent mode, which means:-
Papyrus will not freeze the game while your C++ code is running.
-
From Papyrus’s perspective,
TakePhoto(...)now returns immediately but continues “behind the scenes.” Once your C++ side finishes, you fire a “completion” event back to Papyrus (viaregistry->SetFunctionReturnValue()or by firing a mod‐event).
-
-
Why the “simple”
RegisterFunction("TakePhoto", …, Papyrus_TakePhoto)was wrong
That overload is only intended for non‐latent functions. Internally, it wraps your C++ pointer inside a non‐latentNativeFunctionwrapper. There is no way for Papyrus to know “this one should be latent” unless you explicitly use theNativeFunction<…>constructor and callSetLatent()on it.
What you must do in your code
-
Include
#include "SKSE/PapyrusNativeFunctions.h"(That header defines
RE::BSScript::NativeFunction<…>and ensures thatBSFixedString,StaticFunctionTag,VMClassRegistry, etc., are all visible.) -
Replace your old, simple registrations:
registry->RegisterFunction("TakePhoto", "Printscreen_Formula_script", Papyrus_TakePhoto);with the
new NativeFunction<…>( … )->SetLatent()form shown above. -
Keep your other non‐latent functions (like
CheckPath,GetResult, andCancel) either in the “simple” form or in aNativeFunction<…>form withoutSetLatent(). It works either way. Generally, if you don’t need any special flags, you can still write:registry->RegisterFunction( new BSScript::NativeFunction<StaticFunctionTag, BSFixedString, BSFixedString>( "CheckPath", "Printscreen_Formula_script", CheckPath, registry ) );or even
registry->RegisterFunction("CheckPath", "Printscreen_Formula_script", CheckPath);but for latent behavior you must use
new NativeFunction<…>(…)->SetLatent().
In summary:
-
Yes, you were “missing” the
SetLatent()call. -
Concretely, you cannot register a latent function simply by name + pointer; you must wrap it in a
BSScript::NativeFunction<…>and then callSetLatent()before handing it toregistry->RegisterFunction(…). -
Once you do that, Papyrus’s
TakePhoto(...)call will be treated as latent, allowing Skyrim to keep running until your capture‐thread finishes and signals back to Papyrus.