diff --git a/CommonLibF4/cmake/sourcelist.cmake b/CommonLibF4/cmake/sourcelist.cmake index 4b4cfe8c..3a68b4c0 100644 --- a/CommonLibF4/cmake/sourcelist.cmake +++ b/CommonLibF4/cmake/sourcelist.cmake @@ -2,6 +2,7 @@ set(SOURCES include/F4SE/API.h include/F4SE/F4SE.h include/F4SE/Impl/PCH.h + include/F4SE/InputMap.h include/F4SE/Interfaces.h include/F4SE/Logger.h include/F4SE/Trampoline.h @@ -45,6 +46,7 @@ set(SOURCES include/RE/Bethesda/BSGraphics.h include/RE/Bethesda/BSHavok.h include/RE/Bethesda/BSInputDeviceManager.h + include/RE/Bethesda/BSInputEnableManager.h include/RE/Bethesda/BSInputEventReceiver.h include/RE/Bethesda/BSInputEventSingleUser.h include/RE/Bethesda/BSInputEventUser.h @@ -353,6 +355,8 @@ set(SOURCES include/REL/Relocation.h include/REL/Segment.h include/REL/Version.h + include/REX/PS4.h + include/REX/PS4/SCEPAD.h include/REX/W32.h include/REX/W32/ADVAPI32.h include/REX/W32/BASE.h @@ -382,6 +386,7 @@ set(SOURCES include/REX/W32/XINPUT.h src/F4SE/API.cpp src/F4SE/Impl/PCH.cpp + src/F4SE/InputMap.cpp src/F4SE/Interfaces.cpp src/F4SE/Logger.cpp src/F4SE/Trampoline.cpp diff --git a/CommonLibF4/include/F4SE/F4SE.h b/CommonLibF4/include/F4SE/F4SE.h index f59a15a0..fc60949a 100644 --- a/CommonLibF4/include/F4SE/F4SE.h +++ b/CommonLibF4/include/F4SE/F4SE.h @@ -3,6 +3,7 @@ #include "F4SE/Impl/PCH.h" #include "F4SE/API.h" +#include "F4SE/InputMap.h" #include "F4SE/Interfaces.h" #include "F4SE/Logger.h" #include "F4SE/Trampoline.h" diff --git a/CommonLibF4/include/F4SE/Impl/PCH.h b/CommonLibF4/include/F4SE/Impl/PCH.h index 284fe858..2a5e7441 100644 --- a/CommonLibF4/include/F4SE/Impl/PCH.h +++ b/CommonLibF4/include/F4SE/Impl/PCH.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -329,6 +330,12 @@ namespace F4SE return *this; } + constexpr enumeration& reset() noexcept + { + _impl = 0; + return *this; + } + template [[nodiscard]] constexpr bool any(Args... a_args) const noexcept // requires(std::same_as&&...) diff --git a/CommonLibF4/include/F4SE/InputMap.h b/CommonLibF4/include/F4SE/InputMap.h new file mode 100644 index 00000000..840ca120 --- /dev/null +++ b/CommonLibF4/include/F4SE/InputMap.h @@ -0,0 +1,51 @@ +#pragma once + +namespace F4SE +{ + namespace InputMap + { + enum + { + // 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_NumKeyboardKeys = 256, + + kMacro_MouseButtonOffset = kMacro_NumKeyboardKeys, // 256 + kMacro_NumMouseButtons = 8, + + kMacro_MouseWheelOffset = kMacro_MouseButtonOffset + kMacro_NumMouseButtons, // 264 + kMacro_MouseWheelDirections = 2, + + kMacro_GamepadOffset = kMacro_MouseWheelOffset + kMacro_MouseWheelDirections, // 266 + kMacro_NumGamepadButtons = 16, + + kMaxMacros = kMacro_GamepadOffset + kMacro_NumGamepadButtons // 282 + }; + + enum + { + kGamepadButtonOffset_DPAD_UP = kMacro_GamepadOffset, // 266 + kGamepadButtonOffset_DPAD_DOWN, + kGamepadButtonOffset_DPAD_LEFT, + kGamepadButtonOffset_DPAD_RIGHT, + kGamepadButtonOffset_START, + kGamepadButtonOffset_BACK, + kGamepadButtonOffset_LEFT_THUMB, + kGamepadButtonOffset_RIGHT_THUMB, + kGamepadButtonOffset_LEFT_SHOULDER, + kGamepadButtonOffset_RIGHT_SHOULDER, + kGamepadButtonOffset_A, + kGamepadButtonOffset_B, + kGamepadButtonOffset_X, + kGamepadButtonOffset_Y, + kGamepadButtonOffset_LT, + kGamepadButtonOffset_RT // 281 + }; + + std::uint32_t XInputToScePadOffset(std::uint32_t keyMask); + std::uint32_t ScePadOffsetToXInput(std::uint32_t keyMask); + + std::uint32_t GamepadMaskToKeycode(std::uint32_t keyMask); + std::uint32_t GamepadKeycodeToMask(std::uint32_t keyCode); + } +} diff --git a/CommonLibF4/include/RE/Bethesda/BGSSaveLoad.h b/CommonLibF4/include/RE/Bethesda/BGSSaveLoad.h index c6750642..3438ddc8 100644 --- a/CommonLibF4/include/RE/Bethesda/BGSSaveLoad.h +++ b/CommonLibF4/include/RE/Bethesda/BGSSaveLoad.h @@ -123,14 +123,14 @@ namespace RE [[nodiscard]] static BGSSaveLoadManager* GetSingleton() { - REL::Relocation singleton{ REL::ID(1247320) }; + REL::Relocation singleton{ REL::ID(2697802) }; return *singleton; } void QueueSaveLoadTask(QUEUED_TASK a_task) { using func_t = decltype(&BGSSaveLoadManager::QueueSaveLoadTask); - REL::Relocation func{ REL::ID(1487308) }; + REL::Relocation func{ REL::ID(2228080) }; return func(this, a_task); } diff --git a/CommonLibF4/include/RE/Bethesda/BSExtraData.h b/CommonLibF4/include/RE/Bethesda/BSExtraData.h index 3f2cd526..d0523ce5 100644 --- a/CommonLibF4/include/RE/Bethesda/BSExtraData.h +++ b/CommonLibF4/include/RE/Bethesda/BSExtraData.h @@ -70,7 +70,7 @@ namespace RE kLock, // ExtraLock kTeleport, // ExtraTeleport kMapMarker, - kLeveledCreature, + kLeveledCreature, // ExtraLeveledCreature kLevelItem, kScale, kSeed, @@ -249,6 +249,7 @@ namespace RE class BGSMessage; class BGSRefAlias; class TBO_InstanceData; + class TESActorBase; class TESBoundObject; class TESForm; class TESObjectCELL; @@ -400,6 +401,20 @@ namespace RE }; static_assert(sizeof(ExtraTeleport) == 0x20); + class __declspec(novtable) ExtraLeveledCreature : + public BSExtraData // 00 + { + public: + static constexpr auto RTTI{ RTTI::ExtraLeveledCreature }; + static constexpr auto VTABLE{ VTABLE::ExtraLeveledCreature }; + static constexpr auto TYPE{ EXTRA_DATA_TYPE::kLeveledCreature }; + + // members + TESActorBase* originalBase; // 18 + TESActorBase* templates[13]; // 20 + }; + static_assert(sizeof(ExtraLeveledCreature) == 0x88); + class __declspec(novtable) ExtraInstanceData : public BSExtraData // 00 { diff --git a/CommonLibF4/include/RE/Bethesda/BSInputEnableManager.h b/CommonLibF4/include/RE/Bethesda/BSInputEnableManager.h new file mode 100644 index 00000000..1fa05f0d --- /dev/null +++ b/CommonLibF4/include/RE/Bethesda/BSInputEnableManager.h @@ -0,0 +1,145 @@ +#pragma once + +#include "RE/Bethesda/BSFixedString.h" +#include "RE/Bethesda/BSLock.h" +#include "RE/Bethesda/BSTArray.h" +#include "RE/Bethesda/BSTSingleton.h" +#include "RE/Bethesda/BSTSmartPointer.h" +#include "RE/Bethesda/UserEvents.h" + +namespace RE +{ + class InputEnableLayerDestroyedEvent; + class OtherEventEnabledEvent; + class UserEventEnabledEvent; + + namespace OtherInputEvents + { + enum class OTHER_EVENT_FLAG + { + kAll = static_cast>(-1), + + kJournalTabs = 1 << 0, + kActivation = 1 << 1, + kFastTravel = 1 << 2, + kPOVChange = 1 << 3, + kVATS = 1 << 4, + kFavorites = 1 << 5, + kPipboyLight = 1 << 6, + kZKey = 1 << 7, + kRunning = 1 << 8, + kCursor = 1 << 9, + kSprinting = 1 << 10, + }; + } + + using OEFlag = OtherInputEvents::OTHER_EVENT_FLAG; + + class BSInputEnableLayer + { + public: + constexpr BSInputEnableLayer() noexcept {} // NOLINT(modernize-use-equals-default) + + [[nodiscard]] std::uint32_t DecRef() const + { + using func_t = decltype(&BSInputEnableLayer::DecRef); + REL::Relocation func{ REL::ID(659989) }; + return func(this); + } + + std::uint32_t IncRef() const + { + stl::atomic_ref myRefCount{ refCount }; + return ++myRefCount; + } + + // members + std::uint32_t layerID; // 00 + mutable std::uint32_t refCount; // 04 + }; + static_assert(sizeof(BSInputEnableLayer) == 0x08); + + class BSInputEnableManager : + public BSTEventSource, // 000 + public BSTEventSource, // 058 + public BSTEventSource, // 0B0 + public BSTSingletonSDM // 108 + { + public: + struct EnableLayer + { + public: + // members + stl::enumeration inputUserEvents; // 00 + stl::enumeration otherInputEvents; // 04 + }; + static_assert(sizeof(EnableLayer) == 0x08); + + [[nodiscard]] static BSInputEnableManager* GetSingleton() + { + REL::Relocation singleton{ REL::ID(781703) }; + return *singleton; + } + + bool AllocateNewLayer(BSTSmartPointer& a_layer, const char* a_debugName) + { + using func_t = decltype(&BSInputEnableManager::AllocateNewLayer); + REL::Relocation func{ REL::ID(537494) }; + return func(this, a_layer, a_debugName); + } + + void ClearForcedState() + { + BSAutoLock locker(cacheLock); + forceEnableInputUserEventsFlags.reset(); + forceOtherInputEventsFlags.reset(); + } + + bool EnableUserEvent(std::uint32_t a_layerID, UEFlag a_userEventFlags, bool a_enable, UserEvents::SENDER_ID a_senderID) + { + using func_t = decltype(&BSInputEnableManager::EnableUserEvent); + REL::Relocation func{ REL::ID(1432984) }; + return func(this, a_layerID, a_userEventFlags, a_enable, a_senderID); + } + + bool EnableOtherEvent(std::uint32_t a_layerID, OEFlag a_otherEventFlags, bool a_enable, UserEvents::SENDER_ID a_senderID) + { + using func_t = decltype(&BSInputEnableManager::EnableOtherEvent); + REL::Relocation func{ REL::ID(1419268) }; + return func(this, a_layerID, a_otherEventFlags, a_enable, a_senderID); + } + + void ForceUserEventEnabled(UEFlag a_userEventFlags, bool a_enable) + { + BSAutoLock locker(cacheLock); + if (a_enable) { + forceEnableInputUserEventsFlags.set(a_userEventFlags); + } else { + forceEnableInputUserEventsFlags.reset(a_userEventFlags); + } + } + + void ForceOtherEventEnabled(OEFlag a_otherEventFlags, bool a_enable) + { + BSAutoLock locker(cacheLock); + if (a_enable) { + forceOtherInputEventsFlags.set(a_otherEventFlags); + } else { + forceOtherInputEventsFlags.reset(a_otherEventFlags); + } + } + + // members + BSSpinLock cacheLock; // 110 + stl::enumeration cachedInputUserEventsFlags; // 118 + stl::enumeration cachedOtherInputEventsFlags; // 11C + stl::enumeration forceEnableInputUserEventsFlags; // 120 + stl::enumeration forceOtherInputEventsFlags; // 124 + BSSpinLock layerLock; // 128 + BSTArray layers; // 130 + BSTArray> layerWrappers; // 148 + BSTArray debugNames; // 160 + bool isCurrentlyInSaveLoad; // 178 + }; + static_assert(sizeof(BSInputEnableManager) == 0x180); +} diff --git a/CommonLibF4/include/RE/Bethesda/BSPointerHandle.h b/CommonLibF4/include/RE/Bethesda/BSPointerHandle.h index b612d438..a4d036fc 100644 --- a/CommonLibF4/include/RE/Bethesda/BSPointerHandle.h +++ b/CommonLibF4/include/RE/Bethesda/BSPointerHandle.h @@ -168,14 +168,14 @@ namespace RE static BSPointerHandle GetHandle(T* a_ptr) { using func_t = decltype(&BSPointerHandleManagerInterface::GetHandle); - REL::Relocation func{ REL::ID(901626) }; + REL::Relocation func{ REL::ID(2188676) }; return func(a_ptr); } static bool GetSmartPointer(const BSPointerHandle& a_handle, NiPointer& a_smartPointerOut) { using func_t = decltype(&BSPointerHandleManagerInterface::GetSmartPointer); - REL::Relocation func{ REL::ID(2188369) }; + REL::Relocation func{ REL::ID(2188681) }; return func(a_handle, a_smartPointerOut); } }; diff --git a/CommonLibF4/include/RE/Bethesda/ControlMap.h b/CommonLibF4/include/RE/Bethesda/ControlMap.h index a45ac15c..ff3b1733 100644 --- a/CommonLibF4/include/RE/Bethesda/ControlMap.h +++ b/CommonLibF4/include/RE/Bethesda/ControlMap.h @@ -20,6 +20,13 @@ namespace RE public BSTSingletonSDM // 000 { public: + using InputContextID = UserEvents::INPUT_CONTEXT_ID; + + enum : std::uint32_t + { + kInvalid = static_cast(-1) + }; + struct UserEventMapping { public: @@ -59,6 +66,24 @@ namespace RE return *singleton; } + std::uint32_t GetMappedKey(std::string_view a_eventID, INPUT_DEVICE a_device, InputContextID a_context = InputContextID::kMainGameplay) const + { + assert(a_device < INPUT_DEVICE::kTotal); + assert(a_context < InputContextID::kTotal); + + if (controlMaps[std::to_underlying(a_context)]) { + const auto& mappings = controlMaps[std::to_underlying(a_context)]->deviceMappings[std::to_underlying(a_device)]; + BSFixedString eventID(a_eventID); + for (auto& mapping : mappings) { + if (mapping.eventID == eventID) { + return mapping.inputKey; + } + } + } + + return kInvalid; + } + bool PopInputContext(UserEvents::INPUT_CONTEXT_ID a_context) { using func_t = decltype(&ControlMap::PopInputContext); diff --git a/CommonLibF4/include/RE/Bethesda/Events.h b/CommonLibF4/include/RE/Bethesda/Events.h index c9bb4807..f503ffa2 100644 --- a/CommonLibF4/include/RE/Bethesda/Events.h +++ b/CommonLibF4/include/RE/Bethesda/Events.h @@ -582,7 +582,7 @@ namespace RE [[nodiscard]] static BSTEventSource* GetEventSource() { using func_t = decltype(&TESDeathEvent::GetEventSource); - REL::Relocation func{ REL::ID(1465690) }; + REL::Relocation func{ REL::ID(2201833) }; return func(); } @@ -612,6 +612,21 @@ namespace RE }; static_assert(sizeof(TESEquipEvent) == 0x18); + struct TESFormDeleteEvent + { + public: + [[nodiscard]] static BSTEventSource* GetEventSource() + { + using func_t = decltype(&TESFormDeleteEvent::GetEventSource); + REL::Relocation func{ REL::ID(2201842) }; + return func(); + } + + // members + std::uint32_t formID; // 00 + }; + static_assert(sizeof(TESFormDeleteEvent) == 0x04); + struct TESFurnitureEvent { public: diff --git a/CommonLibF4/include/RE/Bethesda/FormComponents.h b/CommonLibF4/include/RE/Bethesda/FormComponents.h index 8bd029f9..43daad54 100644 --- a/CommonLibF4/include/RE/Bethesda/FormComponents.h +++ b/CommonLibF4/include/RE/Bethesda/FormComponents.h @@ -1,5 +1,6 @@ #pragma once +#include "RE/Bethesda/BSContainer.h" #include "RE/Bethesda/BSFixedString.h" #include "RE/Bethesda/BSStringT.h" #include "RE/Bethesda/BSTArray.h" @@ -900,17 +901,32 @@ namespace RE // add virtual BGSKeyword* GetDefaultKeyword() const { return nullptr; } // 07 + void CopyKeywords(const std::vector& a_copiedData); + void AddKeyword(BGSKeyword* a_keyword) { using func_t = decltype(&BGSKeywordForm::AddKeyword); - REL::Relocation func{ REL::ID(762999) }; + REL::Relocation func{ REL::ID(2192766) }; return func(this, a_keyword); } + bool AddKeywords(const std::vector& a_keywords); + [[nodiscard]] bool ContainsKeywordString(std::string_view a_editorID) const; [[nodiscard]] bool HasKeywordID(std::uint32_t a_formID) const; [[nodiscard]] bool HasKeywordString(std::string_view a_editorID) const; + void ForEachKeyword(std::function a_callback) const + { + if (keywords) { + for (std::uint32_t idx = 0; idx < numKeywords; ++idx) { + if (keywords[idx] && a_callback(keywords[idx]) == BSContainer::ForEachResult::kStop) { + return; + } + } + } + } + [[nodiscard]] std::optional GetKeywordAt(std::uint32_t a_idx) const { if (a_idx < numKeywords) { @@ -941,6 +957,8 @@ namespace RE return func(this, a_keyword); } + bool RemoveKeywords(const std::vector& a_keywords); + // members BGSKeyword** keywords; // 10 std::uint32_t numKeywords; // 18 @@ -1216,6 +1234,20 @@ namespace RE static constexpr auto RTTI{ RTTI::TESContainer }; static constexpr auto VTABLE{ VTABLE::TESContainer }; + void CopyObjectList(const std::vector& a_copiedData) + { + const auto oldData = containerObjects; + + const auto newSize = a_copiedData.size(); + const auto newData = calloc(newSize); + std::ranges::copy(a_copiedData, newData); + + numContainerObjects = static_cast(newSize); + containerObjects = newData; + + free(oldData); + } + void ForEachContainerObject(std::function a_fn) const { for (std::uint32_t i = 0; i < numContainerObjects; ++i) { @@ -1242,22 +1274,33 @@ namespace RE std::vector copiedData{ containerObjects, containerObjects + numContainerObjects }; const auto newObj = new ContainerObject(a_object, a_count, a_owner); copiedData.push_back(newObj); - - const auto oldData = containerObjects; - - const auto newSize = copiedData.size(); - const auto newData = calloc(newSize); - std::ranges::copy(copiedData, newData); - - numContainerObjects = static_cast(newSize); - containerObjects = newData; - free(oldData); - + CopyObjectList(copiedData); return true; } return added; } + bool AddObjectsToContainer(std::map& a_objects, TESForm* a_owner) + { + for (std::uint32_t i = 0; i < numContainerObjects; ++i) { + if (const auto entry = containerObjects[i]; entry && entry->obj) { + if (auto it = a_objects.find(entry->obj); it != a_objects.end()) { + entry->count += it->second; + a_objects.erase(it); + } + } + } + if (!a_objects.empty()) { + std::vector copiedData{ containerObjects, containerObjects + numContainerObjects }; + for (auto& [object, count] : a_objects) { + const auto newObj = new ContainerObject(object, count, a_owner); + copiedData.push_back(newObj); + } + CopyObjectList(copiedData); + } + return true; + } + // members ContainerObject** containerObjects; // 08 std::uint32_t numContainerObjects; // 10 @@ -1555,6 +1598,13 @@ namespace RE [[nodiscard]] constexpr bool UsesOppositeGenderAnims() const noexcept { return actorData.actorBaseFlags.all(ACTOR_BASE_DATA::Flag::kOppositeGenderanims); } [[nodiscard]] constexpr bool UsesTemplate() const noexcept { return actorData.actorBaseFlags.all(ACTOR_BASE_DATA::Flag::kUsesTemplate); } + std::uint16_t GetLevel() const + { + using func_t = decltype(&TESActorBaseData::GetLevel); + REL::Relocation func{ REL::ID(2192891) }; + return func(this); + } + // members ACTOR_BASE_DATA actorData; // 08 std::int32_t changeFlags; // 1C @@ -1632,6 +1682,30 @@ namespace RE }; static_assert(sizeof(TESImageSpaceModifiableForm) == 0x10); + struct INSTANCE_FILTER + { + public: + // members + std::uint32_t levelOverride; // 00 + std::uint8_t tierStartLevel; // 04 + std::uint8_t altLevelsPerTier; // 05 + bool epic; // 06 + BSScrapArray> keywordChances; // 08 + }; + static_assert(sizeof(INSTANCE_FILTER) == 0x28); + + struct CALCED_OBJECT + { + public: + // members + TESBoundObject* object; // 00 + const char* overrideName; // 08 + std::int32_t count; // 10 + ContainerItemExtra itemExtra; // 18 + INSTANCE_FILTER instanceFilter; // 30 + }; + static_assert(sizeof(CALCED_OBJECT) == 0x58); + struct LEVELED_OBJECT { public: @@ -1651,6 +1725,14 @@ namespace RE static constexpr auto RTTI{ RTTI::TESLeveledList }; static constexpr auto VTABLE{ VTABLE::TESLeveledList }; + enum class LeveledListAllBelowForce + { + kNever = -1, + kDefault = 0, + kAlways = 1, + kShiftUp = 2, + }; + // add virtual std::int8_t GetChanceNone(); // 07 virtual bool GetMultCalc(); // 08 @@ -1665,6 +1747,27 @@ namespace RE return func(this, a_level, a_count, a_chanceNone, a_item, a_itemExtra); } + void CalculateCurrentFormList( + std::uint16_t a_level, + std::uint16_t a_count, + BSScrapArray& a_outCont, + LeveledListAllBelowForce a_allBelowForce = LeveledListAllBelowForce::kDefault, + bool a_clampToPlayer = false, + INSTANCE_FILTER* a_instanceFilter = nullptr, + const char* a_overrideName = nullptr) + { + using func_t = decltype(&TESLeveledList::CalculateCurrentFormList); + REL::Relocation func{ REL::ID(2193259) }; + return func(this, a_level, a_count, a_outCont, a_allBelowForce, a_clampToPlayer, a_instanceFilter, a_overrideName); + } + + void CalculateCurrentFormListForRef(TESObjectREFR* a_ref, BSScrapArray& a_outCont, bool a_legendary) + { + using func_t = decltype(&TESLeveledList::CalculateCurrentFormListForRef); + REL::Relocation func{ REL::ID(2193260) }; + return func(this, a_ref, a_outCont, a_legendary); + } + bool GetUseAll() { using func_t = decltype(&TESLeveledList::GetUseAll); @@ -1800,6 +1903,98 @@ namespace RE F4_HEAP_REDEFINE_NEW(SpellData); + void CopySpellList(const std::vector& a_copiedData) + { + const auto oldData = levSpells; + + const auto newSize = a_copiedData.size(); + const auto newData = calloc(newSize); + std::ranges::copy(a_copiedData, newData); + + numLevSpells = static_cast(newSize); + levSpells = newData; + + free(oldData); + } + + void CopySpellList(const std::vector& a_copiedData) + { + const auto oldData = spells; + + const auto newSize = a_copiedData.size(); + const auto newData = calloc(newSize); + std::ranges::copy(a_copiedData, newData); + + numSpells = static_cast(newSize); + spells = newData; + + free(oldData); + } + + bool AddLevSpells(const std::vector& a_levSpells) + { + std::vector copiedData{ levSpells, levSpells + numLevSpells }; + std::ranges::remove_copy_if(a_levSpells, std::back_inserter(copiedData), [&](auto& spell) { + return std::ranges::find(copiedData, spell) != copiedData.end(); + }); + CopySpellList(copiedData); + return true; + } + + bool AddSpells(const std::vector& a_spells) + { + std::vector copiedData{ spells, spells + numSpells }; + std::ranges::remove_copy_if(a_spells, std::back_inserter(copiedData), [&](auto& spell) { + return std::ranges::find(copiedData, spell) != copiedData.end(); + }); + CopySpellList(copiedData); + return true; + } + + std::optional GetIndex(const SpellItem* a_spell) const + { + if (spells) { + for (std::uint32_t i = 0; i < numSpells; i++) { + if (spells[i] == a_spell) { + return i; + } + } + } + return std::nullopt; + } + + std::optional GetIndex(const TESLevSpell* a_levSpell) const + { + if (levSpells) { + for (std::uint32_t i = 0; i < numLevSpells; i++) { + if (levSpells[i] == a_levSpell) { + return i; + } + } + } + return std::nullopt; + } + + bool RemoveLevSpells(const std::vector& a_levSpells) + { + std::vector copiedData{ levSpells, levSpells + numLevSpells }; + if (std::erase_if(copiedData, [&](auto& spell) { return std::ranges::find(a_levSpells, spell) != a_levSpells.end(); }) > 0) { + CopySpellList(copiedData); + return true; + } + return false; + } + + bool RemoveSpells(const std::vector& a_spells) + { + std::vector copiedData{ spells, spells + numSpells }; + if (std::erase_if(copiedData, [&](auto& spell) { return std::ranges::find(a_spells, spell) != a_spells.end(); }) > 0) { + CopySpellList(copiedData); + return true; + } + return false; + } + // members SpellItem** spells; // 00 TESLevSpell** levSpells; // 08 diff --git a/CommonLibF4/include/RE/Bethesda/FormFactory.h b/CommonLibF4/include/RE/Bethesda/FormFactory.h index 8d0fec3a..04139dee 100644 --- a/CommonLibF4/include/RE/Bethesda/FormFactory.h +++ b/CommonLibF4/include/RE/Bethesda/FormFactory.h @@ -36,7 +36,7 @@ namespace RE -> std::span { constexpr auto len = std::to_underlying(ENUM_FORM_ID::kTotal); - REL::Relocation factories{ REL::ID(228366) }; + REL::Relocation factories{ REL::ID(2689177) }; return { *factories }; } }; diff --git a/CommonLibF4/include/RE/Bethesda/IMenu.h b/CommonLibF4/include/RE/Bethesda/IMenu.h index 9cd01c0b..7b8f98b2 100644 --- a/CommonLibF4/include/RE/Bethesda/IMenu.h +++ b/CommonLibF4/include/RE/Bethesda/IMenu.h @@ -55,6 +55,7 @@ namespace RE class BSGFxShaderFXTarget; class ExamineMenu; class ExtraDataList; + class HUDComponentBase; class MenuOpenCloseEvent; class MessageBoxData; class NiAVObject; @@ -75,6 +76,7 @@ namespace RE struct PickRefUpdateEvent; struct PipboyValueChangedEvent; struct QueueSurvivalBumpDownMessage; + struct RequestHUDModesEvent; struct RevertPlayerCharacterEvent; struct SaveLoadMessageTypeEvent; struct UIAdvanceMenusFunctionCompleteEvent; @@ -859,6 +861,31 @@ namespace RE }; static_assert(sizeof(Console) == 0xF0); + struct __declspec(novtable) HUDMenu : + public GameMenuBase, // 000 + public BSTEventSink, // 0E0 + public BSTEventSink // 0E8 + { + public: + static constexpr auto RTTI{ RTTI::HUDMenu }; + static constexpr auto VTABLE{ VTABLE::HUDMenu }; + static constexpr auto MENU_NAME{ "HUDMenu"sv }; + + enum class ShowMenuState + { + kConstructed = 0, + kShown, + kHidden, + kReshowOnDestructor + }; + + // members + BSTSmallArray, 32> hudObjects; // 0F0 + BSTArray hudModes; // 200 + stl::enumeration hudShowMenuState; // 218 + }; + static_assert(sizeof(HUDMenu) == 0x220); + struct BaseLoadedInventoryModel { public: diff --git a/CommonLibF4/include/RE/Bethesda/InputEvent.h b/CommonLibF4/include/RE/Bethesda/InputEvent.h index 98fb8815..abd2dd99 100644 --- a/CommonLibF4/include/RE/Bethesda/InputEvent.h +++ b/CommonLibF4/include/RE/Bethesda/InputEvent.h @@ -31,6 +31,7 @@ namespace RE kBackspace = 0x08, kTab = 0x09, kEnter = 0x0D, + kPause = 0x13, kCapsLock = 0x14, kEscape = 0x1B, kSpace = 0x20, @@ -42,6 +43,7 @@ namespace RE kUp = 0x26, kRight = 0x27, kDown = 0x28, + kPrintScreen = 0x2C, kInsert = 0x2D, kDelete = 0x2E, k0 = 0x30, @@ -123,6 +125,8 @@ namespace RE kRControl = 0xA3, kLAlt = 0xA4, kRAlt = 0xA5, + kNumLock = 0x90, + kScrollLock = 0x91, kGamepad = 0x10000, kDPAD_Up = 0x10001, @@ -131,7 +135,8 @@ namespace RE kDPAD_Right = 0x10008, kLTrigger = 0x10009, kRTrigger = 0x1000A, - kSelect = 0x10020, + kStart = 0x10010, + kBack = 0x10020, kLStick = 0x10040, kRStick = 0x10080, kLShoulder = 0x10100, @@ -140,6 +145,9 @@ namespace RE kBButton = 0x12000, kXButton = 0x14000, kYButton = 0x18000, + + kWheelUp = 0x10800, + kWheelDown = 0x10900, }; class IDEvent; @@ -320,7 +328,9 @@ namespace RE [[nodiscard]] bool QHeldDown() const noexcept { return value != 0.0 && heldDownSecs > 0.0F; } [[nodiscard]] float QHeldDownSecs() const noexcept { return heldDownSecs; } [[nodiscard]] bool QJustPressed() const noexcept { return value != 0.0F && heldDownSecs == 0.0F; } + [[nodiscard]] bool QPressed() const noexcept { return value != 0.0F; } [[nodiscard]] bool QReleased(float a_heldDownSecs) const noexcept { return value == 0.0F && a_heldDownSecs <= heldDownSecs; } + [[nodiscard]] bool QReleased() const noexcept { return value == 0.0F && heldDownSecs > 0.0F; } // members float value{ 0.0F }; // 38 diff --git a/CommonLibF4/include/RE/Bethesda/Sky.h b/CommonLibF4/include/RE/Bethesda/Sky.h index 861acb7b..2f447019 100644 --- a/CommonLibF4/include/RE/Bethesda/Sky.h +++ b/CommonLibF4/include/RE/Bethesda/Sky.h @@ -73,8 +73,9 @@ namespace RE [[nodiscard]] static Sky* GetSingleton() { - REL::Relocation singleton{ REL::ID(484694) }; - return *singleton; + using func_t = decltype(&Sky::GetSingleton); + REL::Relocation func{ REL::ID(484694) }; + return func(); } void ForceWeather(TESWeather* a_weather, bool a_override) diff --git a/CommonLibF4/include/RE/Bethesda/TESBoundAnimObjects.h b/CommonLibF4/include/RE/Bethesda/TESBoundAnimObjects.h index a13f01d6..a732de0d 100644 --- a/CommonLibF4/include/RE/Bethesda/TESBoundAnimObjects.h +++ b/CommonLibF4/include/RE/Bethesda/TESBoundAnimObjects.h @@ -362,6 +362,8 @@ namespace RE }; static_assert(sizeof(HeadRelatedData) == 0x18); + void CopyPerkRankArray(const std::vector& a_copiedData); + bool AddPerk(BGSPerk* a_perk, std::int8_t a_rank) { if (!GetPerkIndex(a_perk)) { @@ -379,6 +381,8 @@ namespace RE return false; } + bool AddPerks(const std::vector& a_perks, std::int8_t a_rank); + [[nodiscard]] bool ContainsKeyword(std::string_view a_editorID) const; [[nodiscard]] static BSTHashMap>& GetAlternateHeadPartListMap() @@ -453,6 +457,8 @@ namespace RE [[nodiscard]] bool HasApplicableKeywordString(std::string_view a_editorID) const; + bool RemovePerks(const std::vector& a_perks); + [[nodiscard]] bool IsInFaction(const TESFaction* a_faction) { for (const auto& faction : factions) { diff --git a/CommonLibF4/include/RE/Bethesda/TESForms.h b/CommonLibF4/include/RE/Bethesda/TESForms.h index 8f00edaf..e63d7f66 100644 --- a/CommonLibF4/include/RE/Bethesda/TESForms.h +++ b/CommonLibF4/include/RE/Bethesda/TESForms.h @@ -803,6 +803,17 @@ namespace RE return func(a_formTypeString); } + [[nodiscard]] static const char* GetFormTypeString(ENUM_FORM_ID a_formType) + { + auto formEnumString = GetFormEnumString(); + return formEnumString[std::to_underlying(a_formType)].formString; + } + + [[nodiscard]] const char* GetFormTypeString() const + { + return GetFormTypeString(GetFormType()); + } + [[nodiscard]] std::uint32_t GetFormFlags() const noexcept { return formFlags; } [[nodiscard]] std::uint32_t GetFormID() const noexcept { return formID; } [[nodiscard]] ENUM_FORM_ID GetFormType() const noexcept { return *formType; } @@ -1530,7 +1541,7 @@ namespace RE [[nodiscard]] TESRegionList* GetRegionList(bool a_createIfMissing) { using func_t = decltype(&TESObjectCELL::GetRegionList); - REL::Relocation func{ REL::ID(1565031) }; + REL::Relocation func{ REL::ID(2200265) }; return func(this, a_createIfMissing); } @@ -2032,6 +2043,23 @@ namespace RE return func(this, a_form); } + void ForEachForm(std::function a_callback) const + { + for (const auto& form : arrayOfForms) { + if (form && a_callback(form) == BSContainer::ForEachResult::kStop) { + return; + } + } + if (scriptAddedTempForms) { + for (const auto& addedFormID : *scriptAddedTempForms) { + const auto addedForm = TESForm::GetFormByID(addedFormID); + if (addedForm && a_callback(addedForm) == BSContainer::ForEachResult::kStop) { + return; + } + } + } + } + [[nodiscard]] std::optional GetItemIndex(const TESForm& a_item) const noexcept { if (scriptAddedTempForms) { @@ -3409,3 +3437,47 @@ namespace RE }; static_assert(sizeof(BGSGodRays) == 0x60); } + +namespace std +{ + [[nodiscard]] inline std::string to_string(RE::ENUM_FORM_ID a_formType) + { + return RE::TESForm::GetFormTypeString(a_formType); + } +} + +#ifdef FMT_VERSION +namespace fmt +{ + template <> + struct formatter + { + template + constexpr auto parse(ParseContext& a_ctx) + { + return a_ctx.begin(); + } + + template + auto format(const RE::ENUM_FORM_ID& a_formType, FormatContext& a_ctx) + { + return fmt::format_to(a_ctx.out(), "{}", RE::TESForm::GetFormTypeString(a_formType)); + } + }; +} +#endif + +#ifdef __cpp_lib_format +namespace std +{ + template + struct formatter : std::formatter + { + template + auto format(RE::ENUM_FORM_ID a_formType, FormatContext& a_ctx) + { + return formatter::format(RE::TESForm::GetFormTypeString(a_formType), a_ctx); + } + }; +} +#endif diff --git a/CommonLibF4/include/RE/Bethesda/TESObjectREFRs.h b/CommonLibF4/include/RE/Bethesda/TESObjectREFRs.h index 85835e3b..eb668890 100644 --- a/CommonLibF4/include/RE/Bethesda/TESObjectREFRs.h +++ b/CommonLibF4/include/RE/Bethesda/TESObjectREFRs.h @@ -366,6 +366,13 @@ namespace RE } } + void BuildFromContainer(const TESContainer* a_container) + { + using func_t = decltype(&BGSInventoryList::BuildFromContainer); + REL::Relocation func{ REL::ID(2194158) }; + return func(this, a_container); + } + // members BSTArray data; // 58 float cachedWeight; // 70 @@ -834,6 +841,13 @@ namespace RE return func(this); } + [[nodiscard]] bool IsLeveledCreature() + { + using func_t = decltype(&TESObjectREFR::IsLeveledCreature); + REL::Relocation func{ REL::ID(2202655) }; + return func(this); + } + void MarkAsDeleted() { using func_t = decltype(&TESObjectREFR::MarkAsDeleted); diff --git a/CommonLibF4/include/RE/Bethesda/UserEvents.h b/CommonLibF4/include/RE/Bethesda/UserEvents.h index 8f673ba1..35263b63 100644 --- a/CommonLibF4/include/RE/Bethesda/UserEvents.h +++ b/CommonLibF4/include/RE/Bethesda/UserEvents.h @@ -71,4 +71,6 @@ namespace RE kScript }; } + + using UEFlag = UserEvents::USER_EVENT_FLAG; } diff --git a/CommonLibF4/include/RE/Fallout.h b/CommonLibF4/include/RE/Fallout.h index 311670f3..eedbbf88 100644 --- a/CommonLibF4/include/RE/Fallout.h +++ b/CommonLibF4/include/RE/Fallout.h @@ -41,6 +41,7 @@ #include "RE/Bethesda/BSGraphics.h" #include "RE/Bethesda/BSHavok.h" #include "RE/Bethesda/BSInputDeviceManager.h" +#include "RE/Bethesda/BSInputEnableManager.h" #include "RE/Bethesda/BSInputEventReceiver.h" #include "RE/Bethesda/BSInputEventSingleUser.h" #include "RE/Bethesda/BSInputEventUser.h" diff --git a/CommonLibF4/include/REX/PS4.h b/CommonLibF4/include/REX/PS4.h new file mode 100644 index 00000000..8d7b0f3f --- /dev/null +++ b/CommonLibF4/include/REX/PS4.h @@ -0,0 +1,3 @@ +#pragma once + +#include "REX/PS4/SCEPAD.h" diff --git a/CommonLibF4/include/REX/PS4/SCEPAD.h b/CommonLibF4/include/REX/PS4/SCEPAD.h new file mode 100644 index 00000000..fa88dfc2 --- /dev/null +++ b/CommonLibF4/include/REX/PS4/SCEPAD.h @@ -0,0 +1,27 @@ +#pragma once + +namespace REX::PS4 +{ + enum SCE_PAD_BUTTON : std::uint32_t + { + SCE_PAD_BUTTON_SHARE = 0x00000001, + SCE_PAD_BUTTON_L3 = 0x00000002, + SCE_PAD_BUTTON_R3 = 0x00000004, + SCE_PAD_BUTTON_OPTIONS = 0x00000008, + SCE_PAD_BUTTON_UP = 0x00000010, + SCE_PAD_BUTTON_RIGHT = 0x00000020, + SCE_PAD_BUTTON_DOWN = 0x00000040, + SCE_PAD_BUTTON_LEFT = 0x00000080, + SCE_PAD_BUTTON_L2 = 0x00000100, + SCE_PAD_BUTTON_R2 = 0x00000200, + SCE_PAD_BUTTON_L1 = 0x00000400, + SCE_PAD_BUTTON_R1 = 0x00000800, + SCE_PAD_BUTTON_TRIANGLE = 0x00001000, + SCE_PAD_BUTTON_CIRCLE = 0x00002000, + SCE_PAD_BUTTON_CROSS = 0x00004000, + SCE_PAD_BUTTON_SQUARE = 0x00008000, + SCE_PAD_BUTTON_PLAYSTATION = 0x00010000, + SCE_PAD_BUTTON_TOUCH_PAD = 0x00100000, + SCE_PAD_BUTTON_INTERCEPTED = 0x80000000, + }; +} diff --git a/CommonLibF4/src/F4SE/InputMap.cpp b/CommonLibF4/src/F4SE/InputMap.cpp new file mode 100644 index 00000000..24772bbd --- /dev/null +++ b/CommonLibF4/src/F4SE/InputMap.cpp @@ -0,0 +1,192 @@ +#include "F4SE/InputMap.h" + +#include "RE/Bethesda/ControlMap.h" + +#include "REX/PS4/SCEPAD.h" +#include "REX/W32/DINPUT.h" +#include "REX/W32/USER32.h" +#include "REX/W32/XINPUT.h" + +namespace F4SE +{ + std::uint32_t InputMap::XInputToScePadOffset(std::uint32_t keyMask) + { + switch (keyMask) { + case REX::W32::XINPUT_GAMEPAD_DPAD_UP: + return REX::PS4::SCE_PAD_BUTTON_UP; + case REX::W32::XINPUT_GAMEPAD_DPAD_DOWN: + return REX::PS4::SCE_PAD_BUTTON_DOWN; + case REX::W32::XINPUT_GAMEPAD_DPAD_LEFT: + return REX::PS4::SCE_PAD_BUTTON_LEFT; + case REX::W32::XINPUT_GAMEPAD_DPAD_RIGHT: + return REX::PS4::SCE_PAD_BUTTON_RIGHT; + case REX::W32::XINPUT_GAMEPAD_START: + return REX::PS4::SCE_PAD_BUTTON_OPTIONS; + case REX::W32::XINPUT_GAMEPAD_BACK: + return REX::PS4::SCE_PAD_BUTTON_TOUCH_PAD; + case REX::W32::XINPUT_GAMEPAD_LEFT_THUMB: + return REX::PS4::SCE_PAD_BUTTON_L3; + case REX::W32::XINPUT_GAMEPAD_RIGHT_THUMB: + return REX::PS4::SCE_PAD_BUTTON_R3; + case REX::W32::XINPUT_GAMEPAD_LEFT_SHOULDER: + return REX::PS4::SCE_PAD_BUTTON_L1; + case REX::W32::XINPUT_GAMEPAD_RIGHT_SHOULDER: + return REX::PS4::SCE_PAD_BUTTON_R1; + case REX::W32::XINPUT_GAMEPAD_A: + return REX::PS4::SCE_PAD_BUTTON_CROSS; + case REX::W32::XINPUT_GAMEPAD_B: + return REX::PS4::SCE_PAD_BUTTON_CIRCLE; + case REX::W32::XINPUT_GAMEPAD_X: + return REX::PS4::SCE_PAD_BUTTON_SQUARE; + case REX::W32::XINPUT_GAMEPAD_Y: + return REX::PS4::SCE_PAD_BUTTON_TRIANGLE; + default: + return keyMask; + } + } + + std::uint32_t InputMap::ScePadOffsetToXInput(std::uint32_t keyMask) + { + switch (keyMask) { + case REX::PS4::SCE_PAD_BUTTON_UP: + return REX::W32::XINPUT_GAMEPAD_DPAD_UP; + case REX::PS4::SCE_PAD_BUTTON_DOWN: + return REX::W32::XINPUT_GAMEPAD_DPAD_DOWN; + case REX::PS4::SCE_PAD_BUTTON_LEFT: + return REX::W32::XINPUT_GAMEPAD_DPAD_LEFT; + case REX::PS4::SCE_PAD_BUTTON_RIGHT: + return REX::W32::XINPUT_GAMEPAD_DPAD_RIGHT; + case REX::PS4::SCE_PAD_BUTTON_OPTIONS: + return REX::W32::XINPUT_GAMEPAD_START; + case REX::PS4::SCE_PAD_BUTTON_TOUCH_PAD: + return REX::W32::XINPUT_GAMEPAD_BACK; + case REX::PS4::SCE_PAD_BUTTON_L3: + return REX::W32::XINPUT_GAMEPAD_LEFT_THUMB; + case REX::PS4::SCE_PAD_BUTTON_R3: + return REX::W32::XINPUT_GAMEPAD_RIGHT_THUMB; + case REX::PS4::SCE_PAD_BUTTON_L1: + return REX::W32::XINPUT_GAMEPAD_LEFT_SHOULDER; + case REX::PS4::SCE_PAD_BUTTON_R1: + return REX::W32::XINPUT_GAMEPAD_RIGHT_SHOULDER; + case REX::PS4::SCE_PAD_BUTTON_CROSS: + return REX::W32::XINPUT_GAMEPAD_A; + case REX::PS4::SCE_PAD_BUTTON_CIRCLE: + return REX::W32::XINPUT_GAMEPAD_B; + case REX::PS4::SCE_PAD_BUTTON_SQUARE: + return REX::W32::XINPUT_GAMEPAD_X; + case REX::PS4::SCE_PAD_BUTTON_TRIANGLE: + return REX::W32::XINPUT_GAMEPAD_Y; + default: + return keyMask; + } + } + + std::uint32_t InputMap::GamepadMaskToKeycode(std::uint32_t keyMask) + { + if (RE::ControlMap::GetSingleton()->pcGamePadMapType == RE::PC_GAMEPAD_TYPE::kOrbis) { + keyMask = ScePadOffsetToXInput(keyMask); + } + + switch (keyMask) { + case REX::W32::XINPUT_GAMEPAD_DPAD_UP: + return kGamepadButtonOffset_DPAD_UP; + case REX::W32::XINPUT_GAMEPAD_DPAD_DOWN: + return kGamepadButtonOffset_DPAD_DOWN; + case REX::W32::XINPUT_GAMEPAD_DPAD_LEFT: + return kGamepadButtonOffset_DPAD_LEFT; + case REX::W32::XINPUT_GAMEPAD_DPAD_RIGHT: + return kGamepadButtonOffset_DPAD_RIGHT; + case REX::W32::XINPUT_GAMEPAD_START: + return kGamepadButtonOffset_START; + case REX::W32::XINPUT_GAMEPAD_BACK: + return kGamepadButtonOffset_BACK; + case REX::W32::XINPUT_GAMEPAD_LEFT_THUMB: + return kGamepadButtonOffset_LEFT_THUMB; + case REX::W32::XINPUT_GAMEPAD_RIGHT_THUMB: + return kGamepadButtonOffset_RIGHT_THUMB; + case REX::W32::XINPUT_GAMEPAD_LEFT_SHOULDER: + return kGamepadButtonOffset_LEFT_SHOULDER; + case REX::W32::XINPUT_GAMEPAD_RIGHT_SHOULDER: + return kGamepadButtonOffset_RIGHT_SHOULDER; + case REX::W32::XINPUT_GAMEPAD_A: + return kGamepadButtonOffset_A; + case REX::W32::XINPUT_GAMEPAD_B: + return kGamepadButtonOffset_B; + case REX::W32::XINPUT_GAMEPAD_X: + return kGamepadButtonOffset_X; + case REX::W32::XINPUT_GAMEPAD_Y: + return kGamepadButtonOffset_Y; + case 0x9: // Left Trigger game-defined ID + return kGamepadButtonOffset_LT; + case 0xA: // Right Trigger game-defined ID + return kGamepadButtonOffset_RT; + default: + return kMaxMacros; // Invalid + } + } + + std::uint32_t InputMap::GamepadKeycodeToMask(std::uint32_t keyCode) + { + std::uint32_t keyMask; + + switch (keyCode) { + case kGamepadButtonOffset_DPAD_UP: + keyMask = REX::W32::XINPUT_GAMEPAD_DPAD_UP; + break; + case kGamepadButtonOffset_DPAD_DOWN: + keyMask = REX::W32::XINPUT_GAMEPAD_DPAD_DOWN; + break; + case kGamepadButtonOffset_DPAD_LEFT: + keyMask = REX::W32::XINPUT_GAMEPAD_DPAD_LEFT; + break; + case kGamepadButtonOffset_DPAD_RIGHT: + keyMask = REX::W32::XINPUT_GAMEPAD_DPAD_RIGHT; + break; + case kGamepadButtonOffset_START: + keyMask = REX::W32::XINPUT_GAMEPAD_START; + break; + case kGamepadButtonOffset_BACK: + keyMask = REX::W32::XINPUT_GAMEPAD_BACK; + break; + case kGamepadButtonOffset_LEFT_THUMB: + keyMask = REX::W32::XINPUT_GAMEPAD_LEFT_THUMB; + break; + case kGamepadButtonOffset_RIGHT_THUMB: + keyMask = REX::W32::XINPUT_GAMEPAD_RIGHT_THUMB; + break; + case kGamepadButtonOffset_LEFT_SHOULDER: + keyMask = REX::W32::XINPUT_GAMEPAD_LEFT_SHOULDER; + break; + case kGamepadButtonOffset_RIGHT_SHOULDER: + keyMask = REX::W32::XINPUT_GAMEPAD_RIGHT_SHOULDER; + break; + case kGamepadButtonOffset_A: + keyMask = REX::W32::XINPUT_GAMEPAD_A; + break; + case kGamepadButtonOffset_B: + keyMask = REX::W32::XINPUT_GAMEPAD_B; + break; + case kGamepadButtonOffset_X: + keyMask = REX::W32::XINPUT_GAMEPAD_X; + break; + case kGamepadButtonOffset_Y: + keyMask = REX::W32::XINPUT_GAMEPAD_Y; + break; + case kGamepadButtonOffset_LT: + keyMask = 0x9; // Left Trigger game-defined ID + break; + case kGamepadButtonOffset_RT: + keyMask = 0xA; // Right Trigger game-defined ID + break; + default: + keyMask = 0xFF; // Invalid + break; + } + + if (RE::ControlMap::GetSingleton()->pcGamePadMapType == RE::PC_GAMEPAD_TYPE::kOrbis) { + keyMask = XInputToScePadOffset(keyMask); + } + + return keyMask; + } +} diff --git a/CommonLibF4/src/RE/Bethesda/FormComponents.cpp b/CommonLibF4/src/RE/Bethesda/FormComponents.cpp index 7a2c3e4c..f198f1de 100644 --- a/CommonLibF4/src/RE/Bethesda/FormComponents.cpp +++ b/CommonLibF4/src/RE/Bethesda/FormComponents.cpp @@ -19,42 +19,78 @@ namespace RE } } + void BGSKeywordForm::CopyKeywords(const std::vector& a_copiedData) + { + const auto oldData = keywords; + + const auto newSize = a_copiedData.size(); + const auto newData = calloc(newSize); + std::ranges::copy(a_copiedData, newData); + + numKeywords = static_cast(newSize); + keywords = newData; + + free(oldData); + } + + bool BGSKeywordForm::AddKeywords(const std::vector& a_keywords) + { + std::vector copiedData{ keywords, keywords + numKeywords }; + std::ranges::remove_copy_if(a_keywords, std::back_inserter(copiedData), [&](auto& keyword) { + return std::ranges::find(copiedData, keyword) != copiedData.end(); + }); + CopyKeywords(copiedData); + return true; + } + bool BGSKeywordForm::ContainsKeywordString(std::string_view a_editorID) const { - if (keywords) { - for (std::uint32_t idx = 0; idx < numKeywords; ++idx) { - if (keywords[idx] && keywords[idx]->formEditorID.contains(a_editorID)) { - return true; - } + bool result = false; + ForEachKeyword([&](const BGSKeyword* a_keyword) { + if (a_keyword->formEditorID.contains(a_editorID)) { + result = true; + return BSContainer::ForEachResult::kStop; } - } + return BSContainer::ForEachResult::kContinue; + }); + return result; return false; } bool BGSKeywordForm::HasKeywordID(std::uint32_t a_formID) const { - if (keywords) { - for (std::uint32_t idx = 0; idx < numKeywords; ++idx) { - if (keywords[idx] && keywords[idx]->formID == a_formID) { - return true; - } + bool result = false; + ForEachKeyword([&](const BGSKeyword* a_keyword) { + if (a_keyword->GetFormID() == a_formID) { + result = true; + return BSContainer::ForEachResult::kStop; } - } - - return false; + return BSContainer::ForEachResult::kContinue; + }); + return result; } bool BGSKeywordForm::HasKeywordString(std::string_view a_editorID) const { - if (keywords) { - for (std::uint32_t idx = 0; idx < numKeywords; ++idx) { - if (keywords[idx] && keywords[idx]->formEditorID == a_editorID) { - return true; - } + bool result = false; + ForEachKeyword([&](const BGSKeyword* a_keyword) { + if (a_keyword->formEditorID == a_editorID) { + result = true; + return BSContainer::ForEachResult::kStop; } - } + return BSContainer::ForEachResult::kContinue; + }); + return result; + } + bool BGSKeywordForm::RemoveKeywords(const std::vector& a_keywords) + { + std::vector copiedData{ keywords, keywords + numKeywords }; + if (std::erase_if(copiedData, [&](auto& keyword) { return std::ranges::find(a_keywords, keyword) != a_keywords.end(); }) > 0) { + CopyKeywords(copiedData); + return true; + } return false; } diff --git a/CommonLibF4/src/RE/Bethesda/TESBoundAnimObjects.cpp b/CommonLibF4/src/RE/Bethesda/TESBoundAnimObjects.cpp index a8f70a04..1207077a 100644 --- a/CommonLibF4/src/RE/Bethesda/TESBoundAnimObjects.cpp +++ b/CommonLibF4/src/RE/Bethesda/TESBoundAnimObjects.cpp @@ -5,6 +5,33 @@ namespace RE { + void TESNPC::CopyPerkRankArray(const std::vector& a_copiedData) + { + const auto oldData = perks; + + const auto newSize = a_copiedData.size(); + const auto newData = calloc(newSize); + std::ranges::copy(a_copiedData, newData); + + perkCount = static_cast(newSize); + perks = newData; + + free(oldData); + } + + bool TESNPC::AddPerks(const std::vector& a_perks, std::int8_t a_rank) + { + std::vector copiedData{ perks, perks + perkCount }; + std::ranges::for_each(a_perks, [&](auto& perk) { + if (!GetPerkIndex(perk)) { + const auto newPerk = new PerkRankData(perk, a_rank); + copiedData.push_back(*newPerk); + } + }); + CopyPerkRankArray(copiedData); + return true; + } + bool TESNPC::ContainsKeyword(std::string_view a_editorID) const { if (ContainsKeywordString(a_editorID)) { @@ -27,6 +54,16 @@ namespace RE return false; } + bool TESNPC::RemovePerks(const std::vector& a_perks) + { + std::vector copiedData{ perks, perks + perkCount }; + if (std::erase_if(copiedData, [&](auto& perkRank) { return std::ranges::find(a_perks, perkRank.perk) != a_perks.end(); }) > 0) { + CopyPerkRankArray(copiedData); + return true; + } + return false; + } + bool TESNPC::UsingAlternateHeadPartList() const { if (const auto player = PlayerCharacter::GetSingleton(); IsPlayer() && player) {