diff --git a/CMakeLists.txt b/CMakeLists.txt index dd69398..1d1c53e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.21) # Set your project name. This will be the name of your SKSE .dll file. -project(SkipEquipAnimation VERSION 1.0.4 LANGUAGES CXX) +project(SkipEquipAnimation VERSION 1.0.9 LANGUAGES CXX) set(ADD_COMMONLIBSSE_PLUGIN_AUTHOR "AnArchos" CACHE STRING "Plugin author") set(ADD_COMMONLIBSSE_PLUGIN_EMAIL "patreon.com/AnArchos" CACHE STRING "Plugin email") diff --git a/SKSE/Plugins/BehaviorDataInjector/SkipEquipAnimation_BDI.json b/SKSE/Plugins/BehaviorDataInjector/SkipEquipAnimation_BDI.json index 77d5fd3..272059a 100644 --- a/SKSE/Plugins/BehaviorDataInjector/SkipEquipAnimation_BDI.json +++ b/SKSE/Plugins/BehaviorDataInjector/SkipEquipAnimation_BDI.json @@ -6,6 +6,13 @@ "value": false }, + { + "projectPath": "actors\\Character", + "type": "kBool", + "name" "InstantEquipAnim", + "value": false + }, + { "projectPath": "actors\\Character", "type": "kInt", @@ -19,4 +26,4 @@ "name": "Skip3DLoading", "value": false } -] \ No newline at end of file +] diff --git a/cmake/headerlist.cmake b/cmake/headerlist.cmake index 58fcb1e..a41db02 100644 --- a/cmake/headerlist.cmake +++ b/cmake/headerlist.cmake @@ -2,6 +2,6 @@ set(headers ${headers} src/PCH.h src/log.h src/util.h + src/hook.h src/event.h - src/hook.h ) \ No newline at end of file diff --git a/cmake/sourcelist.cmake b/cmake/sourcelist.cmake index dd11b4a..5339f15 100644 --- a/cmake/sourcelist.cmake +++ b/cmake/sourcelist.cmake @@ -1,5 +1,5 @@ set(sources ${sources} src/plugin.cpp - src/event.cpp src/hook.cpp + src/event.cpp ) \ No newline at end of file diff --git a/src/PCH.h b/src/PCH.h index de71a02..9d105b6 100644 --- a/src/PCH.h +++ b/src/PCH.h @@ -6,6 +6,5 @@ #define RELOCATION_OFFSET(SE, AE) REL::VariantOffset(SE, AE, 0).offset() #define DEBUG_MODE -//#define WORK_WITH_MAGIC_OBJECTS using namespace std::literals; diff --git a/src/event.cpp b/src/event.cpp index f06e377..8cad370 100644 --- a/src/event.cpp +++ b/src/event.cpp @@ -1,35 +1,46 @@ -#include "event.h" -/**/ -bool AnimationEventTracker::Register() -{ - bool foundEventSource = eventSource != nullptr; - if (!foundEventSource) { - const auto pc = RE::PlayerCharacter::GetSingleton(); - - RE::BSAnimationGraphManagerPtr graphManager; - if (pc) pc->GetAnimationGraphManager(graphManager); - else spdlog::debug("Can't found Player Character"); - - if (graphManager) { - for (auto& animationGraph : graphManager->graphs) { - eventSource = animationGraph->GetEventSource(); - if (eventSource) { - spdlog::info("Registered {}", typeid(RE::BSAnimationGraphEvent).name()); - return true; - } - } - } - - if (!eventSource) { - spdlog::info("Failed to register {}", typeid(RE::BSAnimationGraphEvent).name()); - } - } return foundEventSource; -} - -void AnimationEventTracker::SendAnimationEvent(RE::Actor* a_this, const RE::BSFixedString a_tag, const RE::BSFixedString a_payload) -{ - if (a_this && eventSource) { - RE::BSAnimationGraphEvent event = {a_tag, a_this, a_payload}; - a_this->ProcessEvent(&event, eventSource); - } +#include "event.h" +/**/ +bool AnimationEventTracker::Register() +{ + bool foundEventSource = eventSource != nullptr; + if (!foundEventSource) + { + const auto pc = RE::PlayerCharacter::GetSingleton(); + + RE::BSAnimationGraphManagerPtr graphManager; + if (pc) + pc->GetAnimationGraphManager(graphManager); + else + spdlog::debug("Can't found Player Character"); + + if (graphManager) + { + for (auto &animationGraph : graphManager->graphs) + { + eventSource = animationGraph->GetEventSource(); + if (eventSource) + { + spdlog::info("Registered {}", typeid(RE::BSAnimationGraphEvent).name()); + return true; + } + } + } + + if (!eventSource) + { + spdlog::info("Failed to register {}", typeid(RE::BSAnimationGraphEvent).name()); + }else{ + spdlog::info("Event Source registered {}", typeid(RE::BSAnimationGraphEvent).name()); + } + } + return foundEventSource; +} + +void AnimationEventTracker::SendAnimationEvent(RE::Actor *a_this, const RE::BSFixedString a_tag, const RE::BSFixedString a_payload) +{ + if (a_this && eventSource) + { + RE::BSAnimationGraphEvent event = {a_tag, a_this, a_payload}; + a_this->ProcessEvent(&event, eventSource); + } } \ No newline at end of file diff --git a/src/event.h b/src/event.h index f898079..65d5fd2 100644 --- a/src/event.h +++ b/src/event.h @@ -1,18 +1,22 @@ -#pragma once - -using EventChecker = RE::BSEventNotifyControl; -class AnimationEventTracker : public RE::BSTEventSink -{ -public: - static AnimationEventTracker* GetSingleton() {static AnimationEventTracker singleton; return &singleton;} - - bool Register(); - - virtual EventChecker ProcessEvent(const RE::BSAnimationGraphEvent* a_event, RE::BSTEventSource* a_eventSource) override - { - return EventChecker::kContinue; - }; - void SendAnimationEvent(RE::Actor* a_this, const RE::BSFixedString a_tag, const RE::BSFixedString a_payload = ""); - - RE::BSTEventSource* eventSource; +#pragma once + +using EventChecker = RE::BSEventNotifyControl; +class AnimationEventTracker : public RE::BSTEventSink +{ +public: + static AnimationEventTracker *GetSingleton() + { + static AnimationEventTracker singleton; + return &singleton; + } + + bool Register(); + + virtual EventChecker ProcessEvent(const RE::BSAnimationGraphEvent *a_event, RE::BSTEventSource *a_eventSource) override + { + return EventChecker::kContinue; + }; + void SendAnimationEvent(RE::Actor *a_this, const RE::BSFixedString a_tag, const RE::BSFixedString a_payload = ""); + + RE::BSTEventSource *eventSource; }; \ No newline at end of file diff --git a/src/hook.cpp b/src/hook.cpp index 3004824..f9f13e7 100644 --- a/src/hook.cpp +++ b/src/hook.cpp @@ -1,70 +1,225 @@ #include "hook.h" #include "event.h" #include "util.h" + +#include +#include +#include +#include + +using namespace std::literals; using namespace Util; -void EquipHook::OnEquipItemPC(RE::PlayerCharacter* a_this, bool a_playAnim) +namespace { - _OnEquipItemPC(a_this, !SkipAnim(a_this, a_playAnim)); + static inline std::vector g_pendingClips; + static inline std::vector g_suppressedClips; + + bool IsEquipClip(std::string_view nm) + { + return Util::String::iContains(nm, "Equip"); + } + + RE::Actor *GetActorFromContext(const RE::hkbContext &a_context) + { + if (!a_context.character) + return nullptr; + + const auto *graph = reinterpret_cast( + reinterpret_cast(a_context.character) - 0xC0); + + return graph->holder; + } } -/* -void EquipHook::OnEquipItemNPC(RE::Actor* a_this, bool a_playAnim) + +// --------------------------------------------------------------------------- +// Helpers shared by both paths +// --------------------------------------------------------------------------- + +static bool CheckIsValidBoundObject(const RE::TESForm *a_object) { - _OnEquipItemNPC(a_this, !SkipAnim(a_this, a_playAnim)); + if (!a_object) + return false; + if (a_object->IsMagicItem()) + return true; + return a_object->As() != nullptr || a_object->As() != nullptr; } -*/ -bool CheckIsValidBoundObject(const RE::TESForm* a_object) + +// Sends the animation events that normally fire at the end of an equip animation. +static void SendEquipEvents(RE::Actor *a_this, + RE::TESForm const *a_lHandObject, + RE::TESForm const *a_rHandObject) { -#ifdef WORK_WITH_MAGIC_OBJECTS - if ((a_object && a_object->IsMagicItem())) - return = true; -#endif - return {// && (uint32_t)a_object->As()->GetWeaponType() <= 6u) - a_object && (a_object->As() || a_object->As()) - }; + auto *tracker = AnimationEventTracker::GetSingleton(); + if (!tracker) + return; + + tracker->SendAnimationEvent(a_this, "weaponDraw"); + + if ((a_lHandObject && (a_lHandObject->IsWeapon() || a_lHandObject->IsArmor())) || + (a_rHandObject && (a_rHandObject->IsWeapon() || a_rHandObject->IsArmor()))) + { + tracker->SendAnimationEvent(a_this, "WeapEquip_Out"); + tracker->SendAnimationEvent(a_this, "WeapEquip_OutMoving"); + } } -void SendEquipEvents(RE::Actor* a_this, RE::TESForm* a_lHandObject, RE::TESForm* a_rHandObject) + +// --------------------------------------------------------------------------- +// Mode detection +// --------------------------------------------------------------------------- + +EquipHook::EquipMode EquipHook::GetEquipMode(const RE::PlayerCharacter *a_this, bool a_playAnim) +{ + using enum EquipHook::EquipMode; + if (a_this) + { + bool instantAnim = false; + a_this->GetGraphVariableBool("InstantEquipAnim", instantAnim); + if (instantAnim && a_playAnim) + return InstantAnim; + + bool skipAnim = false; + a_this->GetGraphVariableBool("SkipEquipAnimation", skipAnim); + if (skipAnim && a_playAnim) + return Skip; + } + return Normal; +} + +// --------------------------------------------------------------------------- +// Vtable hook: PlayerCharacter::OnEquipItem +// --------------------------------------------------------------------------- + +void EquipHook::OnEquipItemPC(RE::PlayerCharacter *a_this, bool a_playAnim) { - if (auto animationEventTracker = AnimationEventTracker::GetSingleton()) { - animationEventTracker->SendAnimationEvent(a_this, "weaponDraw"); -#ifdef WORK_WITH_MAGIC_OBJECTS - if ((a_lHandObject && a_lHandObject->IsMagicItem()) || (a_rHandObject && a_rHandObject->IsMagicItem())) - animationEventTracker->SendAnimationEvent(a_this, "MagicEquip_Out"); -#endif - - if ((a_lHandObject && (a_lHandObject->IsWeapon() || a_lHandObject->IsArmor())) - || (a_rHandObject && (a_rHandObject->IsWeapon() || a_rHandObject->IsArmor()))) { - animationEventTracker->SendAnimationEvent(a_this, "WeapEquip_Out"); - animationEventTracker->SendAnimationEvent(a_this, "WeapEquip_OutMoving"); + switch (GetEquipMode(a_this, a_playAnim)) + { + case EquipMode::Skip: + { + auto *tracker = AnimationEventTracker::GetSingleton(); + tracker->Register(); + + auto const *rHandObj = a_this->GetEquippedObject(false); + auto const *lHandObj = a_this->GetEquippedObject(true); + + int delay = 300; + bool skip3D = false; + a_this->GetGraphVariableInt("LoadBoundObjectDelay", delay); + a_this->GetGraphVariableBool("Skip3DLoading", skip3D); + if (delay < static_cast(*g_deltaTimeRealTime * 1000.f)) + delay = static_cast(*g_deltaTimeRealTime * 1000.f); + + _OnEquipItemPC(a_this, false); + + if (!skip3D) + { + std::jthread([=]() + { + std::this_thread::sleep_for(std::chrono::milliseconds(delay)); + SendEquipEvents(a_this, lHandObj, rHandObj); }) + .detach(); } + break; + } + + case EquipMode::InstantAnim: + { + _OnEquipItemPC(a_this, a_playAnim); + break; + } + + default: + _OnEquipItemPC(a_this, a_playAnim); + break; } } -bool EquipHook::SkipAnim(RE::PlayerCharacter* a_this, bool a_playAnim) + +// --------------------------------------------------------------------------- +// Vtable hooks: hkbClipGenerator +// --------------------------------------------------------------------------- +void EquipHook::Activate_Hook(RE::hkbClipGenerator *a_this, const RE::hkbContext &a_context) { - _skipAnim = !a_playAnim; - if (AnimationEventTracker::GetSingleton()->Register() && a_this && a_this->AsActorState() && a_this->AsActorState()->IsWeaponDrawn()) { - auto rHandObj = a_this->GetEquippedObject(false); - auto lHandObj = a_this->GetEquippedObject(true); - - if (!(lHandObj && rHandObj) || CheckIsValidBoundObject(lHandObj) || CheckIsValidBoundObject(rHandObj)) { - int delay = 300; - bool skip3D = false; - a_this->GetGraphVariableBool("SkipEquipAnimation", _skipAnim); - a_this->GetGraphVariableInt("LoadBoundObjectDelay", delay); - a_this->GetGraphVariableBool("Skip3DLoading", skip3D); - if (delay < (int)(*g_deltaTimeRealTime * 1000.f)) delay = (int)(*g_deltaTimeRealTime * 1000.f); // the loading process will start next frame. - if (!skip3D && _skipAnim) { - std::jthread delayedEquipThread([=]() { - if (_skipAnim) { - spdlog::debug("equip anim skipped"); - std::this_thread::sleep_for(std::chrono::milliseconds(delay)); - SendEquipEvents(a_this, lHandObj, rHandObj); - spdlog::debug("weapon 3d model called"); - } - } - ); - delayedEquipThread.detach(); + _Activate(a_this, a_context); + + if (!a_this) + return; + + auto const *player = RE::PlayerCharacter::GetSingleton(); + if (GetActorFromContext(a_context) != player) + return; + + if (const std::string_view name{a_this->animationName.c_str()}; !IsEquipClip(name)) + return; + + bool instantAnim = false; + player->GetGraphVariableBool("InstantEquipAnim", instantAnim); + if (!instantAnim) + return; + + g_pendingClips.push_back(a_this); +} + +void EquipHook::Update_Hook(RE::hkbClipGenerator *a_this, const RE::hkbContext &a_context, float a_timestep) +{ + if (a_this) + { + for (auto const *clip : g_suppressedClips) + { + if (clip == a_this) + { + if (a_this->atEnd) + return; + + float duration = 2.0f; + if (a_this->binding && a_this->binding->animation) + duration = a_this->binding->animation->duration; + + a_this->mode = RE::hkbClipGenerator::PlaybackMode::kModeSinglePlay; + _Update(a_this, a_context, duration + 0.01f); + a_this->atEnd = true; + return; } } - } return _skipAnim; + + if (auto it = std::ranges::find(g_pendingClips, a_this); it != g_pendingClips.end()) + { + g_pendingClips.erase(it); + + float duration = 2.0f; + if (a_this->binding && a_this->binding->animation) + duration = a_this->binding->animation->duration; + + a_this->mode = RE::hkbClipGenerator::PlaybackMode::kModeSinglePlay; + _Update(a_this, a_context, duration + 0.01f); + a_this->atEnd = true; + + g_suppressedClips.push_back(a_this); + return; + } + } + + _Update(a_this, a_context, a_timestep); +} + +void EquipHook::Deactivate_Hook(RE::hkbClipGenerator *a_this, const RE::hkbContext &a_context) +{ + if (auto it = std::ranges::find(g_pendingClips, a_this); it != g_pendingClips.end()) + g_pendingClips.erase(it); + + if (auto it2 = std::ranges::find(g_suppressedClips, a_this); it2 != g_suppressedClips.end()) + g_suppressedClips.erase(it2); + + if (!a_this) + { + _Deactivate(a_this, a_context); + return; + } + + _Deactivate(a_this, a_context); +} + +void EquipHook::ResetState() +{ + g_pendingClips.clear(); + g_suppressedClips.clear(); } \ No newline at end of file diff --git a/src/hook.h b/src/hook.h index 4546fed..0c56b98 100644 --- a/src/hook.h +++ b/src/hook.h @@ -1,26 +1,38 @@ #pragma once - -using EventChecker = RE::BSEventNotifyControl; +#include "event.h" class EquipHook { public: static void Hook() { - REL::Relocation PlayerCharacterVtbl{ RE::VTABLE_PlayerCharacter[0] }; - // REL::Relocation NonPlayerCharacterVtbl{ RE::VTABLE_Actor[0] }; + REL::Relocation PlayerCharacterVtbl{RE::VTABLE_PlayerCharacter[0]}; + REL::Relocation vtbl{RE::VTABLE_hkbClipGenerator[0]}; - _OnEquipItemPC = PlayerCharacterVtbl.write_vfunc(0xB2, OnEquipItemPC); - // _OnEquipItemNPC = NonPlayerCharacterVtbl.write_vfunc(0xB2, OnEquipItemNPC); // maybe later + _OnEquipItemPC = PlayerCharacterVtbl.write_vfunc(0xB2, OnEquipItemPC); + _Activate = vtbl.write_vfunc(0x04, Activate_Hook); + _Update = vtbl.write_vfunc(0x05, Update_Hook); + _Deactivate = vtbl.write_vfunc(0x07, Deactivate_Hook); } + static void ResetState(); + private: - static void OnEquipItemPC(RE::PlayerCharacter* a_this, bool a_playAnim); -// static void OnEquipItemNPC(RE::Actor* a_this, bool a_playAnim); + enum class EquipMode + { + Normal, // play animation normally + Skip, // playAnim=false + delayed SendEquipEvents + InstantAnim, // playAnim=true, fast-forward clip, suppress Generate + }; - static bool SkipAnim(RE::PlayerCharacter* a_this, bool a_playAnim); + static void OnEquipItemPC(RE::PlayerCharacter *a_this, bool a_playAnim); + static void Activate_Hook(RE::hkbClipGenerator *a_this, const RE::hkbContext &a_context); + static void Update_Hook(RE::hkbClipGenerator *a_this, const RE::hkbContext &a_context, float a_timestep); + static void Deactivate_Hook(RE::hkbClipGenerator *a_this, const RE::hkbContext &a_context); - static inline REL::Relocation _OnEquipItemPC; -// static inline REL::Relocation _OnEquipItemNPC; + static EquipMode GetEquipMode(const RE::PlayerCharacter *a_this, bool a_playAnim); - static inline bool _skipAnim; + static inline REL::Relocation _OnEquipItemPC; + static inline REL::Relocation _Activate; + static inline REL::Relocation _Update; + static inline REL::Relocation _Deactivate; }; \ No newline at end of file diff --git a/src/plugin.cpp b/src/plugin.cpp index 9526a78..885577c 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -2,9 +2,10 @@ #include "hook.h" #include "event.h" -void MessageHandler(SKSE::MessagingInterface::Message* a_msg) +void MessageHandler(SKSE::MessagingInterface::Message *a_msg) { - switch (a_msg->type) { + switch (a_msg->type) + { case SKSE::MessagingInterface::kDataLoaded: EquipHook::Hook(); break; @@ -13,22 +14,25 @@ void MessageHandler(SKSE::MessagingInterface::Message* a_msg) case SKSE::MessagingInterface::kPreLoadGame: case SKSE::MessagingInterface::kPostLoadGame: case SKSE::MessagingInterface::kNewGame: + EquipHook::ResetState(); if (auto animationEventTracker = AnimationEventTracker::GetSingleton(); animationEventTracker) animationEventTracker->Register(); break; } } -SKSEPluginLoad(const SKSE::LoadInterface *skse) { +SKSEPluginLoad(const SKSE::LoadInterface *skse) +{ SetupLog(); - auto* plugin = SKSE::PluginDeclaration::GetSingleton(); + auto *plugin = SKSE::PluginDeclaration::GetSingleton(); spdlog::info("{} v{} is loading...", plugin->GetName(), plugin->GetVersion()); SKSE::Init(skse); auto messaging = SKSE::GetMessagingInterface(); - if (!messaging->RegisterListener("SKSE", MessageHandler)) { + if (!messaging->RegisterListener("SKSE", MessageHandler)) + { return false; } diff --git a/src/util.h b/src/util.h index 4c149f9..7cd9010 100644 --- a/src/util.h +++ b/src/util.h @@ -9,51 +9,62 @@ #define PI8 0.3926990816987242f #define downVec {0.f, 0.f, -1.f} -#define upVec {0.f, 0.f, 1.f} +#define upVec {0.f, 0.f, 1.f} -static float* g_deltaTime = (float*)RELOCATION_ID(523660, 410199).address(); // sensitive to slow time spell -static float* g_deltaTimeRealTime = (float*)RELOCATION_ID(523661, 410200).address(); // const +static float *g_deltaTime = (float *)RELOCATION_ID(523660, 410199).address(); // sensitive to slow time spell +static float *g_deltaTimeRealTime = (float *)RELOCATION_ID(523661, 410200).address(); // const using namespace RE; -namespace PointerUtil //yoinked po3's code +namespace PointerUtil // yoinked po3's code { -template -inline auto adjust_pointer(U* a_ptr, std::ptrdiff_t a_adjust) noexcept -{ - auto addr = a_ptr ? reinterpret_cast(a_ptr) + a_adjust : 0; - if constexpr (std::is_const_v && std::is_volatile_v) { - return reinterpret_cast*>(addr); - } else if constexpr (std::is_const_v) { - return reinterpret_cast*>(addr); - } else if constexpr (std::is_volatile_v) { - return reinterpret_cast*>(addr); - } else { - return reinterpret_cast(addr); + template + inline auto adjust_pointer(U *a_ptr, std::ptrdiff_t a_adjust) noexcept + { + auto addr = a_ptr ? reinterpret_cast(a_ptr) + a_adjust : 0; + if constexpr (std::is_const_v && std::is_volatile_v) + { + return reinterpret_cast *>(addr); + } + else if constexpr (std::is_const_v) + { + return reinterpret_cast *>(addr); + } + else if constexpr (std::is_volatile_v) + { + return reinterpret_cast *>(addr); + } + else + { + return reinterpret_cast(addr); + } } } -} namespace SystemUtil { struct Engine { static float GetEngineTime() { - REL::Relocation g_engineTime{ RELOCATION_ID(517597, 404125) }; // credits to https://github.com/jarari + REL::Relocation g_engineTime{RELOCATION_ID(517597, 404125)}; // credits to https://github.com/jarari return *g_engineTime; } }; - struct File + struct File { static std::vector GetConfigs(std::string_view a_folder, std::string_view a_suffix, std::string_view a_extension = ".ini"sv) { std::vector configs{}; - for (const auto iterator = std::filesystem::directory_iterator(a_folder); const auto& entry : iterator) { - if (entry.exists()) { - if (const auto& path = entry.path(); !path.empty() && path.extension() == a_extension) { - if (const auto& fileName = entry.path().string(); fileName.rfind(a_suffix) != std::string::npos) { + for (const auto iterator = std::filesystem::directory_iterator(a_folder); const auto &entry : iterator) + { + if (entry.exists()) + { + if (const auto &path = entry.path(); !path.empty() && path.extension() == a_extension) + { + if (const auto &fileName = entry.path().string(); fileName.rfind(a_suffix) != std::string::npos) + { configs.push_back(fileName); } } @@ -67,31 +78,34 @@ namespace SystemUtil }; } -namespace KeyUtil +namespace KeyUtil { - enum class MACRO_LIMITS { + enum class MACRO_LIMITS + { kMaxMacros = 282 }; - enum class KBM_OFFSETS { + enum class KBM_OFFSETS + { // first 256 for keyboard, then 8 mouse buttons, then mouse wheel up, wheel down, then 16 gamepad buttons - kMacro_KeyboardOffset = 0, // not actually used, just for self-documentation + kMacro_KeyboardOffset = 0, // not actually used, just for self-documentation kMacro_NumKeyboardKeys = 256, - kMacro_MouseButtonOffset = kMacro_NumKeyboardKeys, // 256 + kMacro_MouseButtonOffset = kMacro_NumKeyboardKeys, // 256 kMacro_NumMouseButtons = 8, - kMacro_MouseWheelOffset = kMacro_MouseButtonOffset + kMacro_NumMouseButtons, // 264 + kMacro_MouseWheelOffset = kMacro_MouseButtonOffset + kMacro_NumMouseButtons, // 264 kMacro_MouseWheelDirections = 2, - kMacro_GamepadOffset = kMacro_MouseWheelOffset + kMacro_MouseWheelDirections, // 266 + kMacro_GamepadOffset = kMacro_MouseWheelOffset + kMacro_MouseWheelDirections, // 266 kMacro_NumGamepadButtons = 16, - // 282 + // 282 }; - enum class GAMEPAD_OFFSETS { + enum class GAMEPAD_OFFSETS + { kGamepadButtonOffset_DPAD_UP = static_cast(KBM_OFFSETS::kMacro_GamepadOffset), // 266 kGamepadButtonOffset_DPAD_DOWN, kGamepadButtonOffset_DPAD_LEFT, @@ -110,32 +124,49 @@ namespace KeyUtil kGamepadButtonOffset_RT // 281 }; - struct Interpreter { - public: - static uint32_t GamepadMaskToKeycode(uint32_t keyMask) { - switch (keyMask) { - case 0x001: return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_DPAD_UP); - case 0x002: return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_DPAD_DOWN); - case 0x004: return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_DPAD_LEFT); - case 0x008: return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_DPAD_RIGHT); - case 0x0010: return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_START); - case 0x0020: return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_BACK); - case 0x0040: return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_LEFT_THUMB); - case 0x0080: return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_RIGHT_THUMB); - case 0x0100: return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_LEFT_SHOULDER); - case 0x0200: return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_RIGHT_SHOULDER); - case 0x1000: return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_A); - case 0x2000: return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_B); - case 0x4000: return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_X); - case 0x8000: return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_Y); - case 0x9: return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_LT); - case 0xA: return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_RT); - default: return 282; // Invalid - } -} - + public: + static uint32_t GamepadMaskToKeycode(uint32_t keyMask) + { + switch (keyMask) + { + case 0x001: + return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_DPAD_UP); + case 0x002: + return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_DPAD_DOWN); + case 0x004: + return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_DPAD_LEFT); + case 0x008: + return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_DPAD_RIGHT); + case 0x0010: + return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_START); + case 0x0020: + return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_BACK); + case 0x0040: + return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_LEFT_THUMB); + case 0x0080: + return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_RIGHT_THUMB); + case 0x0100: + return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_LEFT_SHOULDER); + case 0x0200: + return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_RIGHT_SHOULDER); + case 0x1000: + return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_A); + case 0x2000: + return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_B); + case 0x4000: + return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_X); + case 0x8000: + return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_Y); + case 0x9: + return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_LT); + case 0xA: + return static_cast(GAMEPAD_OFFSETS::kGamepadButtonOffset_RT); + default: + return 282; // Invalid + } + } }; } @@ -144,47 +175,55 @@ namespace Util { struct String { - static std::vector Split(const std::string& a_str, std::string_view a_delimiter) + static std::vector Split(const std::string &a_str, std::string_view a_delimiter) { - auto range = a_str | std::ranges::views::split(a_delimiter) | std::ranges::views::transform([](auto&& r) { return std::string_view(r); }); - return { range.begin(), range.end() }; - } + std::vector result; + size_t start = 0; + size_t end = 0; + while ((end = a_str.find(a_delimiter, start)) != std::string::npos) + { + result.emplace_back(a_str.substr(start, end - start)); + start = end + a_delimiter.size(); + } + result.emplace_back(a_str.substr(start)); + return result; + } static bool iContains(std::string_view a_str1, std::string_view a_str2) { - if (a_str2.length() > a_str1.length()) { + if (a_str2.length() > a_str1.length()) + { return false; } - const auto subrange = std::ranges::search(a_str1, a_str2, [](unsigned char ch1, unsigned char ch2) { - return std::toupper(ch1) == std::toupper(ch2); - }); + const auto subrange = std::ranges::search(a_str1, a_str2, [](unsigned char ch1, unsigned char ch2) + { return std::toupper(ch1) == std::toupper(ch2); }); return !subrange.empty(); } static bool iEquals(std::string_view a_str1, std::string_view a_str2) { - return std::ranges::equal(a_str1, a_str2, [](unsigned char ch1, unsigned char ch2) { - return std::toupper(ch1) == std::toupper(ch2); - }); + return std::ranges::equal(a_str1, a_str2, [](unsigned char ch1, unsigned char ch2) + { return std::toupper(ch1) == std::toupper(ch2); }); } // https://stackoverflow.com/a/35452044 - static std::string Join(const std::vector& a_vec, std::string_view a_delimiter) + static std::string Join(const std::vector &a_vec, std::string_view a_delimiter) { return std::accumulate(a_vec.begin(), a_vec.end(), std::string{}, - [a_delimiter](const auto& str1, const auto& str2) { - return str1.empty() ? str2 : str1 + a_delimiter.data() + str2; - }); + [a_delimiter](const auto &str1, const auto &str2) + { + return str1.empty() ? str2 : str1 + a_delimiter.data() + str2; + }); } static std::vector ToFloatVector(const std::vector stringVector) { - std::vector floatNumbers; - for(auto str : stringVector) + std::vector floatNumbers; + for (auto str : stringVector) { float num = atof(str.c_str()); floatNumbers.push_back(num); @@ -194,30 +233,33 @@ namespace Util static std::string ToLower(std::string_view a_str) { std::string result(a_str); - std::ranges::transform(result, result.begin(), [](unsigned char ch) { return static_cast(std::tolower(ch)); }); + std::ranges::transform(result, result.begin(), [](unsigned char ch) + { return static_cast(std::tolower(ch)); }); return result; } static std::string ToUpper(std::string_view a_str) { std::string result(a_str); - std::ranges::transform(result, result.begin(), [](unsigned char ch) { return static_cast(std::toupper(ch)); }); + std::ranges::transform(result, result.begin(), [](unsigned char ch) + { return static_cast(std::toupper(ch)); }); return result; } }; - - constexpr uint32_t hash(const char* data, size_t const size) noexcept + + constexpr uint32_t hash(const char *data, size_t const size) noexcept { uint32_t hash = 5381; - for (const char* c = data; c < data + size; ++c) { + for (const char *c = data; c < data + size; ++c) + { hash = ((hash << 5) + hash) + (unsigned char)*c; } return hash; } - constexpr uint32_t operator""_h(const char* str, size_t size) noexcept + constexpr uint32_t operator""_h(const char *str, size_t size) noexcept { return hash(str, size); } @@ -226,7 +268,7 @@ namespace Util namespace MathUtil { - struct Angle + struct Angle { [[nodiscard]] constexpr static float DegreeToRadian(float a_angle) { @@ -240,12 +282,12 @@ namespace MathUtil static NiPoint3 ToRadianVector(float x, float y, float z) { - RE::NiPoint3 rotationVector{ 0.f, 0.f, 0.f }; + RE::NiPoint3 rotationVector{0.f, 0.f, 0.f}; - rotationVector.x = DegreeToRadian(x); - rotationVector.y = DegreeToRadian(y); - rotationVector.z = DegreeToRadian(z); - return rotationVector; + rotationVector.x = DegreeToRadian(x); + rotationVector.y = DegreeToRadian(y); + rotationVector.z = DegreeToRadian(z); + return rotationVector; } static float NormalAbsoluteAngle(float a_angle) @@ -269,16 +311,18 @@ namespace MathUtil // return fmod(a_angle, TWO_PI) >= 0 ? (a_angle < PI) ? a_angle : a_angle - TWO_PI : (a_angle >= -PI) ? a_angle : a_angle + TWO_PI; } - }; + }; struct Algebra { - [[nodiscard]] static inline float generateRandomFloat(const float lower, const float upper) { + [[nodiscard]] static inline float generateRandomFloat(const float lower, const float upper) + { static std::default_random_engine generator; static std::uniform_real_distribution distribution(lower, upper); - return distribution(generator); + return distribution(generator); } - [[nodiscard]] static inline void SetRotationMatrix(RE::NiMatrix3& a_matrix, const float sacb, const float cacb, const float sb) { + [[nodiscard]] static inline void SetRotationMatrix(RE::NiMatrix3 &a_matrix, const float sacb, const float cacb, const float sb) + { const float cb = std::sqrtf(1 - sb * sb); const float ca = cacb / cb; const float sa = sacb / cb; @@ -292,39 +336,48 @@ namespace MathUtil a_matrix.entry[1][2] = -ca * sb; a_matrix.entry[2][2] = cb; } - [[nodiscard]] static void RotateMatrixAroundAxis(NiMatrix3& a_matrix, const float angleRad, const char* axis) { + [[nodiscard]] static void RotateMatrixAroundAxis(NiMatrix3 &a_matrix, const float angleRad, const char *axis) + { const float cosA = std::cos(angleRad); const float sinA = std::sin(angleRad); - if (std::strcmp(axis, "x") == 0) { + if (std::strcmp(axis, "x") == 0) + { a_matrix.entry[0][0] = 1.0; a_matrix.entry[1][1] = cosA; a_matrix.entry[1][2] = -sinA; a_matrix.entry[2][1] = sinA; a_matrix.entry[2][2] = cosA; - } else if (std::strcmp(axis, "y") == 0) { + } + else if (std::strcmp(axis, "y") == 0) + { a_matrix.entry[0][0] = cosA; a_matrix.entry[0][2] = sinA; a_matrix.entry[1][1] = 1.0; a_matrix.entry[2][0] = -sinA; a_matrix.entry[2][2] = cosA; - } else if (std::strcmp(axis, "z") == 0) { + } + else if (std::strcmp(axis, "z") == 0) + { a_matrix.entry[0][0] = cosA; a_matrix.entry[0][1] = -sinA; a_matrix.entry[1][0] = sinA; a_matrix.entry[1][1] = cosA; a_matrix.entry[2][2] = 1.0; - } else { + } + else + { return; } } - [[nodiscard]] static void RotateMatrixAroundAxisses(NiMatrix3& a_matrix, float angleRadX, float angleRadY, float angleRadZ) { + [[nodiscard]] static void RotateMatrixAroundAxisses(NiMatrix3 &a_matrix, float angleRadX, float angleRadY, float angleRadZ) + { float cosX = std::cos(angleRadX); float sinX = std::sin(angleRadX); float cosY = std::cos(angleRadY); float sinY = std::sin(angleRadY); float cosZ = std::cos(angleRadZ); float sinZ = std::sin(angleRadZ); - // X ekseninde döndürme matrisi + // X ekseninde döndürme matrisi NiMatrix3 rotationMatrixX; rotationMatrixX.entry[0][0] = 1.0; rotationMatrixX.entry[1][1] = cosX; @@ -351,87 +404,100 @@ namespace MathUtil // Orijinal matris ile çarpma a_matrix = a_matrix * rotationMatrixX * rotationMatrixY * rotationMatrixZ; } - // [[nodiscard]] static NiMatrix3 SetRotationMatrixByVector(float Ax, float Ay, float Az, float Bx, float By, float Bz) { - // const float cosAxBx = std::cos(Bx); - // const float cosAxBy = std::cos(angleRadX); - // const float cosAxBz = std::cos(angleRadY); - // const float cosAyBx = std::cos(angleRadY); - // const float cosAyBy = std::cos(By); - // const float cosAyBz = std::cos(angleRadZ); - // const float cosAzBx = std::cos(angleRadY); - // const float cosAzBy = std::cos(angleRadZ); - // const float cosAzBz = std::cos(Bz); -// - // NiMatrix3 matrix; - // // X axis - // matrix.entry[0][0] = 1.0; - // matrix.entry[1][0] = cosX; - // matrix.entry[2][0] = -sinX; -// - // // Y axis - // matrix.entry[0][1] = cosY; - // matrix.entry[1][1] = sinY; - // matrix.entry[2][1] = 1.0; -// - // // Z axis - // matrix.entry[0][2] = sinZ; - // matrix.entry[1][2] = cosZ; - // matrix.entry[2][2] = 1.0; -// - // return matrix; - // } + // [[nodiscard]] static NiMatrix3 SetRotationMatrixByVector(float Ax, float Ay, float Az, float Bx, float By, float Bz) { + // const float cosAxBx = std::cos(Bx); + // const float cosAxBy = std::cos(angleRadX); + // const float cosAxBz = std::cos(angleRadY); + // const float cosAyBx = std::cos(angleRadY); + // const float cosAyBy = std::cos(By); + // const float cosAyBz = std::cos(angleRadZ); + // const float cosAzBx = std::cos(angleRadY); + // const float cosAzBy = std::cos(angleRadZ); + // const float cosAzBz = std::cos(Bz); + // + // NiMatrix3 matrix; + // // X axis + // matrix.entry[0][0] = 1.0; + // matrix.entry[1][0] = cosX; + // matrix.entry[2][0] = -sinX; + // + // // Y axis + // matrix.entry[0][1] = cosY; + // matrix.entry[1][1] = sinY; + // matrix.entry[2][1] = 1.0; + // + // // Z axis + // matrix.entry[0][2] = sinZ; + // matrix.entry[1][2] = cosZ; + // matrix.entry[2][2] = 1.0; + // + // return matrix; + // } }; } namespace ObjectUtil { struct Projectile { - static bool DeleteAnExtraArrow(RE::Actor* a_victim, RE::NiAVObject* a_arrow3D) - { - if (a_victim && a_arrow3D) { - // auto attachedArrows = static_cast(a_victim->extraList.GetByType(RE::ExtraDataType::kAttachedArrows3D)); - // for (auto& extraArrow : attachedArrows->data) { - // if (extraArrow.arrow3D.get() == a_arrow3D) {extraArrow.timeStamp = (uint64_t)0; return true;} - // } - if (auto* xList = &a_victim->extraList; xList) + static bool DeleteAnExtraArrow(RE::Actor *a_victim, RE::NiAVObject *a_arrow3D) + { + if (a_victim && a_arrow3D) + { + // auto attachedArrows = static_cast(a_victim->extraList.GetByType(RE::ExtraDataType::kAttachedArrows3D)); + // for (auto& extraArrow : attachedArrows->data) { + // if (extraArrow.arrow3D.get() == a_arrow3D) {extraArrow.timeStamp = (uint64_t)0; return true;} + // } + if (auto *xList = &a_victim->extraList; xList) if (auto xArrows = xList->GetByType(); xArrows) if (!xArrows->data.empty()) - for (auto& extraArrow : xArrows->data) { - if (extraArrow.arrow3D.get() && extraArrow.arrow3D.get()->name == a_arrow3D->name) {extraArrow.timeStamp = 0; return true;} + for (auto &extraArrow : xArrows->data) + { + if (extraArrow.arrow3D.get() && extraArrow.arrow3D.get()->name == a_arrow3D->name) + { + extraArrow.timeStamp = 0; + return true; + } } - } return false; + } + return false; } }; struct Actor { /* - * Requires my modder utility for this feature: https://github.com/PhiloSocio/SkipEquipAnimation - */ - static void SkipEquipAnimationDuring(int a_durationMS, RE::Actor* a_this, int a_load3dDleayMS = 0) + * Requires my modder utility for this feature: https://github.com/PhiloSocio/SkipEquipAnimation + */ + static void SkipEquipAnimationDuring(int a_durationMS, RE::Actor *a_this, int a_load3dDleayMS = 0) { - if (a_this) { + if (a_this) + { a_this->SetGraphVariableBool("SkipEquipAnimation", true); a_this->SetGraphVariableBool("LoadBoundObjectDelay", a_load3dDleayMS); - if (a_durationMS < (int)(*g_deltaTimeRealTime * 2.f)) a_durationMS = (int)(*g_deltaTimeRealTime * 2.f); - std::jthread DisableEquipAnim([=]() { + if (a_durationMS < (int)(*g_deltaTimeRealTime * 2.f)) + a_durationMS = (int)(*g_deltaTimeRealTime * 2.f); + std::jthread DisableEquipAnim([=]() + { std::this_thread::sleep_for(std::chrono::milliseconds(a_durationMS)); - a_this->SetGraphVariableBool("SkipEquipAnimation", false); - }); + a_this->SetGraphVariableBool("SkipEquipAnimation", false); }); DisableEquipAnim.detach(); } } - static void SendAnimationEvent(RE::Actor* a_this, const RE::BSFixedString a_tag, const RE::BSFixedString a_payload = "") + static void SendAnimationEvent(RE::Actor *a_this, const RE::BSFixedString a_tag, const RE::BSFixedString a_payload = "") { - if (a_this) { + if (a_this) + { RE::BSAnimationGraphManagerPtr graphManager; a_this->GetAnimationGraphManager(graphManager); - if (graphManager) { + if (graphManager) + { bool bSinked = false; - for (auto& animationGraph : graphManager->graphs) { - if (auto eventSource = animationGraph->GetEventSource(); eventSource) { + for (auto &animationGraph : graphManager->graphs) + { + if (auto eventSource = animationGraph->GetEventSource(); eventSource) + { RE::BSAnimationGraphEvent event = {a_tag, a_this, a_payload}; a_this->ProcessEvent(&event, eventSource); break; @@ -440,10 +506,12 @@ namespace ObjectUtil } } } - static bool DoAction(RE::BGSAction* a_action, RE::Actor* a_actor = RE::PlayerCharacter::GetSingleton()) + static bool DoAction(RE::BGSAction *a_action, RE::Actor *a_actor = RE::PlayerCharacter::GetSingleton()) { - if (auto taskInterface = SKSE::GetTaskInterface(); taskInterface && a_action && a_actor) { - taskInterface->AddTask([a_action, a_actor]() { + if (auto taskInterface = SKSE::GetTaskInterface(); taskInterface && a_action && a_actor) + { + taskInterface->AddTask([a_action, a_actor]() + { std::unique_ptr data(TESActionData::Create()); if (data) { //data->source = a_actor->As()->GetHandle().get(); // alternate @@ -452,33 +520,41 @@ namespace ObjectUtil typedef bool func_t(TESActionData*); REL::Relocation func{ RELOCATION_ID(40551, 41557) }; // credits to https://github.com/jarari return func(data.get()); - } return false; - }); - } return false; - } - static void EquipItem(RE::Actor* a_actor, RE::FormID a_formID, const bool a_skipAnim = false, - uint32_t a_count = 1U, bool a_queueEquip = true, bool a_forceEquip = false, bool a_playSounds = true, bool a_applyNow = false, - const RE::BGSEquipSlot *a_slot = (const RE::BGSEquipSlot *)nullptr) { - if (a_actor) { + } return false; }); + } + return false; + } + static void EquipItem(RE::Actor *a_actor, RE::FormID a_formID, const bool a_skipAnim = false, + uint32_t a_count = 1U, bool a_queueEquip = true, bool a_forceEquip = false, bool a_playSounds = true, bool a_applyNow = false, + const RE::BGSEquipSlot *a_slot = (const RE::BGSEquipSlot *)nullptr) + { + if (a_actor) + { auto eqManager = RE::ActorEquipManager::GetSingleton(); auto invChanges = a_actor->GetInventoryChanges(); auto entries = invChanges ? invChanges->entryList : nullptr; - RE::ExtraDataList* xList = nullptr; + RE::ExtraDataList *xList = nullptr; if (entries) - for (auto entry : *entries) { - if (entry && eqManager && entry->extraLists && entry->object && entry->object->formID == a_formID) { - if (entry->extraLists->empty()) spdlog::debug("your extralist is empty!"); - else xList = entry->extraLists->front(); + for (auto entry : *entries) + { + if (entry && eqManager && entry->extraLists && entry->object && entry->object->formID == a_formID) + { + if (entry->extraLists->empty()) + spdlog::debug("your extralist is empty!"); + else + xList = entry->extraLists->front(); eqManager->EquipObject(a_actor, entry->object, xList, a_count, a_slot, a_queueEquip, a_forceEquip, a_playSounds, a_applyNow); - break; + break; } } - else spdlog::debug("there is no inventory changes!"); + else + spdlog::debug("there is no inventory changes!"); } } - static void UnEquipItem(RE::Actor* a_actor, const bool a_isLeft, const bool a_soundPlay) + static void UnEquipItem(RE::Actor *a_actor, const bool a_isLeft, const bool a_soundPlay) { - if (a_actor) { + if (a_actor) + { auto eData = a_actor->GetEquippedEntryData(a_isLeft); auto xLists = eData ? eData->extraLists : nullptr; auto xList = xLists ? xLists->front() : nullptr; @@ -492,12 +568,12 @@ namespace ObjectUtil struct Spell { - static void SetMagnitude(RE::SpellItem* a_spell, const float a_magnitude) + static void SetMagnitude(RE::SpellItem *a_spell, const float a_magnitude) { if (a_spell && a_spell->effects[0]) a_spell->effects[0]->effectItem.magnitude = a_magnitude; } - static void SetDuration(RE::SpellItem* a_spell, const uint8_t a_duration) + static void SetDuration(RE::SpellItem *a_spell, const uint8_t a_duration) { if (a_spell && a_spell->effects[0]) a_spell->effects[0]->effectItem.duration = a_duration; @@ -506,68 +582,91 @@ namespace ObjectUtil struct Poison { - static RE::AlchemyItem* GetEquippedObjPoison(RE::Actor* a_actor, const bool a_isLeft = false) + static RE::AlchemyItem *GetEquippedObjPoison(RE::Actor *a_actor, const bool a_isLeft = false) { - if (a_actor) { - if (auto eData = a_actor->GetEquippedEntryData(a_isLeft); eData) { + if (a_actor) + { + if (auto eData = a_actor->GetEquippedEntryData(a_isLeft); eData) + { auto xList = eData->IsPoisoned() ? eData->extraLists : nullptr; - if (xList) return GetPoison(xList); + if (xList) + return GetPoison(xList); } - } return nullptr; + } + return nullptr; } - static RE::AlchemyItem* GetPoison(RE::BSSimpleList* a_xList) + static RE::AlchemyItem *GetPoison(RE::BSSimpleList *a_xList) { - if (a_xList && !a_xList->empty()) { - for (auto xData : *a_xList) { + if (a_xList && !a_xList->empty()) + { + for (auto xData : *a_xList) + { if (xData) - if (auto xPoison = xData->GetByType(); auto poison = xPoison ? xPoison->poison : nullptr) { + if (auto xPoison = xData->GetByType(); auto poison = xPoison ? xPoison->poison : nullptr) + { return poison; } } - } return nullptr; + } + return nullptr; } }; struct Enchantment { // setters - static void EnchantEquippedWeapon(RE::Actor* a_actor, RE::EnchantmentItem* a_ench, const float a_charge = 500.f, const bool a_isLeft = false, const bool a_removeOnUnequip = false) + static void EnchantEquippedWeapon(RE::Actor *a_actor, RE::EnchantmentItem *a_ench, const float a_charge = 500.f, const bool a_isLeft = false, const bool a_removeOnUnequip = false) { - if (!a_actor || !a_ench) return; - if (auto eData = a_actor->GetEquippedEntryData(a_isLeft); auto xList = eData ? eData->extraLists : nullptr) { - if (xList) return EnchantItem(xList, a_ench, a_charge, a_removeOnUnequip); + if (!a_actor || !a_ench) + return; + if (auto eData = a_actor->GetEquippedEntryData(a_isLeft); auto xList = eData ? eData->extraLists : nullptr) + { + if (xList) + return EnchantItem(xList, a_ench, a_charge, a_removeOnUnequip); } } - static void DisEnchantEquippedWeapon(RE::Actor* a_actor, const bool a_isLeft = false, const bool a_defaultEnch = false) + static void DisEnchantEquippedWeapon(RE::Actor *a_actor, const bool a_isLeft = false, const bool a_defaultEnch = false) { - if (auto eData = a_actor->GetEquippedEntryData(a_isLeft); auto obj = a_actor->GetEquippedObject(a_isLeft)) { - if (auto weap = obj->As(); weap) { - if (a_defaultEnch && weap->formEnchanting) { + if (auto eData = a_actor->GetEquippedEntryData(a_isLeft); auto obj = a_actor->GetEquippedObject(a_isLeft)) + { + if (auto weap = obj->As(); weap) + { + if (a_defaultEnch && weap->formEnchanting) + { weap->formEnchanting = nullptr; weap->amountofEnchantment = 0; } - if (auto xList = eData->extraLists; xList) { + if (auto xList = eData->extraLists; xList) + { return DisEnchantItem(xList); } } } } - static void ChargeEquippedWeapon(RE::Actor* a_actor, const float a_charge, const bool a_isLeft = false) + static void ChargeEquippedWeapon(RE::Actor *a_actor, const float a_charge, const bool a_isLeft = false) { - if (a_actor && a_actor->AsActorValueOwner()) { + if (a_actor && a_actor->AsActorValueOwner()) + { float maxCharge = 0.f; - if (auto eData = a_actor->GetEquippedEntryData(a_isLeft); auto xList = eData ? eData->extraLists : nullptr) { - if (eData->object && eData->object->As()) { + if (auto eData = a_actor->GetEquippedEntryData(a_isLeft); auto xList = eData ? eData->extraLists : nullptr) + { + if (eData->object && eData->object->As()) + { maxCharge = eData->object->As()->amountofEnchantment; - } for (auto xData : *xList) { - if (auto xEnch = xData->GetByType(); xEnch) { + } + for (auto xData : *xList) + { + if (auto xEnch = xData->GetByType(); xEnch) + { maxCharge = (float)xEnch->charge - 1.f; } - if (auto xCharge = xData->GetByType(); xCharge) { + if (auto xCharge = xData->GetByType(); xCharge) + { float sum = xCharge->charge + a_charge; - if (sum < 0.f) sum = 0.f; + if (sum < 0.f) + sum = 0.f; xCharge->charge = sum >= maxCharge ? maxCharge : sum; - break; + break; } } } @@ -577,118 +676,154 @@ namespace ObjectUtil a_actor->AsActorValueOwner()->ModActorValue(av, charge); } } - static void ChargeInventoryWeapon(RE::Actor* a_actor, RE::FormID a_weapID, const float a_charge) + static void ChargeInventoryWeapon(RE::Actor *a_actor, RE::FormID a_weapID, const float a_charge) { - if (a_actor) { + if (a_actor) + { auto invChanges = a_actor->GetInventoryChanges(); if (auto entries = invChanges ? invChanges->entryList : nullptr; entries && !entries->empty()) - for (auto entry : *entries) { - if (entry && entry->object && entry->object->IsWeapon() && entry->object->formID == a_weapID) { + for (auto entry : *entries) + { + if (entry && entry->object && entry->object->IsWeapon() && entry->object->formID == a_weapID) + { ChargeWeapon(entry, a_charge); } } } } - static void ChargeWeapon(RE::InventoryEntryData* a_eData, const float a_charge) + static void ChargeWeapon(RE::InventoryEntryData *a_eData, const float a_charge) { - if (a_eData && a_eData->extraLists && !a_eData->extraLists->empty()) { + if (a_eData && a_eData->extraLists && !a_eData->extraLists->empty()) + { float maxCharge = 0.f; auto xList = a_eData->extraLists; - if (a_eData->object && a_eData->object->As()) { + if (a_eData->object && a_eData->object->As()) + { maxCharge = a_eData->object->As()->amountofEnchantment; - } for (auto xData : *xList) { + } + for (auto xData : *xList) + { if (xData) - if (auto xEnch = xData->GetByType(); xEnch) { + if (auto xEnch = xData->GetByType(); xEnch) + { maxCharge = (float)xEnch->charge - 1.f; } - if (auto xCharge = xData->GetByType(); xCharge) { - float sum = xCharge->charge + a_charge; - if (sum < 0.f) sum = 0.f; - xCharge->charge = sum >= maxCharge ? maxCharge : sum; - break; - } + if (auto xCharge = xData->GetByType(); xCharge) + { + float sum = xCharge->charge + a_charge; + if (sum < 0.f) + sum = 0.f; + xCharge->charge = sum >= maxCharge ? maxCharge : sum; + break; + } } } } - - static void EnchantInventoryWeapon(RE::Actor* a_actor, RE::FormID a_weapID, RE::EnchantmentItem* a_ench, const float a_magnitude, const float a_charge, const bool a_removeOnUnequip = false) + + static void EnchantInventoryWeapon(RE::Actor *a_actor, RE::FormID a_weapID, RE::EnchantmentItem *a_ench, const float a_magnitude, const float a_charge, const bool a_removeOnUnequip = false) { - if (!a_actor || !a_ench) return; + if (!a_actor || !a_ench) + return; auto invChanges = a_actor->GetInventoryChanges(); auto entries = invChanges ? invChanges->entryList : nullptr; - RE::BSSimpleList* xList = nullptr; + RE::BSSimpleList *xList = nullptr; if (entries && !entries->empty()) - for (auto entry : *entries) { - if (entry && entry->object && entry->object->IsWeapon() && entry->object->formID == a_weapID) { + for (auto entry : *entries) + { + if (entry && entry->object && entry->object->IsWeapon() && entry->object->formID == a_weapID) + { xList = entry->extraLists; } - } if (xList) return EnchantItem(xList, a_ench, a_charge, a_removeOnUnequip); + } + if (xList) + return EnchantItem(xList, a_ench, a_charge, a_removeOnUnequip); } - static void EnchantItem(RE::BSSimpleList* a_xList, RE::EnchantmentItem* a_ench, const float a_charge = 500.f, const bool a_removeOnUnequip = false) + static void EnchantItem(RE::BSSimpleList *a_xList, RE::EnchantmentItem *a_ench, const float a_charge = 500.f, const bool a_removeOnUnequip = false) { - if (!a_xList || a_xList->empty() || !a_ench) return; + if (!a_xList || a_xList->empty() || !a_ench) + return; bool isEnchanted = false; bool isCharged = false; - for (auto xData : *a_xList) { - if (xData) { - if (auto xEnch = xData->GetByType(); xEnch) { + for (auto xData : *a_xList) + { + if (xData) + { + if (auto xEnch = xData->GetByType(); xEnch) + { xEnch->enchantment = a_ench; xEnch->charge = a_charge; xEnch->removeOnUnequip = a_removeOnUnequip; isEnchanted = true; - } if (auto xCharge = xData->GetByType(); xCharge) { + } + if (auto xCharge = xData->GetByType(); xCharge) + { xCharge->charge = a_charge; isCharged = true; } } } if (!isEnchanted) - for (auto xData : *a_xList) { + for (auto xData : *a_xList) + { auto newEnch = RE::BSExtraData::Create(); newEnch->enchantment = a_ench; newEnch->charge = a_charge; newEnch->removeOnUnequip = a_removeOnUnequip; - // RE::ExtraEnchantment* newEnch = new RE::ExtraEnchantment(a_ench, a_charge, a_removeOnUnequip); - // RE::ExtraEnchantment newEnch(a_ench, a_charge, a_removeOnUnequip); // causing crashes - if (xData) xData->Add(newEnch); - break; + // RE::ExtraEnchantment* newEnch = new RE::ExtraEnchantment(a_ench, a_charge, a_removeOnUnequip); + // RE::ExtraEnchantment newEnch(a_ench, a_charge, a_removeOnUnequip); // causing crashes + if (xData) + xData->Add(newEnch); + break; } if (!isCharged) - for (auto xData : *a_xList) { - // RE::ExtraCharge* newCharge = new RE::ExtraCharge(); - RE::ExtraCharge* newCharge = RE::BSExtraData::Create(); + for (auto xData : *a_xList) + { + // RE::ExtraCharge* newCharge = new RE::ExtraCharge(); + RE::ExtraCharge *newCharge = RE::BSExtraData::Create(); newCharge->charge = a_charge; - if (xData) xData->Add(newCharge); - break; + if (xData) + xData->Add(newCharge); + break; } } - static void DisEnchantInventoryWeapon(RE::Actor* a_actor, RE::FormID a_weapID) + static void DisEnchantInventoryWeapon(RE::Actor *a_actor, RE::FormID a_weapID) { - if (a_actor) { + if (a_actor) + { auto invChanges = a_actor->GetInventoryChanges(); auto entries = invChanges->entryList; - RE::BSSimpleList* xList = nullptr; - for (auto entry : *entries) { - if (entry && entry->object && entry->object->IsWeapon() && entry->object->formID == a_weapID) { + RE::BSSimpleList *xList = nullptr; + for (auto entry : *entries) + { + if (entry && entry->object && entry->object->IsWeapon() && entry->object->formID == a_weapID) + { xList = entry->extraLists; } - } if (xList) return DisEnchantItem(xList); + } + if (xList) + return DisEnchantItem(xList); } } - static void DisEnchantItem(RE::BSSimpleList* a_xList) + static void DisEnchantItem(RE::BSSimpleList *a_xList) { - if (a_xList) { - for (auto xData : *a_xList) { - if (xData) { - if (auto xEnch = xData->GetByType(); xEnch) { + if (a_xList) + { + for (auto xData : *a_xList) + { + if (xData) + { + if (auto xEnch = xData->GetByType(); xEnch) + { xEnch->enchantment = nullptr; xEnch->charge = 0; xData->Remove(xEnch); xEnch->~ExtraEnchantment(); xEnch = nullptr; - } if (auto xCharge = xData->GetByType(); xCharge) { + } + if (auto xCharge = xData->GetByType(); xCharge) + { xCharge->charge = 0.f; xData->Remove(xCharge); xCharge->~ExtraCharge(); @@ -699,54 +834,69 @@ namespace ObjectUtil } } // getters - static RE::EnchantmentItem* GetEquippedWeaponEnchantment(RE::Actor* a_actor, const bool a_isLeft = false, const bool a_baseEnchPrior = false) + static RE::EnchantmentItem *GetEquippedWeaponEnchantment(RE::Actor *a_actor, const bool a_isLeft = false, const bool a_baseEnchPrior = false) { - RE::EnchantmentItem* ench = nullptr; + RE::EnchantmentItem *ench = nullptr; if (a_actor) - if (auto obj = a_actor->GetEquippedObject(a_isLeft); obj) ench = GetInventoryItemEnchantment(a_actor, obj->formID, a_baseEnchPrior); + if (auto obj = a_actor->GetEquippedObject(a_isLeft); obj) + ench = GetInventoryItemEnchantment(a_actor, obj->formID, a_baseEnchPrior); return ench; } - static float GetEquippedWeaponCharge(RE::Actor* a_actor, const bool a_isLeft = false) + static float GetEquippedWeaponCharge(RE::Actor *a_actor, const bool a_isLeft = false) { float charge = 0.f; - if (a_actor && a_actor->AsActorValueOwner()) { + if (a_actor && a_actor->AsActorValueOwner()) + { const auto av = a_isLeft ? RE::ActorValue::kLeftItemCharge : RE::ActorValue::kRightItemCharge; charge = a_actor->AsActorValueOwner()->GetActorValue(av); - } return charge; + } + return charge; } - static RE::EnchantmentItem* GetInventoryItemEnchantment(RE::Actor* a_actor, RE::FormID a_weapID, const bool a_baseEnchPrior = false) + static RE::EnchantmentItem *GetInventoryItemEnchantment(RE::Actor *a_actor, RE::FormID a_weapID, const bool a_baseEnchPrior = false) { - RE::EnchantmentItem* formEnch = nullptr; - RE::EnchantmentItem* ench = nullptr; - if (a_actor) { + RE::EnchantmentItem *formEnch = nullptr; + RE::EnchantmentItem *ench = nullptr; + if (a_actor) + { auto invChanges = a_actor->GetInventoryChanges(); auto entries = invChanges->entryList; - for (auto entry : *entries) { - if (entry && entry->object && entry->object->IsWeapon() && entry->object->formID == a_weapID) { - if (auto eForm = entry->object->As(); eForm) formEnch = eForm->formEnchanting; - if (auto xList = entry->extraLists; xList && !xList->empty()) ench = GetExtraEnchantment(xList); + for (auto entry : *entries) + { + if (entry && entry->object && entry->object->IsWeapon() && entry->object->formID == a_weapID) + { + if (auto eForm = entry->object->As(); eForm) + formEnch = eForm->formEnchanting; + if (auto xList = entry->extraLists; xList && !xList->empty()) + ench = GetExtraEnchantment(xList); } } } - if (a_baseEnchPrior) return formEnch ? formEnch : ench; - else return ench ? ench : formEnch; + if (a_baseEnchPrior) + return formEnch ? formEnch : ench; + else + return ench ? ench : formEnch; } - static RE::EnchantmentItem* GetExtraEnchantment(RE::BSSimpleList* a_xList) + static RE::EnchantmentItem *GetExtraEnchantment(RE::BSSimpleList *a_xList) { - if (a_xList && !a_xList->empty()) { - for (auto xData : *a_xList) { + if (a_xList && !a_xList->empty()) + { + for (auto xData : *a_xList) + { if (xData) - if (auto xEnch = xData->GetByType(); xEnch) return xEnch->enchantment; + if (auto xEnch = xData->GetByType(); xEnch) + return xEnch->enchantment; } - } return nullptr; + } + return nullptr; } }; struct Sound { - static void PlaySound(RE::BGSSoundDescriptorForm* a_sound, RE::NiAVObject* a_source, const float a_volume = 1.f) + static void PlaySound(RE::BGSSoundDescriptorForm *a_sound, RE::NiAVObject *a_source, const float a_volume = 1.f) { - if (a_sound && a_source) { + if (a_sound && a_source) + { auto audioManager = RE::BSAudioManager::GetSingleton(); BSSoundHandle handle; audioManager->BuildSoundDataFromDescriptor(handle, a_sound->soundDescriptor); @@ -793,13 +943,12 @@ namespace ObjectUtil }; } - namespace AnimUtil { struct Idle { - static bool Play(RE::TESIdleForm* idle, RE::Actor* actor, RE::DEFAULT_OBJECT action, RE::Actor* target) - { + static bool Play(RE::TESIdleForm *idle, RE::Actor *actor, RE::DEFAULT_OBJECT action, RE::Actor *target) + { if (actor && actor->GetActorRuntimeData().currentProcess) { typedef bool (*func_t)(RE::AIProcess *, RE::Actor *, RE::DEFAULT_OBJECT, RE::TESIdleForm *, bool, bool, RE::Actor *); @@ -807,7 +956,7 @@ namespace AnimUtil return func(actor->GetActorRuntimeData().currentProcess, actor, action, idle, true, true, target); } return false; - } + } }; } @@ -815,140 +964,141 @@ namespace FormUtil { struct Parse { - static RE::TESForm *GetFormFromMod(uint32_t formid,std::string modname) - { + static RE::TESForm *GetFormFromMod(uint32_t formid, std::string modname) + { if (!modname.length() || !formid) return nullptr; RE::TESDataHandler *dh = RE::TESDataHandler::GetSingleton(); - return dh->LookupForm(formid, modname); - - } + return dh->LookupForm(formid, modname); + } - static RE::TESForm *GetFormFromMod(std::string modname, std::string formIDString) - { - if (formIDString.length() == 0) return nullptr; + static RE::TESForm *GetFormFromMod(std::string modname, std::string formIDString) + { + if (formIDString.length() == 0) + return nullptr; - uint32_t formID = std::stoi(formIDString, 0, 16); - return GetFormFromMod(formID,modname); - } + uint32_t formID = std::stoi(formIDString, 0, 16); + return GetFormFromMod(formID, modname); + } - static RE::TESForm *GetFormFromConfigString(std::string str, std::string_view delimiter) - { - std::vector splitData = Util::String::Split(str, delimiter); - if (splitData.size() < 2) return nullptr; - return GetFormFromMod(splitData[1], splitData[0]); - } - static RE::TESForm *GetFormFromConfigString(std::string str) - { - return GetFormFromConfigString(str, "~"sv); - } - static RE::FormID GetFormIDFromMod(uint32_t relativeFormID, std::string modName) - { - auto *dataHandler = TESDataHandler::GetSingleton(); + static RE::TESForm *GetFormFromConfigString(std::string str, std::string_view delimiter) + { + std::vector splitData = Util::String::Split(str, delimiter); + if (splitData.size() < 2) + return nullptr; + return GetFormFromMod(splitData[1], splitData[0]); + } + static RE::TESForm *GetFormFromConfigString(std::string str) + { + return GetFormFromConfigString(str, "~"sv); + } + static RE::FormID GetFormIDFromMod(uint32_t relativeFormID, std::string modName) + { + auto *dataHandler = TESDataHandler::GetSingleton(); - if (!dataHandler) + if (!dataHandler) return -1; - return dataHandler->LookupFormID(relativeFormID, modName); - } - - static RE::FormID GetFormIDFromMod(std::string relativeFormIDString, std::string modName) - { - if (relativeFormIDString.length() == 0) return -1; - + return dataHandler->LookupFormID(relativeFormID, modName); + } - uint32_t relativeFormID = std::stoi(relativeFormIDString, 0, 16); - return GetFormIDFromMod(relativeFormID, modName); - } + static RE::FormID GetFormIDFromMod(std::string relativeFormIDString, std::string modName) + { + if (relativeFormIDString.length() == 0) + return -1; - static RE::FormID GetFormIDFromConfigString(std::string str, std::string_view delimiter) - { - std::vector splitData = Util::String::Split(str, delimiter); - if (splitData.size() < 2) return -1; - return GetFormIDFromMod(splitData[0], splitData[1]); - } - static RE::FormID GetFormIDFromConfigString(std::string str) - { - return GetFormIDFromConfigString(str, "~"sv); - } + uint32_t relativeFormID = std::stoi(relativeFormIDString, 0, 16); + return GetFormIDFromMod(relativeFormID, modName); + } + static RE::FormID GetFormIDFromConfigString(std::string str, std::string_view delimiter) + { + std::vector splitData = Util::String::Split(str, delimiter); + if (splitData.size() < 2) + return -1; + return GetFormIDFromMod(splitData[0], splitData[1]); + } + static RE::FormID GetFormIDFromConfigString(std::string str) + { + return GetFormIDFromConfigString(str, "~"sv); + } }; - struct Quest + struct Quest { - public: - static BGSBaseAlias *FindAliasByName(std::string_view name, TESQuest *owningQuest) + public: + static BGSBaseAlias *FindAliasByName(std::string_view name, TESQuest *owningQuest) + { + RE::BSWriteLockGuard AliasLock{owningQuest->aliasAccessLock}; + for (auto *alias : owningQuest->aliases) { - RE::BSWriteLockGuard AliasLock{owningQuest->aliasAccessLock}; - for (auto *alias : owningQuest->aliases) - { std::string aliasName = alias->aliasName.c_str(); if (aliasName == name) return alias; - } - return nullptr; } + return nullptr; + } }; } namespace NifUtil { struct Node + { + static NiAVObject *Clone(NiAVObject *original) { - static NiAVObject* Clone(NiAVObject* original) - { - typedef NiAVObject* (*func_t)(NiAVObject* avObj); - REL::Relocation func{ RELOCATION_ID(68835, 70187) }; - return func(original); - } - static RE::NiAVObject* GetNiObject( - RE::NiNode* a_root, - const RE::BSFixedString& a_name) - { - return a_root->GetObjectByName(a_name); - } + typedef NiAVObject *(*func_t)(NiAVObject *avObj); + REL::Relocation func{RELOCATION_ID(68835, 70187)}; + return func(original); + } + static RE::NiAVObject *GetNiObject( + RE::NiNode *a_root, + const RE::BSFixedString &a_name) + { + return a_root->GetObjectByName(a_name); + } - static void AttachToNode( - RE::NiAVObject* a_object, - RE::NiNode* a_node) + static void AttachToNode( + RE::NiAVObject *a_object, + RE::NiNode *a_node) + { + if (a_object->parent != a_node) { - if (a_object->parent != a_node) - { - a_node->AttachChild(a_object, true); - } + a_node->AttachChild(a_object, true); } + } - static std::vector GetAllGeometries(RE::NiAVObject* root) - { - std::vector geometries; - RE::BSVisit::TraverseScenegraphGeometries(root, [&](BSGeometry* geom)-> RE::BSVisit::BSVisitControl - { + static std::vector GetAllGeometries(RE::NiAVObject *root) + { + std::vector geometries; + RE::BSVisit::TraverseScenegraphGeometries(root, [&](BSGeometry *geom) -> RE::BSVisit::BSVisitControl + { geometries.emplace_back(geom); - return RE::BSVisit::BSVisitControl::kContinue; - } - ); - return geometries; - } - - }; + return RE::BSVisit::BSVisitControl::kContinue; }); + return geometries; + } + }; struct Armature { - static RE::NiNode* GetActorNode(RE::Actor* actor, std::string nodeName) + static RE::NiNode *GetActorNode(RE::Actor *actor, std::string nodeName) { - auto root = actor->Get3D(); - if (!root) return nullptr; + auto root = actor->Get3D(); + if (!root) + return nullptr; - auto bone = root->GetObjectByName(nodeName); - if (!bone) return nullptr; + auto bone = root->GetObjectByName(nodeName); + if (!bone) + return nullptr; - auto node = bone->AsNode(); - if (!node) return nullptr; + auto node = bone->AsNode(); + if (!node) + return nullptr; - return node; + return node; } - static void AttachToNode(RE::NiAVObject* obj, RE::Actor* actor, std::string nodeName) + static void AttachToNode(RE::NiAVObject *obj, RE::Actor *actor, std::string nodeName) { - auto* node = GetActorNode(actor, nodeName); + auto *node = GetActorNode(actor, nodeName); if (node) { node->AttachChild(obj, true); @@ -958,14 +1108,16 @@ namespace NifUtil }; struct Collision { - static bool ToggleMeshCollision(RE::NiAVObject* root,RE::bhkWorld* world, bool collisionState) + static bool ToggleMeshCollision(RE::NiAVObject *root, RE::bhkWorld *world, bool collisionState) { constexpr auto no_collision_flag = static_cast(RE::CFilter::Flag::kNoCollision); - if (root && world) { - - RE::BSWriteLockGuard locker(world->worldLock); + if (root && world) + { + + RE::BSWriteLockGuard locker(world->worldLock); - RE::BSVisit::TraverseScenegraphCollision(root, [&](RE::bhkNiCollisionObject* a_col) -> RE::BSVisit::BSVisitControl { + RE::BSVisit::TraverseScenegraphCollision(root, [&](RE::bhkNiCollisionObject *a_col) -> RE::BSVisit::BSVisitControl + { if (auto hkpBody = a_col->body ? static_cast(a_col->body->referencedObject.get()) : nullptr; hkpBody) { auto& filter = hkpBody->collidable.broadPhaseHandle.collisionFilterInfo; if (!collisionState) { @@ -974,23 +1126,24 @@ namespace NifUtil filter &= ~no_collision_flag; } } - return RE::BSVisit::BSVisitControl::kContinue; - }); - } - else - { - return false; - } + return RE::BSVisit::BSVisitControl::kContinue; }); + } + else + { + return false; + } return true; } - static bool RemoveMeshCollision(RE::NiAVObject* root,RE::bhkWorld* world, bool collisionState) + static bool RemoveMeshCollision(RE::NiAVObject *root, RE::bhkWorld *world, bool collisionState) { constexpr auto no_collision_flag = static_cast(RE::CFilter::Flag::kNoCollision); - if (root && world) { - - RE::BSWriteLockGuard locker(world->worldLock); + if (root && world) + { + + RE::BSWriteLockGuard locker(world->worldLock); - RE::BSVisit::TraverseScenegraphCollision(root, [&](RE::bhkNiCollisionObject* a_col) -> RE::BSVisit::BSVisitControl { + RE::BSVisit::TraverseScenegraphCollision(root, [&](RE::bhkNiCollisionObject *a_col) -> RE::BSVisit::BSVisitControl + { if (auto hkpBody = a_col->body ? static_cast(a_col->body->referencedObject.get()) : nullptr; hkpBody) { auto& filter = hkpBody->collidable.broadPhaseHandle.collisionFilterInfo; if (!collisionState) { @@ -999,13 +1152,12 @@ namespace NifUtil filter &= ~no_collision_flag; } } - return RE::BSVisit::BSVisitControl::kContinue; - }); - } - else - { - return false; - } + return RE::BSVisit::BSVisitControl::kContinue; }); + } + else + { + return false; + } return true; } };