diff --git a/include/RE/A/Actor.h b/include/RE/A/Actor.h index 852b676ed..80a0ea4e0 100644 --- a/include/RE/A/Actor.h +++ b/include/RE/A/Actor.h @@ -679,7 +679,7 @@ namespace RE InventoryEntryData* GetAttackingWeapon(); const InventoryEntryData* GetAttackingWeapon() const; bhkCharacterController* GetCharController() const; - uint32_t GetCollisionFilterInfo(uint32_t& a_outCollisionFilterInfo); + std::uint32_t GetCollisionFilterInfo(std::uint32_t& a_outCollisionFilterInfo); NiPointer GetCommandingActor() const; TESFaction* GetCrimeFaction(); const TESFaction* GetCrimeFaction() const; @@ -723,6 +723,7 @@ namespace RE bool HasPerk(BGSPerk* a_perk) const; bool HasShout(TESShout* a_shout) const; bool HasSpell(SpellItem* a_spell) const; + void InitiateDoNothingPackage(); void InterruptCast(bool a_restoreMagicka) const; bool IsAttacking() const; bool IsAIEnabled() const; diff --git a/include/RE/T/TESForm.h b/include/RE/T/TESForm.h index 1ecbf6cf1..88a04f6b8 100644 --- a/include/RE/T/TESForm.h +++ b/include/RE/T/TESForm.h @@ -301,6 +301,7 @@ namespace RE [[nodiscard]] const char* GetName() const; [[nodiscard]] float GetWeight() const; [[nodiscard]] bool HasKeywordInArray(const std::vector& a_keywords, bool a_matchAll) const; + [[nodiscard]] bool HasAnyKeywordByEditorID(const std::vector& editorIDs) const; [[nodiscard]] bool HasKeywordInList(BGSListForm* a_keywordList, bool a_matchAll) const; [[nodiscard]] bool HasVMAD() const; [[nodiscard]] bool HasWorldModel() const noexcept; @@ -326,7 +327,15 @@ namespace RE [[nodiscard]] bool IsInitialized() const noexcept { return (GetFormFlags() & RecordFlags::kInitialized) != 0; } [[nodiscard]] bool IsKey() const noexcept { return Is(FormType::KeyMaster); } [[nodiscard]] bool IsLockpick() const noexcept { return GetFormID() == 0x0000000A; } - + /** + * @brief Checks if the Form represents Skooma. + * + * Determines whether the FormID matches one of the known form IDs for Skooma. + * + * @return True if the FormID is either 0x00057A7A or 0x0201391D, indicating that it is Skooma or RedWater Skooma. + * + */ + [[nodiscard]] bool IsSkooma() const noexcept { return (GetFormID() == 0x00057A7A || GetFormID() == 0x0201391D); } [[nodiscard]] bool IsNot(FormType a_type) const noexcept { return !Is(a_type); } template diff --git a/include/RE/T/TESObjectREFR.h b/include/RE/T/TESObjectREFR.h index 4eb9b62aa..b46867673 100644 --- a/include/RE/T/TESObjectREFR.h +++ b/include/RE/T/TESObjectREFR.h @@ -1,5 +1,6 @@ #pragma once +#include "RE/B/BGSDefaultObjectManager.h" #include "RE/B/BSFixedString.h" #include "RE/B/BSHandleRefObject.h" #include "RE/B/BSPointerHandle.h" @@ -456,18 +457,23 @@ namespace RE bool HasKeyword(const BGSKeyword* a_keyword) const; bool HasKeywordInArray(const std::vector& a_keywords, bool a_matchAll) const; bool HasKeywordInList(BGSListForm* a_keywordList, bool a_matchAll) const; + bool HasKeywordWithType(DEFAULT_OBJECT keywordType) const; bool HasQuestObject() const; void InitChildActivates(TESObjectREFR* a_actionRef); bool InitInventoryIfRequired(bool a_ignoreContainerExtraData = false); bool Is3DLoaded() const; bool IsActivationBlocked() const; + bool IsAnimal() const; bool IsAnOwner(const Actor* a_testOwner, bool a_useFaction, bool a_requiresOwner) const; bool IsCrimeToActivate(); bool IsDisabled() const; + bool IsDragon() const; bool IsEnchanted() const; bool IsHorse() const; + bool IsHumanoid() const; bool IsInitiallyDisabled() const; bool IsInWater() const; + bool IsJewelry() const; bool IsLocked() const; bool IsMarkedForDeletion() const; bool IsOffLimits(); @@ -477,6 +483,7 @@ namespace RE void MoveTo(TESObjectREFR* a_target); bool MoveToNode(TESObjectREFR* a_target, const BSFixedString& a_nodeName); bool MoveToNode(TESObjectREFR* a_target, NiAVObject* a_node); + bool NameIncludes(std::string a_word); NiPointer PlaceObjectAtMe(TESBoundObject* a_baseToPlace, bool a_forcePersist) const; void PlayAnimation(stl::zstring a_from, stl::zstring a_to); void PlayAnimation(NiControllerManager* a_manager, NiControllerSequence* a_toSeq, NiControllerSequence* a_fromSeq); diff --git a/include/RE/T/TESPackage.h b/include/RE/T/TESPackage.h index 20a8a258b..117632712 100644 --- a/include/RE/T/TESPackage.h +++ b/include/RE/T/TESPackage.h @@ -72,7 +72,14 @@ namespace RE kPickPocketWarning = 35, kMovementBlocked = 36, kVampireFeed = 37, - kCannibal = 38 + kCannibal = 38, + kUnk39 = 39, + kUnk40 = 40, + kUnk41 = 41, + kUnk42 = 42, + kUnk43 = 43, + kUnk44 = 44, + kUnk45 = 45, }; enum class PACK_EVENT_ACTION_TYPE diff --git a/src/RE/A/Actor.cpp b/src/RE/A/Actor.cpp index b154c3a9a..d1d99ed9b 100644 --- a/src/RE/A/Actor.cpp +++ b/src/RE/A/Actor.cpp @@ -674,6 +674,13 @@ namespace RE return func(this, a_spell); } + void Actor::InitiateDoNothingPackage() + { + using func_t = decltype(&Actor::InitiateDoNothingPackage); + REL::Relocation func{ RELOCATION_ID(36408, 37402) }; + return func(this); + } + void Actor::InterruptCast(bool a_restoreMagicka) const { using func_t = decltype(&Actor::InterruptCast); diff --git a/src/RE/T/TESForm.cpp b/src/RE/T/TESForm.cpp index 2014eb626..26dd962e3 100644 --- a/src/RE/T/TESForm.cpp +++ b/src/RE/T/TESForm.cpp @@ -62,6 +62,34 @@ namespace RE } } + bool TESForm::HasAnyKeywordByEditorID(const std::vector& editorIDs) const + { + // Try to cast to a keyword form interface + const auto keywordForm = As(); + if (!keywordForm) { + return false; + } + + // Iterate through the keywords + for (std::uint32_t i = 0; i < keywordForm->GetNumKeywords(); ++i) { + auto keywordOpt = keywordForm->GetKeywordAt(i); + if (keywordOpt) { + auto keyword = *keywordOpt; + if (keyword) { + const char* keywordEditorID = keyword->GetFormEditorID(); + if (keywordEditorID) { + // Check if the keywordEditorID is in the given editorIDs vector + if (std::find(editorIDs.begin(), editorIDs.end(), keywordEditorID) != editorIDs.end()) { + return true; + } + } + } + } + } + + return false; + } + bool TESForm::HasKeywordInArray(const std::vector& a_keywords, bool a_matchAll) const { const auto keywordForm = As(); diff --git a/src/RE/T/TESObjectREFR.cpp b/src/RE/T/TESObjectREFR.cpp index 0ff676787..0ae76986f 100644 --- a/src/RE/T/TESObjectREFR.cpp +++ b/src/RE/T/TESObjectREFR.cpp @@ -583,6 +583,17 @@ namespace RE return hasKeyword; } + bool TESObjectREFR::HasKeywordWithType(DEFAULT_OBJECT keywordType) const + { + auto dobj = BGSDefaultObjectManager::GetSingleton(); + if (!dobj) { + return false; + } + + auto keyword = dobj->GetObject(keywordType); + return keyword ? HasKeyword(keyword) : false; + } + bool TESObjectREFR::HasQuestObject() const { using func_t = decltype(&TESObjectREFR::HasQuestObject); @@ -615,6 +626,11 @@ namespace RE return xFlags && xFlags->IsActivationBlocked(); } + bool TESObjectREFR::IsAnimal() const + { + return HasKeywordWithType(DEFAULT_OBJECT::kKeywordAnimal); + } + bool TESObjectREFR::IsAnOwner(const Actor* a_testOwner, bool a_useFaction, bool a_requiresOwner) const { using func_t = decltype(&TESObjectREFR::IsAnOwner); @@ -634,6 +650,11 @@ namespace RE return (GetFormFlags() & RecordFlags::kInitiallyDisabled) != 0; } + bool TESObjectREFR::IsDragon() const + { + return HasKeywordWithType(DEFAULT_OBJECT::kKeywordDragon); + } + bool TESObjectREFR::IsEnchanted() const { auto xEnch = extraList.GetByType(); @@ -654,13 +675,12 @@ namespace RE bool TESObjectREFR::IsHorse() const { - auto dobj = BGSDefaultObjectManager::GetSingleton(); - if (!dobj) { - return false; - } + return HasKeywordWithType(DEFAULT_OBJECT::kKeywordHorse); + } - auto keyword = dobj->GetObject(DEFAULT_OBJECT::kKeywordHorse); - return keyword ? HasKeyword(keyword) : false; + bool TESObjectREFR::IsHumanoid() const + { + return HasKeywordWithType(DEFAULT_OBJECT::kKeywordNPC); } bool TESObjectREFR::IsInitiallyDisabled() const @@ -668,6 +688,11 @@ namespace RE return (GetFormFlags() & RecordFlags::kInitiallyDisabled) != 0; } + bool TESObjectREFR::IsJewelry() const + { + return HasKeywordWithType(DEFAULT_OBJECT::kKeywordJewelry); + } + bool TESObjectREFR::IsInWater() const { return GetWaterHeight() > GetPositionZ(); @@ -750,6 +775,14 @@ namespace RE return true; } + bool TESObjectREFR::NameIncludes(std::string a_word) + { + auto obj = GetObjectReference(); + std::string name = obj ? obj->GetName() : ""; + + return name.find(a_word) != std::string::npos; + } + NiPointer TESObjectREFR::PlaceObjectAtMe(TESBoundObject* a_baseToPlace, bool a_forcePersist) const { const auto handle = TESDataHandler::GetSingleton()->CreateReferenceAtLocation(a_baseToPlace, GetPosition(), GetAngle(), GetParentCell(), GetWorldspace(), nullptr, nullptr, ObjectRefHandle(), a_forcePersist, true);