#pragma once inline bool bTabBlocked = false; class DialogueMenuPatch final : public RE::DialogueMenu { public: static void Install() { REL::Relocation vtbl(RE::VTABLE_DialogueMenu[0]); _ProcessMessageFn = vtbl.write_vfunc(0x4, &ProcessMessageEx); } static void BlockTab(bool a_block = true) { bTabBlocked = a_block; } private: RE::UI_MESSAGE_RESULTS ProcessMessageEx(RE::UIMessage& a_message) { if (bTabBlocked && a_message.type == RE::UI_MESSAGE_TYPE::kScaleformEvent) { RE::BSUIScaleformData* data = static_cast(a_message.data); if (data && data->scaleformEvent->type == RE::GFxEvent::EventType::kKeyDown) { RE::GFxKeyEvent* key = (RE::GFxKeyEvent*)data->scaleformEvent; if ( key // Escape triggers a kTab event, added kEscape just in case && (key->keyCode == RE::GFxKey::kTab || key->keyCode == RE::GFxKey::kEscape) // Covers inventory and barter && !RE::UI::GetSingleton()->GameIsPaused() && RE::UI::GetSingleton()->IsMenuOpen(MENU_NAME)) { OpenJournal(false); return RE::UI_MESSAGE_RESULTS::kIgnore; } } } return _ProcessMessageFn(this, a_message); } // When opening from dialogue menu, a_bool must be false to not break framerate of the UI void static OpenJournal(bool a_bool) { using func_t = decltype(&OpenJournal); // Checked: 1.5.97, 1.6.640, 1.6.659, 1.6.1130 REL::Relocation func{ REL::RelocationID(52428, 53327) }; func(a_bool); } using ProcessMessageFn = decltype(&ProcessMessage); inline static REL::Relocation _ProcessMessageFn; };