From d636a7af771f62a414e0cf2951dbf0c1d7fbea55 Mon Sep 17 00:00:00 2001 From: ozooma10 <98544147+ozooma10@users.noreply.github.com> Date: Wed, 10 Jun 2026 18:05:42 -0400 Subject: [PATCH 1/7] fix: GameVM Layout --- include/RE/G/GameVM.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/include/RE/G/GameVM.h b/include/RE/G/GameVM.h index 68fe251..b3fe7bd 100644 --- a/include/RE/G/GameVM.h +++ b/include/RE/G/GameVM.h @@ -184,10 +184,9 @@ namespace RE std::uint64_t unkC8; // 00C8 std::uint64_t unkD0; // 00D0 std::uint64_t unkD8; // 00D8 - std::uint64_t unkE0; // 00E0 - BSTSmartPointer impl; // 00E8 - BSScript::IVMSaveLoadInterface* saveLoadInterface; // 00F0 - BSScript::IVMDebugInterface* debugInterface; // 00F8 + BSTSmartPointer impl; // 00E0 + BSScript::IVMSaveLoadInterface* saveLoadInterface; // 00E8 + BSScript::IVMDebugInterface* debugInterface; // 00F0 BSScript::SimpleAllocMemoryPagePolicy memoryPagePolicy; // 0128 BSScript::CompiledScriptLoader scriptLoader; // 0160 GameScript::Logger logger; // 01E8 @@ -229,5 +228,5 @@ namespace RE std::uint32_t lastWarningTime; // 8970 std::uint32_t overflowFlags; // 8978 }; - static_assert(sizeof(GameVM) == 0x8980); + static_assert(sizeof(GameVM) == 0x8978); } From 33868be4b525ff653c25e7a7e02cebf0fb076bb2 Mon Sep 17 00:00:00 2001 From: ozooma10 <98544147+ozooma10@users.noreply.github.com> Date: Wed, 10 Jun 2026 16:52:05 -0400 Subject: [PATCH 2/7] fix: implement ScrapHeap::GetThreadScrapHeap --- include/RE/IDs.h | 4 +++- include/RE/M/MemoryManager.h | 21 ++++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/include/RE/IDs.h b/include/RE/IDs.h index 6c5b6ef..cbe0dfa 100644 --- a/include/RE/IDs.h +++ b/include/RE/IDs.h @@ -1165,7 +1165,9 @@ namespace RE::ID inline constexpr REL::ID GetSingleton{ 35721 }; inline constexpr REL::ID Allocate{ 123792 }; inline constexpr REL::ID Free{ 123793 }; - inline constexpr REL::ID GetThreadScrapHeap{ 0 }; // 36848 + // 1.16.242: no standalone GetThreadScrapHeap exists; the TLS getter is inlined engine-wide + inline constexpr REL::ID CreateThreadScrapHeap{ 123806 }; + inline constexpr REL::ID ScrapHeapTlsIndex{ 952578 }; } namespace MenuTopicManager diff --git a/include/RE/M/MemoryManager.h b/include/RE/M/MemoryManager.h index 36e2f2d..b7d4abe 100644 --- a/include/RE/M/MemoryManager.h +++ b/include/RE/M/MemoryManager.h @@ -1,5 +1,7 @@ #pragma once +#include "REX/W32/KERNEL32.h" + namespace RE { class ScrapHeap; @@ -63,11 +65,24 @@ namespace RE ScrapHeap* GetThreadScrapHeap() { - using func_t = decltype(&MemoryManager::GetThreadScrapHeap); - static const REL::Relocation func{ ID::MemoryManager::GetThreadScrapHeap }; - return func(this); + // 1.16.242 has no standalone engine function for this; the getter is inlined at every engine call site as: + // heap = TlsGetValue(g_scrapHeapTlsIndex); + // if (!heap) { heap = CreateThreadScrapHeap(&singleton->threadScrapHeaps); TlsSetValue(idx, heap); } + static const REL::Relocation tlsIndex{ ID::MemoryManager::ScrapHeapTlsIndex }; + auto heap = static_cast(REX::W32::TlsGetValue(*tlsIndex)); + if (!heap) { + using create_t = ScrapHeap* (*)(ScrapHeap**); + static const REL::Relocation create{ ID::MemoryManager::CreateThreadScrapHeap }; + heap = create(&threadScrapHeaps); + REX::W32::TlsSetValue(*tlsIndex, heap); + } + return heap; } + + std::byte pad000[0x460]; // 000 + ScrapHeap* threadScrapHeaps; // 460 }; + static_assert(offsetof(MemoryManager, threadScrapHeaps) == 0x460); [[nodiscard]] inline void* malloc(std::size_t a_size, std::size_t a_alignment = 0) { From 591d832de2cf96fb05a67f6fa82fa87eb6b0082c Mon Sep 17 00:00:00 2001 From: ozooma10 <98544147+ozooma10@users.noreply.github.com> Date: Wed, 10 Jun 2026 17:19:27 -0400 Subject: [PATCH 3/7] fix: TESObjectREFR offset comments --- include/RE/T/TESObjectREFR.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/RE/T/TESObjectREFR.h b/include/RE/T/TESObjectREFR.h index 5fcad6b..084bb21 100644 --- a/include/RE/T/TESObjectREFR.h +++ b/include/RE/T/TESObjectREFR.h @@ -134,15 +134,15 @@ namespace RE class TESObjectREFR : public TESHandleForm, // 00 - public BSTEventSink, // 30 - public IMovementProcessMessageInterface, // 38 - public IPostAnimationChannelUpdateFunctor, // 40 - public BSTEventSink, // 48 - public BSTEventSink, // 50 - public IAnimationGraphManagerHolder, // 58 - public IKeywordFormBase, // 60 - public ActorValueOwner, // 68 - public BSTEventSourceLazyInit // 70 + public BSTEventSink, // 38 + public IMovementProcessMessageInterface, // 40 + public IPostAnimationChannelUpdateFunctor, // 48 + public BSTEventSink, // 50 + public BSTEventSink, // 58 + public IAnimationGraphManagerHolder, // 60 + public IKeywordFormBase, // 68 + public ActorValueOwner, // 70 + public BSTEventSourceLazyInit // 78 { public: SF_RTTI_VTABLE(TESObjectREFR); From 78c630c2740028df3ac97f446fb36b672040d24a Mon Sep 17 00:00:00 2001 From: ozooma10 <98544147+ozooma10@users.noreply.github.com> Date: Wed, 10 Jun 2026 17:18:56 -0400 Subject: [PATCH 4/7] BSScriptUtil - array overload treats null array as empty instead of assert --- include/RE/B/BSScriptUtil.h | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/include/RE/B/BSScriptUtil.h b/include/RE/B/BSScriptUtil.h index 1b5a982..cdef074 100644 --- a/include/RE/B/BSScriptUtil.h +++ b/include/RE/B/BSScriptUtil.h @@ -773,7 +773,7 @@ namespace RE::BSScript [[nodiscard]] T UnpackVariable(const Variable& a_var) { if (!a_var.is()) { - assert(false); + assert(a_var.GetType().IsArray()); return T(); } @@ -797,10 +797,16 @@ namespace RE::BSScript { if (a_var.is()) { return T(); - } else { - using value_type = typename T::value_type; - return T(detail::UnpackVariable(a_var)); } + + if constexpr (detail::array) { + if (a_var.GetType().IsArray() && !a_var.is()) { + return T(); + } + } + + using value_type = typename T::value_type; + return T(detail::UnpackVariable(a_var)); } namespace detail From a21a1ed1b44487f919dc7127325634a53009dcd7 Mon Sep 17 00:00:00 2001 From: ozooma10 <98544147+ozooma10@users.noreply.github.com> Date: Wed, 10 Jun 2026 17:17:52 -0400 Subject: [PATCH 5/7] Actor.h - add kAnimationDriven flag and define movementController --- include/RE/A/Actor.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/RE/A/Actor.h b/include/RE/A/Actor.h index 6faf3ec..63dd0a7 100644 --- a/include/RE/A/Actor.h +++ b/include/RE/A/Actor.h @@ -16,6 +16,7 @@ namespace RE class BGSPerk; class CombatController; class CombatGroup; + class MovementControllerNPC; class MovementMessageUpdateRequestImmediate; class TESFaction; class TESRace; @@ -163,6 +164,7 @@ namespace RE { kNone = 0, kHasChargenSkeleton = 1 << 5, + kAnimationDriven = 1 << 19, }; ~Actor() override; // 00 @@ -388,7 +390,7 @@ namespace RE AIProcess* currentProcess; // 228 std::uint64_t unk230; // 230 std::uint64_t unk238; // 238 - std::uint64_t unk240; // 240 + MovementControllerNPC* movementController; // 240 std::uint64_t unk248; // 248 CombatController* combatController; // 250 std::uint64_t unk258; // 258 From 9630f655fc5f10b0e99b3a8d8d1777c472ac55cb Mon Sep 17 00:00:00 2001 From: ozooma10 <98544147+ozooma10@users.noreply.github.com> Date: Wed, 10 Jun 2026 19:43:02 -0400 Subject: [PATCH 6/7] feat: add ModelNode family, BSAnimationUpdateData, and AnimationManager member layout Runtime-validated via AnimationHeaders guard suite (pass=4/4). --- include/RE/B/BGSAnimationPathImplementation.h | 26 ++++ include/RE/B/BGSModelNode.h | 62 ++++++++ include/RE/B/BSAnimationGraph.h | 132 +++++++++++------- include/RE/B/BSAnimationUpdateData.h | 40 ++++++ include/RE/B/BSFadeNode.h | 22 +++ include/RE/B/BSModelNode.h | 24 ++++ 6 files changed, 254 insertions(+), 52 deletions(-) create mode 100644 include/RE/B/BGSAnimationPathImplementation.h create mode 100644 include/RE/B/BGSModelNode.h create mode 100644 include/RE/B/BSAnimationUpdateData.h create mode 100644 include/RE/B/BSFadeNode.h create mode 100644 include/RE/B/BSModelNode.h diff --git a/include/RE/B/BGSAnimationPathImplementation.h b/include/RE/B/BGSAnimationPathImplementation.h new file mode 100644 index 0000000..5751def --- /dev/null +++ b/include/RE/B/BGSAnimationPathImplementation.h @@ -0,0 +1,26 @@ +#pragma once + +namespace RE +{ + class TESObjectREFR; + + class AnimationPathInterface + { + public: + SF_RTTI(AnimationPathInterface); + + virtual ~AnimationPathInterface(); // 00 + }; + static_assert(sizeof(AnimationPathInterface) == 0x8); + + class BGSAnimationPathImplementation : + public AnimationPathInterface // 00 + { + public: + SF_RTTI_VTABLE(BGSAnimationPathImplementation); + + // members + TESObjectREFR* reference; // 08 + }; + static_assert(offsetof(BGSAnimationPathImplementation, reference) == 0x08); +} diff --git a/include/RE/B/BGSModelNode.h b/include/RE/B/BGSModelNode.h new file mode 100644 index 0000000..f32e557 --- /dev/null +++ b/include/RE/B/BGSModelNode.h @@ -0,0 +1,62 @@ +#pragma once + +#include "RE/B/BSModelNode.h" +#include "RE/B/BSTArray.h" +#include "RE/N/NiTransform.h" + +namespace RE +{ + class NiAVObject; + + class BGSModelNode : + public BSModelNode // 00 + { + public: + SF_RTTI_VTABLE(BGSModelNode); + + struct Rig + { + struct Buffer + { + // members + void* unk00; // 00 + void* unk08; // 08 + NiTransform* data; // 10 - one slot per rig bone + }; + + // members + std::uint32_t refCount; // 00 + std::uint32_t unk04; // 04 + Buffer* local; // 08 + Buffer* world; // 10 + Buffer* prevWorld; // 18 + void* unk20; // 20 + std::uint16_t* parents; // 28 + void* unk30; // 30 + std::uint16_t* order; // 38 + std::byte unk40[0x48]; // 40 + bool rebuild; // 88 + }; + static_assert(offsetof(Rig, rebuild) == 0x88); + + struct NodeEntry + { + // members + std::uint16_t rigIndex; // 00 + std::uint16_t unk02; // 02 + std::uint16_t unk04; // 04 + std::uint16_t unk06; // 06 + NiAVObject* node; // 08 + }; + static_assert(sizeof(NodeEntry) == 0x10); + + ~BGSModelNode() override; // 00 + + // members + Rig* rig; // 10 + BSTArray nodes; // 18 + std::byte unk28[0x68]; // 28 + }; + static_assert(offsetof(BGSModelNode, nodes) == 0x18); + static_assert(sizeof(BGSModelNode) == 0x90); +} diff --git a/include/RE/B/BSAnimationGraph.h b/include/RE/B/BSAnimationGraph.h index 567afb7..6993a1a 100644 --- a/include/RE/B/BSAnimationGraph.h +++ b/include/RE/B/BSAnimationGraph.h @@ -7,77 +7,105 @@ namespace RE { + class BGSAnimationPathImplementation; class BSAnimationGraphEvent; + class BSFadeNode; struct BSMovementDataChangedEvent; struct BSSubGraphActivationUpdate; class BSTransformDeltaEvent; class BSAnimationUpdateData; + class IPostAnimationChannelUpdateFunctor; + class NiTransform; class BSAnimationGraph : - public BSIntrusiveRefCounted, - public BSTEventSource, - public BSTEventSource, - public BSTEventSource, - public BSTEventSource + public BSTEventSource, // 00 + public BSTEventSource, // 28 + public BSTEventSource, // 50 + public BSTEventSource, // 78 + public BSIntrusiveRefCounted // A0 { public: virtual ~BSAnimationGraph(); - virtual void Unk_01(); // 01 - virtual void Unk_02(); // 02 - virtual void Unk_03(); // 03 - virtual void Update(BSAnimationUpdateData& a_updateData); // 04 - virtual void Unk_05(); // 05 - virtual void Unk_06(); // 06 - virtual void Unk_07(); // 07 - virtual void Unk_08(); // 08 - virtual void Unk_09(); // 09 - virtual void Unk_0A(); // 0A - virtual void Unk_0B(); // 0B - virtual void Unk_0C(); // 0C - virtual void Unk_0D(); // 0D - virtual void Unk_0E(); // 0E - virtual void Unk_0F(); // 0F - virtual void Unk_10(); // 10 - virtual void Unk_11(); // 11 - virtual void Unk_12(); // 12 - virtual void Unk_13(); // 13 - virtual void Unk_14(); // 14 - virtual void Unk_15(); // 15 - virtual void Unk_16(); // 16 - virtual void Unk_17(); // 17 - virtual void Unk_18(); // 18 - virtual void Unk_19(); // 19 - virtual void Unk_1A(); // 1A - virtual void Unk_1B(); // 1B - virtual void Unk_1C(); // 1C - virtual void Unk_1D(); // 1D - virtual void Unk_1E(); // 1E - virtual void Unk_1F(); // 1F - virtual void Unk_20(); // 20 - virtual void Unk_21(); // 21 - virtual void Unk_22(); // 22 - virtual void Unk_23(); // 23 - virtual void Unk_24(); // 24 - virtual void Unk_25(); // 25 - virtual void Unk_26(); // 26 - virtual void Unk_27(); // 27 - virtual void Unk_28(); // 28 - virtual void Unk_29(); // 29 - virtual void Unk_2A(); // 2A - virtual void Unk_2B(); // 2B - virtual void Unk_2C(); // 2C + virtual void Unk_01(); // 01 + virtual void Unk_02(); // 02 + virtual void Unk_03(); // 03 + virtual void Update(BSAnimationUpdateData& a_updateData); // 04 + virtual void Unk_05(); // 05 + virtual void Unk_06(); // 06 + virtual void GenerateOutputPose(BSAnimationUpdateData& a_updateData, void* a_eventListOut); // 07 + virtual void Unk_08(); // 08 + virtual void Unk_09(); // 09 + virtual void Unk_0A(); // 0A + virtual void Unk_0B(); // 0B + virtual void Unk_0C(); // 0C + virtual void Unk_0D(); // 0D + virtual void Unk_0E(); // 0E + virtual void Unk_0F(); // 0F + virtual void Unk_10(); // 10 + virtual void Unk_11(); // 11 + virtual void Unk_12(); // 12 + virtual void Unk_13(); // 13 + virtual void Unk_14(); // 14 + virtual void Unk_15(); // 15 + virtual void Unk_16(); // 16 + virtual void Unk_17(); // 17 + virtual void Unk_18(); // 18 + virtual void Unk_19(); // 19 + virtual void Unk_1A(); // 1A + virtual void Unk_1B(); // 1B + virtual void Unk_1C(); // 1C + virtual void Unk_1D(); // 1D + virtual void Unk_1E(); // 1E + virtual void Unk_1F(); // 1F + virtual void Unk_20(); // 20 + virtual void Unk_21(); // 21 + virtual void Unk_22(); // 22 + virtual void Unk_23(); // 23 + virtual void Unk_24(); // 24 + virtual void Unk_25(); // 25 + virtual void Unk_26(); // 26 + virtual void Unk_27(); // 27 + virtual void Unk_28(); // 28 + virtual void Unk_29(); // 29 + virtual void Unk_2A(); // 2A + virtual void Unk_2B(); // 2B + virtual void Unk_2C(); // 2C }; + static_assert(sizeof(BSAnimationGraph) == 0xA8); + class AnimationManager : public BSAnimationGraph { public: virtual ~AnimationManager(); - std::byte unkB0[0x3C0 - 0xB0]; // B0 + // members + std::byte unkA8[0x218]; // A8 + BGSAnimationPathImplementation* animationPath; // 2C0 + std::uint64_t unk2C8; // 2C8 + IPostAnimationChannelUpdateFunctor* postUpdateFunctor; // 2D0 + float sampleQuantum; // 2D8 + std::uint32_t unk2DC; // 2DC + std::uint32_t unk2E0; // 2E0 + std::uint32_t unk2E4; // 2E4 + std::uint32_t prevPoseCount; // 2E8 + std::uint32_t unk2EC; // 2EC + NiTransform* prevPoseSnapshot; // 2F0 + std::uint32_t currPoseCount; // 2F8 + std::uint32_t unk2FC; // 2FC + NiTransform* currPoseSnapshot; // 300 + std::byte unk308[0x28]; // 308 + BSFadeNode* rootNode; // 330 + std::byte unk338[0x4C]; // 338 + float lastUpdateDelta; // 384 + std::uint32_t unk388; // 388 + std::uint32_t updateCount; // 38C + std::byte unk390[0x30]; // 390 }; - // FIXME: compiler doesn't recognize vtable pointer as part of the size, but intellisense does. - static_assert(sizeof(AnimationManager) == 0x3C0 - 0x8); + static_assert(offsetof(AnimationManager, animationPath) == 0x2C0); + static_assert(offsetof(AnimationManager, rootNode) == 0x330); + static_assert(sizeof(AnimationManager) == 0x3C0); class BSAnimationGraphManager : public BSTEventSink, diff --git a/include/RE/B/BSAnimationUpdateData.h b/include/RE/B/BSAnimationUpdateData.h new file mode 100644 index 0000000..7bf4232 --- /dev/null +++ b/include/RE/B/BSAnimationUpdateData.h @@ -0,0 +1,40 @@ +#pragma once + +#include "RE/N/NiPoint.h" + +namespace RE +{ + class BGSModelNode; + class IPostAnimationChannelUpdateFunctor; + + class BSAnimationUpdateData + { + public: + // members + NiPoint3A location; // 00 + NiPoint3A rotation; // 10 + NiPoint3A unk20; // 20 + NiPoint3A unk30; // 30 + float unk40; // 40 + float unk44; // 44 + IPostAnimationChannelUpdateFunctor* postUpdateFunctor; // 48 + BGSModelNode* modelNode; // 50 + float unk58; // 58 + float unk5C; // 5C + float timeDelta; // 60 + std::uint16_t unk64; // 64 + std::uint16_t unk66; // 66 + std::uint16_t unk68; // 68 + bool forceUpdate; // 6A + bool modelCulled; // 6B + bool unk6C; // 6C + bool unk6D; // 6D + bool unk6E; // 6E + bool unk6F; // 6F + float unk70; // 70 + }; + static_assert(offsetof(BSAnimationUpdateData, modelNode) == 0x50); + static_assert(offsetof(BSAnimationUpdateData, timeDelta) == 0x60); + static_assert(offsetof(BSAnimationUpdateData, modelCulled) == 0x6B); + static_assert(sizeof(BSAnimationUpdateData) == 0x80); +} diff --git a/include/RE/B/BSFadeNode.h b/include/RE/B/BSFadeNode.h new file mode 100644 index 0000000..73436b9 --- /dev/null +++ b/include/RE/B/BSFadeNode.h @@ -0,0 +1,22 @@ +#pragma once + +#include "RE/B/BGSModelNode.h" +#include "RE/N/NiNode.h" +#include "RE/N/NiSmartPointer.h" + +namespace RE +{ + class BSFadeNode : + public NiNode // 000 + { + public: + SF_RTTI_VTABLE(BSFadeNode); + + // members + std::byte unk160[0x20]; // 160 + NiPointer bgsModelNode; // 180 + std::byte unk188[0x38]; // 188 + }; + static_assert(offsetof(BSFadeNode, bgsModelNode) == 0x180); + static_assert(sizeof(BSFadeNode) == 0x1C0); +} diff --git a/include/RE/B/BSModelNode.h b/include/RE/B/BSModelNode.h new file mode 100644 index 0000000..c68f8f4 --- /dev/null +++ b/include/RE/B/BSModelNode.h @@ -0,0 +1,24 @@ +#pragma once + +#include "RE/B/BSIntrusiveRefCounted.h" + +namespace RE +{ + class NiNode; + class NiTransform; + class NiUpdateData; + + class BSModelNode : + public BSIntrusiveRefCounted // 08 + { + public: + SF_RTTI_VTABLE(BSModelNode); + + virtual ~BSModelNode(); // 00 + + // add + virtual void* Refresh(NiNode* a_rootNode, NiUpdateData* a_updateData); // 01 + virtual bool Update(void* a_data, NiUpdateData* a_updateData, NiTransform* a_transform); // 02 + }; + static_assert(sizeof(BSModelNode) == 0x10); +} From 866c9c9710719b5bda53cdcec3135e1e990a8d3a Mon Sep 17 00:00:00 2001 From: ozooma10 <98544147+ozooma10@users.noreply.github.com> Date: Wed, 10 Jun 2026 19:43:44 -0400 Subject: [PATCH 7/7] feat: add MovementControllerNPC hierarchy and IMovement* interfaces Extracts IMovementProcessMessageInterface from TESObjectREFR.h inline definition to its own header; adds IMovementSelectIdle, IMovementDirectControl, IMovementPlannerDirectControl, IMovementQueryAnimDeltas, IMovementAnimationDrivenFeedback. Adds MovementControllerAI and MovementControllerNPC with confirmed base layout and SetAnimationDriven/SetMotionDriven IDs --- .../RE/I/IMovementAnimationDrivenFeedback.h | 14 +++++++ include/RE/I/IMovementDirectControl.h | 14 +++++++ include/RE/I/IMovementPlannerDirectControl.h | 14 +++++++ .../RE/I/IMovementProcessMessageInterface.h | 17 ++++++++ include/RE/I/IMovementQueryAnimDeltas.h | 14 +++++++ include/RE/I/IMovementSelectIdle.h | 14 +++++++ include/RE/IDs.h | 6 +++ include/RE/M/MovementControllerAI.h | 19 +++++++++ include/RE/M/MovementControllerNPC.h | 41 +++++++++++++++++++ include/RE/T/TESObjectREFR.h | 13 +----- 10 files changed, 154 insertions(+), 12 deletions(-) create mode 100644 include/RE/I/IMovementAnimationDrivenFeedback.h create mode 100644 include/RE/I/IMovementDirectControl.h create mode 100644 include/RE/I/IMovementPlannerDirectControl.h create mode 100644 include/RE/I/IMovementProcessMessageInterface.h create mode 100644 include/RE/I/IMovementQueryAnimDeltas.h create mode 100644 include/RE/I/IMovementSelectIdle.h create mode 100644 include/RE/M/MovementControllerAI.h create mode 100644 include/RE/M/MovementControllerNPC.h diff --git a/include/RE/I/IMovementAnimationDrivenFeedback.h b/include/RE/I/IMovementAnimationDrivenFeedback.h new file mode 100644 index 0000000..7a39b72 --- /dev/null +++ b/include/RE/I/IMovementAnimationDrivenFeedback.h @@ -0,0 +1,14 @@ +#pragma once + +#include "RE/I/IMovementInterface.h" + +namespace RE +{ + struct IMovementAnimationDrivenFeedback : + public IMovementInterface // 00 + { + public: + SF_RTTI(IMovementAnimationDrivenFeedback); + }; + static_assert(sizeof(IMovementAnimationDrivenFeedback) == 0x8); +} diff --git a/include/RE/I/IMovementDirectControl.h b/include/RE/I/IMovementDirectControl.h new file mode 100644 index 0000000..4aa1135 --- /dev/null +++ b/include/RE/I/IMovementDirectControl.h @@ -0,0 +1,14 @@ +#pragma once + +#include "RE/I/IMovementInterface.h" + +namespace RE +{ + struct IMovementDirectControl : + public IMovementInterface // 00 + { + public: + SF_RTTI(IMovementDirectControl); + }; + static_assert(sizeof(IMovementDirectControl) == 0x8); +} diff --git a/include/RE/I/IMovementPlannerDirectControl.h b/include/RE/I/IMovementPlannerDirectControl.h new file mode 100644 index 0000000..a330110 --- /dev/null +++ b/include/RE/I/IMovementPlannerDirectControl.h @@ -0,0 +1,14 @@ +#pragma once + +#include "RE/I/IMovementInterface.h" + +namespace RE +{ + struct IMovementPlannerDirectControl : + public IMovementInterface // 00 + { + public: + SF_RTTI(IMovementPlannerDirectControl); + }; + static_assert(sizeof(IMovementPlannerDirectControl) == 0x8); +} diff --git a/include/RE/I/IMovementProcessMessageInterface.h b/include/RE/I/IMovementProcessMessageInterface.h new file mode 100644 index 0000000..6a5943b --- /dev/null +++ b/include/RE/I/IMovementProcessMessageInterface.h @@ -0,0 +1,17 @@ +#pragma once + +#include "RE/I/IMovementInterface.h" + +namespace RE +{ + class IMovementProcessMessageInterface : + public IMovementInterface // 00 + { + public: + ~IMovementProcessMessageInterface() override; + + // add + virtual void Unk_01(); // 01 + }; + static_assert(sizeof(IMovementProcessMessageInterface) == 0x8); +} diff --git a/include/RE/I/IMovementQueryAnimDeltas.h b/include/RE/I/IMovementQueryAnimDeltas.h new file mode 100644 index 0000000..9ae2a01 --- /dev/null +++ b/include/RE/I/IMovementQueryAnimDeltas.h @@ -0,0 +1,14 @@ +#pragma once + +#include "RE/I/IMovementInterface.h" + +namespace RE +{ + struct IMovementQueryAnimDeltas : + public IMovementInterface // 00 + { + public: + SF_RTTI(IMovementQueryAnimDeltas); + }; + static_assert(sizeof(IMovementQueryAnimDeltas) == 0x8); +} diff --git a/include/RE/I/IMovementSelectIdle.h b/include/RE/I/IMovementSelectIdle.h new file mode 100644 index 0000000..b186046 --- /dev/null +++ b/include/RE/I/IMovementSelectIdle.h @@ -0,0 +1,14 @@ +#pragma once + +#include "RE/I/IMovementInterface.h" + +namespace RE +{ + struct IMovementSelectIdle : + public IMovementInterface // 00 + { + public: + SF_RTTI(IMovementSelectIdle); + }; + static_assert(sizeof(IMovementSelectIdle) == 0x8); +} diff --git a/include/RE/IDs.h b/include/RE/IDs.h index cbe0dfa..0c42e48 100644 --- a/include/RE/IDs.h +++ b/include/RE/IDs.h @@ -1222,6 +1222,12 @@ namespace RE::ID inline constexpr REL::ID GetEventSource{ 0 }; // 139097 } + namespace MovementControllerNPC + { + inline constexpr REL::ID SetAnimationDriven{ 135316 }; + inline constexpr REL::ID SetMotionDriven{ 135315 }; + } + namespace MissionMenu_ShowItemLocation { inline constexpr REL::ID GetEventSource{ 0 }; // 139100 diff --git a/include/RE/M/MovementControllerAI.h b/include/RE/M/MovementControllerAI.h new file mode 100644 index 0000000..b7d9fda --- /dev/null +++ b/include/RE/M/MovementControllerAI.h @@ -0,0 +1,19 @@ +#pragma once + +namespace RE +{ + class MovementControllerAI + { + public: + SF_RTTI_VTABLE(MovementControllerAI); + + virtual ~MovementControllerAI(); // 00 + + // members + std::byte unk08[0x68]; // 08 + std::uint8_t movementMode; // 70 - 1 = animation-driven, 2 = motion-driven + std::byte unk71[0x07]; // 71 + }; + static_assert(offsetof(MovementControllerAI, movementMode) == 0x70); + static_assert(sizeof(MovementControllerAI) == 0x78); +} diff --git a/include/RE/M/MovementControllerNPC.h b/include/RE/M/MovementControllerNPC.h new file mode 100644 index 0000000..b8d94af --- /dev/null +++ b/include/RE/M/MovementControllerNPC.h @@ -0,0 +1,41 @@ +#pragma once + +#include "RE/B/BSIntrusiveRefCounted.h" +#include "RE/I/IMovementAnimationDrivenFeedback.h" +#include "RE/I/IMovementDirectControl.h" +#include "RE/I/IMovementPlannerDirectControl.h" +#include "RE/I/IMovementProcessMessageInterface.h" +#include "RE/I/IMovementQueryAnimDeltas.h" +#include "RE/I/IMovementSelectIdle.h" +#include "RE/M/MovementControllerAI.h" + +namespace RE +{ + class MovementControllerNPC : + public MovementControllerAI, // 00 + public IMovementProcessMessageInterface, // 78 + public IMovementSelectIdle, // 80 + public IMovementDirectControl, // 88 + public IMovementPlannerDirectControl, // 90 + public IMovementQueryAnimDeltas, // 98 + public IMovementAnimationDrivenFeedback, // A0 + public BSIntrusiveRefCounted // A8 + { + public: + SF_RTTI_VTABLE(MovementControllerNPC); + + void SetAnimationDriven() + { + using func_t = decltype(&MovementControllerNPC::SetAnimationDriven); + static REL::Relocation func{ ID::MovementControllerNPC::SetAnimationDriven }; + return func(this); + } + + void SetMotionDriven() + { + using func_t = decltype(&MovementControllerNPC::SetMotionDriven); + static REL::Relocation func{ ID::MovementControllerNPC::SetMotionDriven }; + return func(this); + } + }; +} diff --git a/include/RE/T/TESObjectREFR.h b/include/RE/T/TESObjectREFR.h index 084bb21..1d8bb49 100644 --- a/include/RE/T/TESObjectREFR.h +++ b/include/RE/T/TESObjectREFR.h @@ -11,7 +11,7 @@ #include "RE/E/ExtraDataList.h" #include "RE/I/IAnimationGraphManagerHolder.h" #include "RE/I/IKeywordFormBase.h" -#include "RE/I/IMovementInterface.h" +#include "RE/I/IMovementProcessMessageInterface.h" #include "RE/I/IPostAnimationChannelUpdateFunctor.h" #include "RE/N/NiPoint.h" #include "RE/N/NiSmartPointer.h" @@ -97,17 +97,6 @@ namespace RE kConsumingIngredient }; - class IMovementProcessMessageInterface : - public IMovementInterface // 00 - { - public: - ~IMovementProcessMessageInterface() override; - - // add - virtual void Unk_01(); // 01 - }; - static_assert(sizeof(IMovementProcessMessageInterface) == 0x8); - struct OBJ_REFR { public: