forked from ciathyza/AlternateConversationCamera
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathSmoothCamAPI.h
375 lines (330 loc) · 13.9 KB
/
SmoothCamAPI.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
#pragma once
#include <functional>
#include <stdint.h>
#if !defined(SMOOTHCAM_API_SKSE) && !defined(SMOOTHCAM_API_COMMONLIB)
static_assert(0, "SmoothCamAPI: SKSE API type not defined. Define one of (SMOOTHCAM_API_SKSE, SMOOTHCAM_API_COMMONLIB)")
#endif
#if defined(SMOOTHCAM_API_SKSE) && defined(SMOOTHCAM_API_COMMONLIB)
static_assert(0, "SmoothCamAPI: Define one of (SMOOTHCAM_API_SKSE, SMOOTHCAM_API_COMMONLIB), not both.")
#endif
#ifdef SMOOTHCAM_API_SKSE
#define SMOOTHCAM_API_LOGGER _MESSAGE
#elif defined(SMOOTHCAM_API_COMMONLIB)
#define SMOOTHCAM_API_LOGGER SKSE::log::info
#endif
/*
* For modders: Copy this file into your own project if you wish to use this API
*
* #define SMOOTHCAM_API_SKSE if using SKSE
* #define SMOOTHCAM_API_COMMONLIB if using CommonLibSSE
*/
namespace SmoothCamAPI {
constexpr const auto SmoothCamPluginName = "SmoothCam";
#ifdef SMOOTHCAM_API_COMMONLIB
using PluginHandle = SKSE::PluginHandle;
using Actor = RE::Actor;
using TESObjectREFR = RE::TESObjectREFR;
using NiCamera = RE::NiCamera;
using NiPoint3 = RE::NiPoint3;
#endif
// Available SmoothCam interface versions
enum class InterfaceVersion : uint8_t {
V1,
V2
};
// Error types that may be returned by the SmoothCam API
enum class APIResult : uint8_t {
// Your API call was successful
OK,
// You tried to release a resource that was not allocated to you
// Do not attempt to manipulate the requested resource if you receive this response
NotOwner,
// SmoothCam currently must keep control of this resource for proper functionality
// Do not attempt to manipulate the requested resource if you receive this response
MustKeep,
// You have already been given control of this resource
AlreadyGiven,
// Another mod has been given control of this resource at the present time
// Do not attempt to manipulate the requested resource if you receive this response
AlreadyTaken,
// You sent a command on a thread that could cause a data race were it to be processed
// Do not attempt to manipulate the requested resource if you receive this response
BadThread,
};
// SmoothCam's modder interface
class IVSmoothCam1 {
public:
/// <summary>
/// Get the thread ID SmoothCam is running in.
/// You may compare this with the result of GetCurrentThreadId() to help determine
/// if you are using the correct thread.
/// </summary>
/// <returns>TID</returns>
[[nodiscard]] virtual unsigned long GetSmoothCamThreadId() const noexcept = 0;
/// <summary>
/// Request control of the player camera.
/// If granted, you may manipulate the camera in whatever ways you wish for the duration of your control.
/// SmoothCam will not perform any positional clean-up of the camera or camera states, you get the camera as-is.
/// </summary>
/// <param name="myPluginHandle">Your assigned plugin handle</param>
/// <returns>OK, MustKeep, AlreadyGiven, AlreadyTaken</returns>
[[nodiscard]] virtual APIResult RequestCameraControl(PluginHandle myPluginHandle) noexcept = 0;
/// <summary>
/// Request control of the HUD crosshair.
/// If enabled, SmoothCam will hide the world-space crosshair for the duration of your control
/// </summary>
/// <param name="myPluginHandle">Your assigned plugin handle</param>
/// <param name="restoreDefaults">SmoothCam will first restore the crosshair to default settings</param>
/// <returns>OK, MustKeep, AlreadyGiven, AlreadyTaken</returns>
[[nodiscard]] virtual APIResult RequestCrosshairControl(PluginHandle myPluginHandle,
bool restoreDefaults = true) noexcept = 0;
/// <summary>
/// Request control of the HUD stealth meter.
/// If granted, you may maniuplate the resource in whatever ways you wish for the duration of your control.
/// </summary>
/// <param name="myPluginHandle">Your assigned plugin handle</param>
/// <param name="restoreDefaults">SmoothCam will first restore the stealth meter to default settings</param>
/// <returns>OK, MustKeep, AlreadyGiven, AlreadyTaken</returns>
[[nodiscard]] virtual APIResult RequestStealthMeterControl(PluginHandle myPluginHandle,
bool restoreDefaults = true) noexcept = 0;
/// <summary>
/// Returns the current owner of the camera resource
/// </summary>
/// <returns>Handle or kPluginHandle_Invalid if no one currently owns the resource</returns>
virtual PluginHandle GetCameraOwner() const noexcept = 0;
/// <summary>
/// Returns the current owner of the crosshair resource
/// </summary>
/// <returns>Handle or kPluginHandle_Invalid if no one currently owns the resource</returns>
virtual PluginHandle GetCrosshairOwner() const noexcept = 0;
/// <summary>
/// Returns the current owner of the stealth meter resource
/// </summary>
/// <returns>Handle or kPluginHandle_Invalid if no one currently owns the resource</returns>
virtual PluginHandle GetStealthMeterOwner() const noexcept = 0;
/// <summary>
/// Release your control of the player camera.
/// </summary>
/// <param name="myPluginHandle">Your assigned plugin handle</param>
/// <returns>OK, NotOwner</returns>
virtual APIResult ReleaseCameraControl(PluginHandle myPluginHandle) noexcept = 0;
/// <summary>
/// Release your control of the crosshair.
/// </summary>
/// <param name="myPluginHandle">Your assigned plugin handle</param>
/// <returns>OK, NotOwner</returns>
virtual APIResult ReleaseCrosshairControl(PluginHandle myPluginHandle) noexcept = 0;
/// <summary>
/// Release your control of the stealth meter.
/// </summary>
/// <param name="myPluginHandle">Your assigned plugin handle</param>
/// <returns>OK, NotOwner</returns>
virtual APIResult ReleaseStealthMeterControl(PluginHandle myPluginHandle) noexcept = 0;
};
class IVSmoothCam2 : public IVSmoothCam1 {
public:
/// <summary>
/// Get the last world position of the camera set by SmoothCam.
/// </summary>
/// <returns>World position</returns>
virtual NiPoint3 GetLastCameraPosition() const noexcept = 0;
/// <summary>
/// Request that position interpolators continue to update while you have camera control.
/// SmoothCam will not update the camera position with interpolators running, but will enable functions like
/// `GetLastCameraPosition` to return current, up to date values while you own the camera resource.
///
/// When given control of the camera, the default action is to pause interpolator updates. If you want them
/// to continue running, you must call this method each time you gain control of the camera.
/// </summary>
/// <param name="myPluginHandle">Your assigned plugin handle</param>
/// <param name="allowUpdates">Allow interpolators to continue updates in the background</param>
/// <returns>OK, NotOwner</returns>
virtual APIResult RequestInterpolatorUpdates(PluginHandle myPluginHandle, bool allowUpdates) noexcept = 0;
/// <summary>
/// When called before releasing camera control, instructs the camera to move to its goal position once control
/// is returned to SmoothCam.
/// </summary>
/// <param name="myPluginHandle">Your assigned plugin handle</param>
/// <param name="shouldMoveToGoal">Instructs the camera to move to it's goal position when control is gained back</param>
/// <param name="moveNow">Move to the current goal immediately</param>
/// <param name="ref">Object reference for rotation calculations</param>
/// <returns>OK, NotOwner</returns>
virtual APIResult SendToGoalPosition(PluginHandle myPluginHandle, bool shouldMoveToGoal, bool moveNow = false,
const Actor* ref = nullptr) noexcept = 0;
/// <summary>
/// Return SmoothCam's current goal position.
/// </summary>
/// <param name="ref">Object reference for rotation calculations</param>
/// <param name="world">World goal position</param>
/// <param name="local">Player local goal position</param>
/// <returns></returns>
virtual void GetGoalPosition(TESObjectREFR* ref, NiPoint3& world, NiPoint3& local) const noexcept = 0;
/// <summary>
/// Returns false if the user has disabled SmoothCam via the MCM/hotkey.
/// When in a disabled state, SmoothCam will not update position information.
/// </summary>
/// <returns>Enabled</returns>
virtual bool IsCameraEnabled() const noexcept = 0;
};
struct PluginCommand {
// Command types available
enum class Type : uint8_t {
RequestInterface,
};
// Packet header
uint32_t header = 0x9007CA50;
// Command type to invoke
Type type;
// Pointer to data for the given command
void* commandStructure = nullptr;
};
struct InterfaceRequest {
// Version to request
InterfaceVersion interfaceVersion;
};
struct PluginResponse {
enum class Type : uint8_t {
Error,
InterfaceProvider,
};
// Response type
Type type;
// Pointer to data for the given resposne
void* responseData = nullptr;
};
struct InterfaceContainer {
// Pointer to interface
void* interfaceInstance = nullptr;
// Contained version
InterfaceVersion interfaceVersion;
};
using InterfaceLoaderCallback = std::function<void(
void* interfaceInstance, InterfaceVersion interfaceVersion
)>;
#ifdef SMOOTHCAM_API_SKSE
/// <summary>
/// Initiate a request for the SmoothCam API interface via SKSE's messaging system.
/// You must register a callback to obtain the response to this request.
/// Recommended: Send your request during SKSEMessagingInterface::kMessage_PostPostLoad
/// </summary>
/// <param name="skseMessaging">SKSE's messaging interface</param>
/// <param name="myPluginHandle">Your assigned plugin handle</param>
/// <param name="version">The interface version to request</param>
/// <returns>If any plugin was listening for this request, true. See skse/PluginAPI.h</returns>
[[nodiscard]]
inline bool RequestInterface(SKSEMessagingInterface* skseMessaging, PluginHandle myPluginHandle,
InterfaceVersion version = InterfaceVersion::V2) noexcept
{
InterfaceRequest req = {};
req.interfaceVersion = version;
PluginCommand cmd = {};
cmd.type = PluginCommand::Type::RequestInterface;
cmd.commandStructure = &req;
return skseMessaging->Dispatch(
myPluginHandle,
0,
&cmd, sizeof(PluginCommand),
SmoothCamPluginName
);
};
/// <summary>
/// Register the callback for obtaining the SmoothCam API interface. Call only once.
/// Recommended: Register your callback during SKSEMessagingInterface::kMessage_PostLoad
/// </summary>
/// <param name="skseMessaging">SKSE's messaging interface</param>
/// <param name="myPluginHandle">Your assigned plugin handle</param>
/// <param name="callback">A callback function receiving both the interface pointer and interface version</param>
/// <returns></returns>
[[nodiscard]]
inline bool RegisterInterfaceLoaderCallback(SKSEMessagingInterface* skseMessaging,
PluginHandle myPluginHandle, InterfaceLoaderCallback&& callback) noexcept
{
static InterfaceLoaderCallback storedCallback = callback;
return skseMessaging->RegisterListener(
myPluginHandle, SmoothCamPluginName,
[](SKSEMessagingInterface::Message* msg) {
if (msg->sender && strcmp(msg->sender, SmoothCamPluginName) != 0) return;
if (msg->type != 0) return;
if (msg->dataLen != sizeof(PluginResponse)) return;
const auto resp = reinterpret_cast<PluginResponse*>(msg->data);
switch (resp->type) {
case PluginResponse::Type::InterfaceProvider: {
auto interfaceContainer = reinterpret_cast<InterfaceContainer*>(resp->responseData);
storedCallback(
interfaceContainer->interfaceInstance,
interfaceContainer->interfaceVersion
);
break;
}
case PluginResponse::Type::Error: {
SMOOTHCAM_API_LOGGER("SmoothCam API: Error obtaining interface");
break;
}
default: return;
}
}
);
}
#elif defined(SMOOTHCAM_API_COMMONLIB)
/// <summary>
/// Initiate a request for the SmoothCam API interface via SKSE's messaging system.
/// You must register a callback to obtain the response to this request.
/// Recommended: Send your request during SKSEMessagingInterface::kMessage_PostPostLoad
/// </summary>
/// <param name="skseMessaging">SKSE's messaging interface</param>
/// <param name="version">The interface version to request</param>
/// <returns>If any plugin was listening for this request, true. See skse/PluginAPI.h</returns>
[[nodiscard]]
inline bool RequestInterface(const SKSE::MessagingInterface* skseMessaging,
InterfaceVersion version = InterfaceVersion::V2) noexcept
{
InterfaceRequest req = {};
req.interfaceVersion = version;
PluginCommand cmd = {};
cmd.type = PluginCommand::Type::RequestInterface;
cmd.commandStructure = &req;
return skseMessaging->Dispatch(
0,
&cmd, sizeof(PluginCommand),
SmoothCamPluginName
);
}
/// <summary>
/// Register the callback for obtaining the SmoothCam API interface. Call only once.
/// Recommended: Register your callback during SKSEMessagingInterface::kMessage_PostLoad
/// </summary>
/// <param name="skseMessaging">SKSE's messaging interface</param>
/// <param name="callback">A callback function receiving both the interface pointer and interface version</param>
/// <returns></returns>
[[nodiscard]]
inline bool RegisterInterfaceLoaderCallback(const SKSE::MessagingInterface* skseMessaging,
InterfaceLoaderCallback&& callback) noexcept
{
static InterfaceLoaderCallback storedCallback = callback;
return skseMessaging->RegisterListener(
SmoothCamPluginName,
[](SKSE::MessagingInterface::Message* msg) {
if (msg->sender && strcmp(msg->sender, SmoothCamPluginName) != 0) return;
if (msg->type != 0) return;
if (msg->dataLen != sizeof(PluginResponse)) return;
const auto resp = reinterpret_cast<PluginResponse*>(msg->data);
switch (resp->type) {
case PluginResponse::Type::InterfaceProvider: {
auto interfaceContainer = reinterpret_cast<InterfaceContainer*>(resp->responseData);
storedCallback(
interfaceContainer->interfaceInstance,
interfaceContainer->interfaceVersion
);
break;
}
case PluginResponse::Type::Error: {
SMOOTHCAM_API_LOGGER("SmoothCam API: Error obtaining interface");
break;
}
default: return;
}
}
);
}
#endif
}