diff --git a/CommonLibSF/include/RE/E/ExtraLock.h b/CommonLibSF/include/RE/E/ExtraLock.h new file mode 100644 index 00000000..491ec265 --- /dev/null +++ b/CommonLibSF/include/RE/E/ExtraLock.h @@ -0,0 +1,57 @@ +#pragma once + +#include "RE/B/BSExtraData.h" + +namespace RE +{ + class TESKey; + class TESObjectREFR; + + enum class LOCK_LEVEL + { + kUnlocked = static_cast>(-1), + kEasy = 0, + kAverage = 1, + kHard = 2, + kVeryHard = 3, + kRequiresKey = 4, + kInaccessible = 5, + kTerminal = 6, + kBarred = 7, + kChained = 8, + }; + + struct REFR_LOCK + { + enum class Flag + { + kNone = 0, + kLocked = 1 << 0, + kLeveled = 1 << 2 + }; + + [[nodiscard]] LOCK_LEVEL GetLockLevel(const TESObjectREFR* a_owner) const; + [[nodiscard]] constexpr bool IsLocked() const noexcept { return flags.all(Flag::kLocked); } + void SetLocked(bool a_locked); + + // members + TESKey* key; // 00 + std::uint32_t numTries; // 08 + stl::enumeration flags; // 0C + std::int8_t baseLevel; // 0D + }; + static_assert(sizeof(REFR_LOCK) == 0x10); + + class ExtraLock : public BSExtraData + { + public: + SF_RTTI_VTABLE(ExtraLock); + SF_EXTRADATATYPE(Lock); + + ~ExtraLock() override; // 00 + + // members + REFR_LOCK* lock; // 18 + }; + static_assert(sizeof(ExtraLock) == 0x20); +} diff --git a/CommonLibSF/include/RE/IDs.h b/CommonLibSF/include/RE/IDs.h index 7be86a8a..d637fbba 100644 --- a/CommonLibSF/include/RE/IDs.h +++ b/CommonLibSF/include/RE/IDs.h @@ -117,6 +117,12 @@ namespace RE::ID inline constexpr REL::ID HasType{ 83208 }; } + namespace LockPickedEvent + { + inline constexpr REL::ID GetEventSource{ 107115 }; + inline constexpr REL::ID Notify{ 107339 }; + } + namespace MemoryManager { inline constexpr REL::ID GetSingleton{ 33961 }; @@ -144,6 +150,11 @@ namespace RE::ID inline constexpr REL::ID singleton{ 865059 }; } + namespace REFR_LOCK + { + inline constexpr REL::ID GetLockLevel{ 84103 }; + } + namespace RegSettingCollection { inline constexpr REL::ID singleton{ 885510 }; @@ -218,8 +229,10 @@ namespace RE::ID namespace TESObjectREFR { - inline constexpr REL::ID GetCurrentLocation{ 106554 }; + inline constexpr REL::ID AddLockChange{ 106386 }; + inline constexpr REL::ID GetCurrentLocation{ 106554 }; inline constexpr REL::ID GetLinkedRef{ 107578 }; + inline constexpr REL::ID GetLock{ 107581 }; inline constexpr REL::ID GetParentWorldSpace{ 106696 }; inline constexpr REL::ID GetSpaceship{ 173851 }; inline constexpr REL::ID GetSpaceshipParentDock{ 174134 }; diff --git a/CommonLibSF/include/RE/L/LockPickedEvent.h b/CommonLibSF/include/RE/L/LockPickedEvent.h new file mode 100644 index 00000000..c4d2a2b9 --- /dev/null +++ b/CommonLibSF/include/RE/L/LockPickedEvent.h @@ -0,0 +1,42 @@ +#pragma once + +#include "RE/B/BSTEvent.h" +#include "RE/E/ExtraLock.h" +#include "RE/N/NiSmartPointer.h" + +namespace RE +{ + class TESObjectREFR; + class BGSTerminalMenu; + + struct LockPickedEvent + { + public: + [[nodiscard]] static BSTEventSource* GetEventSource() + { + using func_t = decltype(&LockPickedEvent::GetEventSource); + REL::Relocation func{ ID::LockPickedEvent::GetEventSource }; + return func(); + } + + static void Notify(const NiPointer& a_actionRef, const NiPointer& a_lock, bool a_succeeded, bool a_isOffLimits, LOCK_LEVEL lockLevel, std::uint32_t a_numDigiPicksUsed, float a_arg7 = 0.0f, float a_arg8 = 0.0f, BGSTerminalMenu* a_terminalMenu = nullptr, std::int32_t a_arg10 = -1) + { + using func_t = decltype(&LockPickedEvent::Notify); + REL::Relocation func{ ID::LockPickedEvent::Notify }; + return func(a_actionRef, a_lock, a_succeeded, a_isOffLimits, lockLevel, a_numDigiPicksUsed, a_arg7, a_arg8, a_terminalMenu, a_arg10); + } + + // members + NiPointer actionRef; // 00 + NiPointer lock; // 08 + BGSTerminalMenu* terminalMenu; // 10 + std::int32_t unk18; // 14 - terminalMenu related + std::uint32_t numDigiPicksUsed; // 18 + float unk20; // 20 - 0.0f + float unk24; // 24 - 0.0f + stl::enumeration lockLevel; // 28 + bool succeeded; // 2C + bool isOffLimits; // 2D + }; + static_assert(sizeof(LockPickedEvent) == 0x30); +} diff --git a/CommonLibSF/include/RE/T/TESObjectREFR.h b/CommonLibSF/include/RE/T/TESObjectREFR.h index 448fe95c..81dd510e 100644 --- a/CommonLibSF/include/RE/T/TESObjectREFR.h +++ b/CommonLibSF/include/RE/T/TESObjectREFR.h @@ -17,6 +17,7 @@ namespace RE { + enum class LOCK_LEVEL; class TESContainer; class Actor; class BGSEquipSlot; @@ -33,6 +34,7 @@ namespace RE class TESTopicInfo; class TESWorldSpace; class TESWaterForm; + struct REFR_LOCK; namespace ActorValueEvents { @@ -361,6 +363,8 @@ namespace RE [[nodiscard]] const TESBoundObject* GetBaseObject() const { return data.objectReference.get(); } [[nodiscard]] BGSLocation* GetCurrentLocation(); [[nodiscard]] TESObjectREFR* GetLinkedRef(BGSKeyword* a_keyword); + [[nodiscard]] REFR_LOCK* GetLock() const; + [[nodiscard]] LOCK_LEVEL GetLockLevel() const; [[nodiscard]] TESWorldSpace* GetParentWorldSpace(); [[nodiscard]] constexpr NiPoint3A GetPosition() const noexcept { return data.location; } [[nodiscard]] constexpr float GetPositionX() const noexcept { return data.location.x; } @@ -373,9 +377,12 @@ namespace RE [[nodiscard]] bool HasKeyword(BGSKeyword* a_keyword); [[nodiscard]] bool IsCrimeToActivate(); [[nodiscard]] bool IsInSpace(bool a_arg1); + [[nodiscard]] bool IsLocked() const; [[nodiscard]] bool IsObjectEquipped(TESBoundObject* a_object); [[nodiscard]] bool IsSpaceshipDocked(); [[nodiscard]] bool IsSpaceshipLanded(); + void Lock(); + void Unlock(); // members OBJ_REFR data; // 0A0 @@ -389,6 +396,9 @@ namespace RE std::uint16_t scale; // 108 bool unk10A; // 10A std::uint8_t flags; // 10B + + private: + void AddLockChange(); }; static_assert(sizeof(TESObjectREFR) == 0x110); } diff --git a/CommonLibSF/src/RE/E/ExtraLock.cpp b/CommonLibSF/src/RE/E/ExtraLock.cpp new file mode 100644 index 00000000..e31d0b44 --- /dev/null +++ b/CommonLibSF/src/RE/E/ExtraLock.cpp @@ -0,0 +1,25 @@ +#include "RE/E/ExtraLock.h" + +namespace RE +{ + LOCK_LEVEL REFR_LOCK::GetLockLevel(const TESObjectREFR* a_owner) const + { + if (IsLocked()) { + using func_t = decltype(&REFR_LOCK::GetLockLevel); + REL::Relocation func{ ID::REFR_LOCK::GetLockLevel }; + return func(this, a_owner); + } else { + return LOCK_LEVEL::kUnlocked; + } + } + + void REFR_LOCK::SetLocked(bool a_locked) + { + if (a_locked) { + flags.set(Flag::kLocked); + } else { + flags.reset(Flag::kLocked); + numTries = 0; + } + } +} diff --git a/CommonLibSF/src/RE/T/TESObjectREFR.cpp b/CommonLibSF/src/RE/T/TESObjectREFR.cpp index 0e393f2b..d1e8def2 100644 --- a/CommonLibSF/src/RE/T/TESObjectREFR.cpp +++ b/CommonLibSF/src/RE/T/TESObjectREFR.cpp @@ -1,8 +1,16 @@ #include "RE/T/TESObjectREFR.h" #include "RE/B/BGSInventoryItem.h" +#include "RE/E/ExtraLock.h" namespace RE { + void TESObjectREFR::AddLockChange() + { + using func_t = decltype(&TESObjectREFR::AddLockChange); + REL::Relocation func{ ID::TESObjectREFR::AddLockChange }; + return func(this); + } + void TESObjectREFR::ForEachEquippedItem(std::function a_callback) const { ForEachInventoryItem([&](const BGSInventoryItem& a_invItem) { @@ -36,6 +44,19 @@ namespace RE return func(this, a_keyword); } + REFR_LOCK* TESObjectREFR::GetLock() const + { + using func_t = decltype(&TESObjectREFR::GetLock); + REL::Relocation func{ ID::TESObjectREFR::GetLock }; + return func(this); + } + + LOCK_LEVEL TESObjectREFR::GetLockLevel() const + { + const auto state = GetLock(); + return state ? state->GetLockLevel(this) : LOCK_LEVEL::kUnlocked; + } + TESWorldSpace* TESObjectREFR::GetParentWorldSpace() { using func_t = decltype(&TESObjectREFR::GetParentWorldSpace); @@ -92,6 +113,11 @@ namespace RE return func(this, a_arg1); } + bool TESObjectREFR::IsLocked() const + { + return GetLockLevel() != LOCK_LEVEL::kUnlocked; + } + bool TESObjectREFR::IsObjectEquipped(TESBoundObject* a_object) { using func_t = decltype(&TESObjectREFR::IsObjectEquipped); @@ -113,4 +139,19 @@ namespace RE return func(this); } + void TESObjectREFR::Lock() + { + if (const auto lock = GetLock()) { + lock->SetLocked(true); + AddLockChange(); + } + } + + void TESObjectREFR::Unlock() + { + if (const auto lock = GetLock()) { + lock->SetLocked(false); + AddLockChange(); + } + } }