#pragma once #include "RE/B/BSFixedString.h" #include "RE/B/BSSpinLock.h" #include "RE/B/BSTHashMap.h" #include "RE/B/BaseFormComponent.h" #include "RE/E/ENUM_FORM_ID.h" #include "RE/F/FORM_ENUM_STRING.h" #include "RE/T/TESFile.h" #include "RE/T/TESFileContainer.h" namespace RE { class BGSLoadFormBuffer; class BGSPreviewTransform; class BGSSaveFormBuffer; class BGSSoundTagComponent; class __declspec(novtable) TESForm : public BaseFormComponent // 00 { public: static constexpr auto RTTI{ RTTI::TESForm }; static constexpr auto VTABLE{ VTABLE::TESForm }; static constexpr auto FORM_ID{ ENUM_FORM_ID::kNONE }; class FormSortFunc { public: std::int32_t operator()(const TESForm* a_arg1, const TESForm* a_arg2) { using func_t = decltype(&FormSortFunc::operator()); static REL::Relocation func{ ID::TESForm::FormSortFunc }; return func(this, a_arg1, a_arg2); } }; static_assert(std::is_empty_v); // override (BaseFormComponent) void InitializeDataComponent() override { return; } // 02 void ClearDataComponent() override { SetFormEditorID(""); } // 03 void CopyComponent(BaseFormComponent*) override { return; } // 06 // add virtual void InitializeData() { return; } // 07 virtual void ClearData() { return; } // 08 virtual bool Load([[maybe_unused]] TESFile* a_file) { return true; } // 09 virtual bool LoadPartial([[maybe_unused]] TESFile* a_file) { return true; } // 0A virtual bool LoadEdit(TESFile* a_file) { return Load(a_file); } // 0B virtual TESForm* CreateDuplicateForm(bool a_createEditorID, BSTHashMap* a_copyMap); // 0C virtual bool AddChange(std::uint32_t a_changeFlags); // 0D virtual void RemoveChange(std::uint32_t a_changeFlags); // 0E virtual bool FindInFileFast([[maybe_unused]] TESFile* a_file) { return false; } // 0F virtual bool CheckSaveGame([[maybe_unused]] BGSSaveFormBuffer* a_saveGameBuffer) const { return true; } // 10 virtual void SaveGame(BGSSaveFormBuffer* a_saveGameBuffer); // 11 virtual void LoadGame(BGSLoadFormBuffer* a_loadGameBuffer); // 12 virtual void InitLoadGame([[maybe_unused]] BGSLoadFormBuffer* a_loadGameBuffer) { return; } // 13 virtual void FinishLoadGame([[maybe_unused]] BGSLoadFormBuffer* a_loadGameBuffer) { return; } // 14 virtual void Revert([[maybe_unused]] BGSLoadFormBuffer* a_loadGameBuffer) { return; } // 15 virtual void InitItemImpl() { return; } // 16 virtual TESFile* GetRevertFile() const; // 17 virtual TESFile* GetDescriptionOwnerFile() const; // 18 virtual ENUM_FORM_ID GetSavedFormType() const { return *formType; } // 19 virtual void GetFormDetailedString(char* a_dest, std::uint32_t a_size) const; // 1A virtual bool GetKnown() const { return (formFlags >> 6) & 1; } // 1B virtual bool GetRandomAnim() const { return (formFlags >> 16) & 1; } // 1C virtual bool GetPlayable([[maybe_unused]] const TBO_InstanceData* a_data) const { return (formFlags >> 2) & 1; } // 1D virtual bool IsHeadingMarker() const { return false; } // 1E virtual bool IsHeadtrackMarker() const { return false; } // 1F virtual bool GetDangerous() const { return (formFlags >> 17) & 1; } // 20 virtual bool QHasCurrents() const { return (formFlags >> 19) & 1; } // 21 virtual bool GetObstacle() const { return (formFlags >> 25) & 1; } // 22 virtual bool QIsLODLandObject() const { return false; } // 23 virtual bool GetOnLocalMap() const { return (formFlags >> 9) & 1; } // 24 virtual bool GetMustUpdate() const { return (formFlags >> 8) & 1; } // 25 virtual void SetOnLocalMap(bool a_val); // 26 virtual bool GetIgnoredBySandbox() const { return false; } // 27 virtual void SetDelete(bool a_deleted); // 28 virtual void SetAltered(bool a_altered); // 29 virtual void SaveObjectBound() { return; } // 2A virtual void LoadObjectBound([[maybe_unused]] TESFile* a_file) { return; } // 2B virtual void SavePreviewTransform(CHUNK_ID) { return; } // 2C virtual void LoadPreviewTransform([[maybe_unused]] TESFile* a_file) { return; } // 2D virtual bool IsBoundObject() const { return false; } // 2E virtual bool IsObject() const { return false; } // 2F virtual bool IsMagicItem() const { return false; } // 30 virtual bool IsWater() const { return false; } // 31 virtual TESObjectREFR* IsReference() { return nullptr; } // 33 virtual const TESObjectREFR* IsReference() const { return nullptr; } // 32 virtual std::uint32_t GetRefCount() const { return 0; } // 34 virtual const char* GetTextForParsedSubTag(const BSFixedString& a_subTag) const; // 35 virtual void Copy([[maybe_unused]] TESForm* a_copy) { return; } // 36 virtual bool BelongsInGroup(FORM* a_groupFORM, bool a_allowParentGroups, bool a_currentOnly); // 37 virtual void CreateGroupData(FORM* a_outGroupFORM, FORM_GROUP* a_parentGroup); // 38 virtual std::uint32_t GetFormEditorIDLength() const; // 39 virtual const char* GetFormEditorID() const { return ""; } // 3A virtual bool SetFormEditorID([[maybe_unused]] const char* a_editorID) { return true; } // 3B virtual bool IsParentForm() { return false; } // 3C virtual bool IsParentFormTree() { return false; } // 3D virtual bool IsFormTypeChild([[maybe_unused]] std::uint8_t a_type) { return false; } // 3E virtual bool LoopingActivate(TESObjectREFR* a_itemActivated, TESObjectREFR* a_actionRef) { return Activate(a_itemActivated, a_actionRef, nullptr, 1); } // 3F virtual bool Activate([[maybe_unused]] TESObjectREFR* a_itemActivated, [[maybe_unused]] TESObjectREFR* a_actionRef, [[maybe_unused]] TESBoundObject* a_objectToGet, [[maybe_unused]] std::int32_t a_count) { return false; } // 40 virtual void SetFormID(TESFormID a_formID, bool a_updateFile); // 41 virtual const char* GetObjectTypeName() const { return ""; } // 42 virtual bool QAvailableInGame() const { return true; } // 43 virtual BGSMod::Template::Items* GetObjectTemplate() { return nullptr; } // 44 virtual BGSPreviewTransform* GetPreviewTransform() { return nullptr; } // 45 virtual BGSSoundTagComponent* GetSoundTagComponent() { return nullptr; } // 46 virtual std::uint32_t GetFilledSlots() const; // 47 virtual std::uint32_t GetFilledSlotsImpl() const { return static_cast(-1); } // 48 virtual float GetDesirability([[maybe_unused]] TBO_InstanceData* a_instanceData, [[maybe_unused]] const TESForm* a_user) const { return 0.0F; } // 49 static void AddCompileIndex(std::uint32_t& a_id, TESFile* a_file) { using func_t = decltype(&TESForm::AddCompileIndex); static REL::Relocation func{ ID::TESForm::AddCompileIndex }; return func(a_id, a_file); } [[nodiscard]] static auto GetAllForms() -> std::pair< BSTHashMap*, std::reference_wrapper> { static REL::Relocation**> allForms{ ID::TESForm::AllForms }; static REL::Relocation allFormsMapLock{ ID::TESForm::AllFormsMapLock }; return { *allForms, *allFormsMapLock }; } [[nodiscard]] static auto GetAllFormsByEditorID() -> std::pair< BSTHashMap*, std::reference_wrapper> { static REL::Relocation**> allFormsByEditorID{ ID::TESForm::AllFormsByEditorID }; static REL::Relocation allFormsEditorIDMapLock{ ID::TESForm::AllFormsEditorIDMapLock }; return { *allFormsByEditorID, *allFormsEditorIDMapLock }; } [[nodiscard]] bool GetDelete() { return (formFlags & 0x20); } [[nodiscard]] TESFile* GetFile(std::int32_t a_index = -1) const { using func_t = decltype(&TESForm::GetFile); static REL::Relocation func{ ID::TESForm::GetFile }; return func(this, a_index); } [[nodiscard]] static TESForm* GetFormByID(TESFormID a_formID) { const auto& [map, lock] = GetAllForms(); BSAutoReadLock l{ lock }; if (map) { const auto it = map->find(a_formID); return it != map->end() ? it->second : nullptr; } else { return nullptr; } } template [[nodiscard]] static T* GetFormByID(TESFormID a_formID) { const auto form = GetFormByID(a_formID); return form ? form->As() : nullptr; } [[nodiscard]] static TESForm* GetFormByEditorID(const BSFixedString& a_editorID) { const auto& [map, lock] = GetAllFormsByEditorID(); BSAutoReadLock l{ lock }; if (map) { const auto it = map->find(a_editorID); return it != map->end() ? it->second : nullptr; } else { return nullptr; } } template [[nodiscard]] static T* GetFormByEditorID(const BSFixedString& a_editorID) { const auto form = GetFormByEditorID(a_editorID); return form ? form->As() : nullptr; } [[nodiscard]] static std::uint32_t GetFormCode(ENUM_FORM_ID a_formType) { auto formEnumString = GetFormEnumString(); return formEnumString[std::to_underlying(a_formType)].formCode; } [[nodiscard]] std::uint32_t GetFormCode() const { return GetFormCode(GetFormType()); } [[nodiscard]] static std::span GetFormEnumString() { static REL::Relocation functions{ ID::TESForm::GetFormEnumString }; return { *functions }; } [[nodiscard]] static ENUM_FORM_ID GetFormTypeFromString(const char* a_formTypeString) { using func_t = decltype(&TESForm::GetFormTypeFromString); static REL::Relocation func{ ID::TESForm::GetFormTypeFromString }; 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]] TESFormID GetFormID() const noexcept { return formID; } [[nodiscard]] ENUM_FORM_ID GetFormType() const noexcept { return *formType; } [[nodiscard]] TESFormID GetLocalFormID() { auto file = GetFile(0); std::uint32_t fileIndex = file->compileIndex << (3 * 8); fileIndex += file->smallFileCompileIndex << ((1 * 8) + 4); return formID & ~fileIndex; } [[nodiscard]] bool Is(ENUM_FORM_ID a_type) const noexcept { return GetFormType() == a_type; } template [[nodiscard]] bool Is(Args... a_args) const noexcept // requires(std::same_as && ...) { return (Is(a_args) || ...); } template [[nodiscard]] bool Is() const noexcept // requires(std::derived_from && !std::is_pointer_v && !std::is_reference_v) { return Is(T::FORM_ID); } [[nodiscard]] bool IsActor() const noexcept { return Is(ENUM_FORM_ID::kACHR); } [[nodiscard]] bool IsAlchemyItem() const noexcept { return Is(ENUM_FORM_ID::kALCH); } [[nodiscard]] bool IsCreated() const noexcept { return (formID >> (8 * 3)) == 0xFF; } [[nodiscard]] bool IsDeleted() noexcept { return (formFlags & (1u << 5)) != 0; } [[nodiscard]] bool IsDisabled() noexcept { return (formFlags & (1u << 11)) != 0; } [[nodiscard]] bool IsInitialized() const noexcept { return (formFlags & (1u << 3)) != 0; } [[nodiscard]] bool IsNot(ENUM_FORM_ID a_type) const noexcept { return !Is(a_type); } template [[nodiscard]] bool IsNot(Args... a_args) const noexcept // requires(std::same_as && ...) { return (IsNot(a_args) && ...); } [[nodiscard]] bool IsPlayer() const noexcept { return GetFormID() == 0x00000007; } [[nodiscard]] bool IsPlayerRef() const noexcept { return GetFormID() == 0x00000014; } [[nodiscard]] bool IsWeapon() const noexcept { return Is(ENUM_FORM_ID::kWEAP); } void SetInGameFormFlags(std::uint16_t a_flags, bool a_value) { if (a_value) { inGameFormFlags |= a_flags; AddChange(1); } else { inGameFormFlags &= ~a_flags; AddChange(1); } } void SetTemporary() { using func_t = decltype(&TESForm::SetTemporary); static REL::Relocation func{ ID::TESForm::SetTemporary }; return func(this); } static TESForm* GetFormByNumericID(std::uint32_t a_searchID) { using func_t = decltype(&TESForm::GetFormByNumericID); static REL::Relocation func{ ID::TESForm::GetFormByNumericID }; return func(a_searchID); } template < class T, class = std::enable_if_t< std::negation_v< std::disjunction< std::is_pointer, std::is_reference, std::is_const, std::is_volatile>>>> [[nodiscard]] T* As() noexcept; template < class T, class = std::enable_if_t< std::negation_v< std::disjunction< std::is_pointer, std::is_reference, std::is_const, std::is_volatile>>>> [[nodiscard]] const T* As() const noexcept; // members TESFileContainer sourceFiles; // 08 std::uint32_t formFlags; // 10 TESFormID formID; // 14 std::uint16_t inGameFormFlags; // 18 REX::TEnumSet formType; // 1A }; static_assert(sizeof(TESForm) == 0x20); } 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