#pragma once #include "RE/B/BSInputEventReceiver.h" #include "RE/B/BSSpinLock.h" #include "RE/B/BSTEvent.h" #include "RE/B/BSTHashMap.h" #include "RE/B/BSTSingleton.h" #include "RE/B/BSTimer.h" #include "RE/I/IMenu.h" #include "RE/U/UIMenuEntry.h" namespace RE { class MenuModeChangeEvent; class MenuOpenCloseEvent; class MenuModeCounterChangedEvent; class TutorialEvent; class __declspec(novtable) UI : public BSInputEventReceiver, // 000 public BSTSingletonSDM, // 010 public BSTEventSource, // 018 public BSTEventSource, // 070 public BSTEventSource, // 0C8 public BSTEventSource // 120 { public: static constexpr auto RTTI{ RTTI::UI }; static constexpr auto VTABLE{ VTABLE::UI }; using Create_t = typename UIMenuEntry::Create_t; using StaticUpdate_t = typename UIMenuEntry::StaticUpdate_t; // add virtual ~UI() = default; // 01 [[nodiscard]] bool CustomRendererHasQuads(const BSFixedString& a_customRendererName) { using func_t = decltype(&UI::CustomRendererHasQuads); static REL::Relocation func{ ID::UI::CustomRendererHasQuads }; return func(this, a_customRendererName); } template [[nodiscard]] BSTEventSource* GetEventSource() { return static_cast*>(this); } [[nodiscard]] static BSReadWriteLock& GetMenuMapRWLock() { static REL::Relocation menuMapRWLock{ ID::UI::GetMenuMapRWLock }; return *menuMapRWLock; } [[nodiscard]] static UI* GetSingleton() { static REL::Relocation singleton{ ID::UI::Singleton }; return *singleton; } [[nodiscard]] Scaleform::Ptr GetMenu(const BSFixedString& a_name) const { BSAutoReadLock l{ GetMenuMapRWLock() }; const auto it = menuMap.find(a_name); return it != menuMap.end() ? it->second.menu : nullptr; } template [[nodiscard]] Scaleform::Ptr GetMenu() const requires(requires { T::MENU_NAME; }) { const auto ptr = GetMenu(T::MENU_NAME); return Scaleform::Ptr{ static_cast(ptr.get()) }; } [[nodiscard]] bool GetMenuOpen(const BSFixedString& a_name) const { const auto menu = GetMenu(a_name); return menu ? menu->OnStack() : false; } template [[nodiscard]] bool GetMenuOpen() const requires(requires { T::MENU_NAME; }) { return GetMenuOpen(T::MENU_NAME); } void RefreshCursor() { using func_t = decltype(&UI::RefreshCursor); static REL::Relocation func{ ID::UI::RefreshCursor }; return func(this); } void RegisterMenu(const char* a_menu, Create_t* a_create, StaticUpdate_t* a_staticUpdate = nullptr) { using func_t = decltype(&UI::RegisterMenu); static REL::Relocation func{ ID::UI::RegisterMenu }; return func(this, a_menu, a_create, a_staticUpdate); } template void RegisterSink(BSTEventSink* a_sink) { GetEventSource()->RegisterSink(a_sink); } void UpdateControllerType() { using func_t = decltype(&UI::UpdateControllerType); static REL::Relocation func{ ID::UI::UpdateControllerType }; return func(this); } template void UnregisterSink(BSTEventSink* a_sink) { GetEventSource()->UnregisterSink(a_sink); } // members BSTArray releasedMovies; // 178 BSTArray> menuStack; // 190 BSTHashMap menuMap; // 1A8 BSSpinLock processMessagesLock; // 1D8 std::uint32_t menuMode; // 1E0 BSTAtomicValue itemMenuMode; // 1E4 BSTAtomicValue pauseMenuDisableCt; // 1E8 std::uint32_t freezeFrameMenuBG; // 1EC std::uint32_t freezeFramePause; // 1F0 std::uint32_t savingDisabled; // 1F4 std::uint32_t disablesCompanion; // 1F8 std::uint32_t largeCacheRenderModeCount; // 1FC std::uint32_t movementToDirectionalCount; // 200 BSTimer uiTimer; // 208 bool menuSystemVisible; // 258 bool closingAllMenus; // 259 bool freezeFrameScreenshotReady; // 25A }; static_assert(sizeof(UI) == 0x260); }