diff --git a/SKSE/Plugins/EnderalSE.dll b/SKSE/Plugins/EnderalSE.dll index 802dfa88..4a7faf19 100644 --- a/SKSE/Plugins/EnderalSE.dll +++ b/SKSE/Plugins/EnderalSE.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8d35facd4bb9cdb353723f53a96e7222fd14b9558b0ba143eea3f06522a1a07 -size 733184 +oid sha256:c6abc73fa679391aeb55f82f4318f642081b8422b5c5c91af3c84c485457277a +size 735744 diff --git a/interface/dialoguemenu.swf b/interface/dialoguemenu.swf index c0f951b5..1eade07d 100644 --- a/interface/dialoguemenu.swf +++ b/interface/dialoguemenu.swf @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8a0091104d54e023bd4902f8c3510caaff2ec1fd409d04237af12d128530e4d -size 24622 +oid sha256:59ceed60457378a29ba00ae1cfa9bc9f961eef88143ae2edda69fc436d7a77b3 +size 24551 diff --git a/scripts/_00e_confirmdialogquit.pex b/scripts/_00e_confirmdialogquit.pex new file mode 100644 index 00000000..336f943c Binary files /dev/null and b/scripts/_00e_confirmdialogquit.pex differ diff --git a/scripts/_00e_questfunctions.pex b/scripts/_00e_questfunctions.pex index 34202d88..99b1a3da 100644 Binary files a/scripts/_00e_questfunctions.pex and b/scripts/_00e_questfunctions.pex differ diff --git a/scripts/enderalfunctions.pex b/scripts/enderalfunctions.pex index 3d2ab3be..9cedf241 100644 Binary files a/scripts/enderalfunctions.pex and b/scripts/enderalfunctions.pex differ diff --git a/source/Enderal DLL/src/DialogueMenuPatch.h b/source/Enderal DLL/src/DialogueMenuPatch.h new file mode 100644 index 00000000..942b4c15 --- /dev/null +++ b/source/Enderal DLL/src/DialogueMenuPatch.h @@ -0,0 +1,47 @@ +#pragma once + +inline bool bTabBlocked = false; + +class DialogueMenuPatch final : public RE::DialogueMenu +{ +public: + static void Install() + { + REL::Relocation OpenJournalMenuFunc{ 0x0092412C }; + + 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; + + // Escape triggers a kTab event, added kEscape just in case + if (key && (key->keyCode == RE::GFxKey::kTab || key->keyCode == RE::GFxKey::kEscape) && !RE::UI::GetSingleton()->IsMenuOpen(RE::ContainerMenu::MENU_NAME) && !RE::UI::GetSingleton()->IsMenuOpen(RE::BarterMenu::MENU_NAME)) { + // The best solution is to show the quest journal, like in LE, but showing a journal seems to mess with framerate of the UI, see dialogue menu after showing journal + //RE::UIMessageQueue::GetSingleton()->AddMessage(RE::InterfaceStrings::GetSingleton()->journalMenu, RE::UI_MESSAGE_TYPE::kShow, 0i64); + RE::BSTSmartPointer stackCallback; + RE::BSScript::Internal::VirtualMachine::GetSingleton()->DispatchStaticCall("_00E_ConfirmDialogQuit", "Show", RE::MakeFunctionArguments(), stackCallback); + return RE::UI_MESSAGE_RESULTS::kIgnore; + } + } + } + + return _ProcessMessageFn(this, a_message); + } + + using ProcessMessageFn = decltype(&RE::DialogueMenu::ProcessMessage); + + inline static REL::Relocation _ProcessMessageFn; +}; diff --git a/source/Enderal DLL/src/EventListener.cpp b/source/Enderal DLL/src/EventListener.cpp index 873c0a59..12999a8c 100644 --- a/source/Enderal DLL/src/EventListener.cpp +++ b/source/Enderal DLL/src/EventListener.cpp @@ -1,5 +1,6 @@ #include "EventListener.h" #include "Util.h" +#include "DialogueMenuPatch.h" #include auto EventListener::GetSingleton() -> EventListener* @@ -11,8 +12,9 @@ auto EventListener::GetSingleton() -> EventListener* // Unused void EventListener::Install() { - RE::ScriptEventSourceHolder::GetSingleton()->AddEventSink(EventListener::GetSingleton()); + //RE::ScriptEventSourceHolder::GetSingleton()->AddEventSink(EventListener::GetSingleton()); RE::UI::GetSingleton()->AddEventSink(EventListener::GetSingleton()); + SKSE::GetModCallbackEventSource()->AddEventSink(EventListener::GetSingleton()); } auto EventListener::ProcessEvent( @@ -46,21 +48,29 @@ auto EventListener::ProcessEvent( RE::BSTEventSource* a_eventSource) -> RE::BSEventNotifyControl { - if (a_event->opening && a_event->menuName == RE::JournalMenu::MENU_NAME) { - if (RE::UI::GetSingleton()->IsMenuOpen(RE::DialogueMenu::MENU_NAME)) { - const auto movie = RE::UI::GetSingleton()->GetMovieView(RE::JournalMenu::MENU_NAME); - if (movie) { - // skse.OpenMenu("Journal Menu") opens quest journal uninitialized, without any tab active and hotkeys bound - // We have to activate a tab for it to work properly - std::array args; - args[0].SetNumber(2); - args[1].SetBoolean(true); - movie->Invoke("_root.QuestJournalFader.Menu_mc.RestoreSavedSettings", nullptr, args.data(), static_cast(args.size())); + if (a_event->opening) { + if (a_event->menuName == RE::JournalMenu::MENU_NAME) { + if (RE::UI::GetSingleton()->IsMenuOpen(RE::DialogueMenu::MENU_NAME)) { + const auto movie = RE::UI::GetSingleton()->GetMovieView(RE::JournalMenu::MENU_NAME); + if (movie) { + // UIMessage opens quest journal uninitialized, without any tab active and hotkeys bound + // We have to activate a tab for it to work properly + std::array args; + args[0].SetNumber(2); + args[1].SetBoolean(false); + movie->Invoke("_root.QuestJournalFader.Menu_mc.RestoreSavedSettings", nullptr, args.data(), static_cast(args.size())); + } } + } else if (a_event->menuName == RE::DialogueMenu::MENU_NAME) { + // Make sure Tab is not blocked. If it should be, a Papyrus script will block it a few frames later. + DialogueMenuPatch::BlockTab(false); + } + } else { + if (a_event->menuName == RE::DialogueMenu::MENU_NAME) { + DialogueMenuPatch::BlockTab(false); + } else if (a_event->menuName == RE::MainMenu::MENU_NAME) { + CheckScriptVersions(); } - } else if (!a_event->opening && a_event->menuName == RE::MainMenu::MENU_NAME) { - RE::UI::GetSingleton()->RemoveEventSink(EventListener::GetSingleton()); - CheckScriptVersions(); } return RE::BSEventNotifyControl::kContinue; diff --git a/source/Enderal DLL/src/Main.cpp b/source/Enderal DLL/src/Main.cpp index 14d6c53e..d96a1bb5 100644 --- a/source/Enderal DLL/src/Main.cpp +++ b/source/Enderal DLL/src/Main.cpp @@ -6,6 +6,7 @@ #include "MapMarkerPlacement.h" #include "AchievementFix.h" #include "BinkInterruptPatch.h" +#include "DialogueMenuPatch.h" using namespace SKSE; @@ -158,11 +159,12 @@ SKSEPluginLoad(const LoadInterface* skse) { setting->data.s = nullptr; RE::INISettingCollection::GetSingleton()->WriteSetting(setting); - SKSE::GetModCallbackEventSource()->AddEventSink(EventListener::GetSingleton()); - RE::UI::GetSingleton()->AddEventSink(EventListener::GetSingleton()); + EventListener::Install(); GetPapyrusInterface()->Register(Papyrus::Bind); + DialogueMenuPatch::Install(); + if (g_settings.at("VideoInterruptPatch")) { logger::info("Making videos interruptible..."); BinkInterruptPatch::Install(); diff --git a/source/Enderal DLL/src/PapyrusFunctions.h b/source/Enderal DLL/src/PapyrusFunctions.h index b376eacb..18720627 100644 --- a/source/Enderal DLL/src/PapyrusFunctions.h +++ b/source/Enderal DLL/src/PapyrusFunctions.h @@ -2,6 +2,7 @@ #include "Util.h" #include "PersistentFormManager.h" +#include "DialogueMenuPatch.h" namespace Papyrus::PapyrusFunctions { @@ -87,6 +88,16 @@ namespace Papyrus::PapyrusFunctions return sstream.str(); } + void DisableDialogueQuitting(RE::StaticFunctionTag*) + { + DialogueMenuPatch::BlockTab(); + } + + void EnableDialogueQuitting(RE::StaticFunctionTag*) + { + DialogueMenuPatch::BlockTab(false); + } + inline void Bind(VM& a_vm) { BIND(CreatePotion); @@ -101,5 +112,9 @@ namespace Papyrus::PapyrusFunctions logger::info("{}", "Registered GetPlayerHash"sv); BIND(StringToHex); logger::info("{}", "Registered StringToHex"sv); + BIND(DisableDialogueQuitting); + logger::info("{}", "Registered DisableDialogueQuitting"sv); + BIND(EnableDialogueQuitting); + logger::info("{}", "Registered EnableDialogueQuitting"sv); } } diff --git a/source/scripts/_00e_confirmdialogquit.psc b/source/scripts/_00e_confirmdialogquit.psc new file mode 100644 index 00000000..b279a972 --- /dev/null +++ b/source/scripts/_00e_confirmdialogquit.psc @@ -0,0 +1,7 @@ +Scriptname _00E_ConfirmDialogQuit Hidden + +function Show() global + if (Game.GetFormFromFile(0x163D, "Update.esm") as Message).Show() == 1 + Game.QuitToMainMenu() + endif +endfunction diff --git a/source/scripts/_00e_questfunctions.psc b/source/scripts/_00e_questfunctions.psc index f31554f1..1f381818 100644 --- a/source/scripts/_00e_questfunctions.psc +++ b/source/scripts/_00e_questfunctions.psc @@ -139,58 +139,21 @@ Function DisableDialogueQuitting() Global {Disables the TAB Key during dialogue. Resets automatically upon dialogue exit via Goodbye.} if ! UI.IsMenuOpen("Dialogue Menu") - UnregisterDialogQuitKey() + EnableDialogueQuitting() return endif - if UI.GetBool("Dialogue Menu", "_root.DialogueMenu_mc.bEnableTab") != true - ; Suspected non-Enderal dialoguemenu.swf replacer, rechecking value in order to be sure. - UI.InvokeBool("Dialogue Menu", "_root.DialogueMenu_mc.SetVariable", True) - if UI.GetBool("Dialogue Menu", "_root.DialogueMenu_mc.bEnableTab") != true - Debug.Notification("Detected incompatible dialoguemenu.swf!") - endif - endif - - UI.InvokeBool("Dialogue Menu", "_root.DialogueMenu_mc.SetVariable", False) - - Quest Levelsystem = Game.GetForm(0x10AA2) as Quest - Levelsystem.RegisterForKey(1) ; Escape - Levelsystem.RegisterForKey(15) ; Tab - int iControllerKey = Input.GetMappedKey("Tween Menu", 0x02) - if iControllerKey > -1 - Levelsystem.RegisterForKey(iControllerKey) ; Controller - endif - - Levelsystem.RegisterForMenu("Dialogue Menu") + EnderalFunctions.DisableDialogueQuitting() EndFunction Function EnableDialogueQuitting() Global {Disables the TAB Key during dialogue. Resets automatically upon dialogue exit via Goodbye.} - UI.InvokeBool("Dialogue Menu", "_root.DialogueMenu_mc.SetVariable", True) - UnregisterDialogQuitKey() + EnderalFunctions.EnableDialogueQuitting() EndFunction -function UnregisterDialogQuitKey() Global - Quest Levelsystem = Game.GetForm(0x10AA2) as Quest - Levelsystem.UnregisterForAllKeys() - Levelsystem.UnregisterForMenu("Dialogue Menu") -endfunction - -Event OnKeyDown(Int KeyCode) - if UI.IsMenuOpen("Dialogue Menu") - if ! UI.IsMenuOpen("MessageBoxMenu") && ! UI.GetBool("Dialogue Menu", "_root.DialogueMenu_mc.bEnableTab") - if (Game.GetFormFromFile(0x163D, "Update.esm") as Message).Show() == 1 - Game.QuitToMainMenu() - endif - endif - else - UnregisterDialogQuitKey() - endif -EndEvent - Function RefreshFace() Global String facegen = "bUseFaceGenPreprocessedHeads:General" @@ -934,8 +897,6 @@ Event OnMenuClose(String MenuName) If MenuName == "Book Menu" MTToRemove.Remove() UnregisterForMenu("Book Menu") - elseif MenuName == "Dialogue Menu" - UnregisterDialogQuitKey() EndIf EndEvent diff --git a/source/scripts/enderalfunctions.psc b/source/scripts/enderalfunctions.psc index cf6e258c..80d94a0f 100644 --- a/source/scripts/enderalfunctions.psc +++ b/source/scripts/enderalfunctions.psc @@ -19,6 +19,10 @@ String Function GetPlayerHash() Global Native ; RETURN - Returns the hexadecimal equivalent of the passed string. String Function StringToHex(String a_string) Global Native +; Disables the TAB Key during dialogue. Resets automatically upon dialogue exit via Goodbye. +Function DisableDialogueQuitting() Global Native +Function EnableDialogueQuitting() Global Native + bool function IsDLLLoaded() global int iVer = SKSE.GetPluginVersion("EnderalSE") return iVer > 0