Handle blocking dialogs with DLL, replaced dialoguemenu.swf with unchanged Better Dialogue Controls
This commit is contained in:
parent
aab2a8c217
commit
13635df9f8
BIN
SKSE/Plugins/EnderalSE.dll
(Stored with Git LFS)
BIN
SKSE/Plugins/EnderalSE.dll
(Stored with Git LFS)
Binary file not shown.
BIN
interface/dialoguemenu.swf
(Stored with Git LFS)
BIN
interface/dialoguemenu.swf
(Stored with Git LFS)
Binary file not shown.
BIN
scripts/_00e_confirmdialogquit.pex
Normal file
BIN
scripts/_00e_confirmdialogquit.pex
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
47
source/Enderal DLL/src/DialogueMenuPatch.h
Normal file
47
source/Enderal DLL/src/DialogueMenuPatch.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
inline bool bTabBlocked = false;
|
||||||
|
|
||||||
|
class DialogueMenuPatch final : public RE::DialogueMenu
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void Install()
|
||||||
|
{
|
||||||
|
REL::Relocation<std::uintptr_t> OpenJournalMenuFunc{ 0x0092412C };
|
||||||
|
|
||||||
|
REL::Relocation<uintptr_t> 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<RE::BSUIScaleformData*>(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<RE::BSScript::IStackCallbackFunctor> 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> _ProcessMessageFn;
|
||||||
|
};
|
@ -1,5 +1,6 @@
|
|||||||
#include "EventListener.h"
|
#include "EventListener.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
#include "DialogueMenuPatch.h"
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
|
|
||||||
auto EventListener::GetSingleton() -> EventListener*
|
auto EventListener::GetSingleton() -> EventListener*
|
||||||
@ -11,8 +12,9 @@ auto EventListener::GetSingleton() -> EventListener*
|
|||||||
// Unused
|
// Unused
|
||||||
void EventListener::Install()
|
void EventListener::Install()
|
||||||
{
|
{
|
||||||
RE::ScriptEventSourceHolder::GetSingleton()->AddEventSink<RE::TESContainerChangedEvent>(EventListener::GetSingleton());
|
//RE::ScriptEventSourceHolder::GetSingleton()->AddEventSink<RE::TESContainerChangedEvent>(EventListener::GetSingleton());
|
||||||
RE::UI::GetSingleton()->AddEventSink<RE::MenuOpenCloseEvent>(EventListener::GetSingleton());
|
RE::UI::GetSingleton()->AddEventSink<RE::MenuOpenCloseEvent>(EventListener::GetSingleton());
|
||||||
|
SKSE::GetModCallbackEventSource()->AddEventSink(EventListener::GetSingleton());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto EventListener::ProcessEvent(
|
auto EventListener::ProcessEvent(
|
||||||
@ -46,21 +48,29 @@ auto EventListener::ProcessEvent(
|
|||||||
RE::BSTEventSource<RE::MenuOpenCloseEvent>* a_eventSource)
|
RE::BSTEventSource<RE::MenuOpenCloseEvent>* a_eventSource)
|
||||||
-> RE::BSEventNotifyControl
|
-> RE::BSEventNotifyControl
|
||||||
{
|
{
|
||||||
if (a_event->opening && a_event->menuName == RE::JournalMenu::MENU_NAME) {
|
if (a_event->opening) {
|
||||||
if (RE::UI::GetSingleton()->IsMenuOpen(RE::DialogueMenu::MENU_NAME)) {
|
if (a_event->menuName == RE::JournalMenu::MENU_NAME) {
|
||||||
const auto movie = RE::UI::GetSingleton()->GetMovieView(RE::JournalMenu::MENU_NAME);
|
if (RE::UI::GetSingleton()->IsMenuOpen(RE::DialogueMenu::MENU_NAME)) {
|
||||||
if (movie) {
|
const auto movie = RE::UI::GetSingleton()->GetMovieView(RE::JournalMenu::MENU_NAME);
|
||||||
// skse.OpenMenu("Journal Menu") opens quest journal uninitialized, without any tab active and hotkeys bound
|
if (movie) {
|
||||||
// We have to activate a tab for it to work properly
|
// UIMessage opens quest journal uninitialized, without any tab active and hotkeys bound
|
||||||
std::array<RE::GFxValue, 2> args;
|
// We have to activate a tab for it to work properly
|
||||||
args[0].SetNumber(2);
|
std::array<RE::GFxValue, 2> args;
|
||||||
args[1].SetBoolean(true);
|
args[0].SetNumber(2);
|
||||||
movie->Invoke("_root.QuestJournalFader.Menu_mc.RestoreSavedSettings", nullptr, args.data(), static_cast<std::uint32_t>(args.size()));
|
args[1].SetBoolean(false);
|
||||||
|
movie->Invoke("_root.QuestJournalFader.Menu_mc.RestoreSavedSettings", nullptr, args.data(), static_cast<std::uint32_t>(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<RE::MenuOpenCloseEvent>(EventListener::GetSingleton());
|
|
||||||
CheckScriptVersions();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return RE::BSEventNotifyControl::kContinue;
|
return RE::BSEventNotifyControl::kContinue;
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "MapMarkerPlacement.h"
|
#include "MapMarkerPlacement.h"
|
||||||
#include "AchievementFix.h"
|
#include "AchievementFix.h"
|
||||||
#include "BinkInterruptPatch.h"
|
#include "BinkInterruptPatch.h"
|
||||||
|
#include "DialogueMenuPatch.h"
|
||||||
|
|
||||||
using namespace SKSE;
|
using namespace SKSE;
|
||||||
|
|
||||||
@ -158,11 +159,12 @@ SKSEPluginLoad(const LoadInterface* skse) {
|
|||||||
setting->data.s = nullptr;
|
setting->data.s = nullptr;
|
||||||
RE::INISettingCollection::GetSingleton()->WriteSetting(setting);
|
RE::INISettingCollection::GetSingleton()->WriteSetting(setting);
|
||||||
|
|
||||||
SKSE::GetModCallbackEventSource()->AddEventSink(EventListener::GetSingleton());
|
EventListener::Install();
|
||||||
RE::UI::GetSingleton()->AddEventSink<RE::MenuOpenCloseEvent>(EventListener::GetSingleton());
|
|
||||||
|
|
||||||
GetPapyrusInterface()->Register(Papyrus::Bind);
|
GetPapyrusInterface()->Register(Papyrus::Bind);
|
||||||
|
|
||||||
|
DialogueMenuPatch::Install();
|
||||||
|
|
||||||
if (g_settings.at("VideoInterruptPatch")) {
|
if (g_settings.at("VideoInterruptPatch")) {
|
||||||
logger::info("Making videos interruptible...");
|
logger::info("Making videos interruptible...");
|
||||||
BinkInterruptPatch::Install();
|
BinkInterruptPatch::Install();
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include "PersistentFormManager.h"
|
#include "PersistentFormManager.h"
|
||||||
|
#include "DialogueMenuPatch.h"
|
||||||
|
|
||||||
namespace Papyrus::PapyrusFunctions
|
namespace Papyrus::PapyrusFunctions
|
||||||
{
|
{
|
||||||
@ -87,6 +88,16 @@ namespace Papyrus::PapyrusFunctions
|
|||||||
return sstream.str();
|
return sstream.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DisableDialogueQuitting(RE::StaticFunctionTag*)
|
||||||
|
{
|
||||||
|
DialogueMenuPatch::BlockTab();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnableDialogueQuitting(RE::StaticFunctionTag*)
|
||||||
|
{
|
||||||
|
DialogueMenuPatch::BlockTab(false);
|
||||||
|
}
|
||||||
|
|
||||||
inline void Bind(VM& a_vm)
|
inline void Bind(VM& a_vm)
|
||||||
{
|
{
|
||||||
BIND(CreatePotion);
|
BIND(CreatePotion);
|
||||||
@ -101,5 +112,9 @@ namespace Papyrus::PapyrusFunctions
|
|||||||
logger::info("{}", "Registered GetPlayerHash"sv);
|
logger::info("{}", "Registered GetPlayerHash"sv);
|
||||||
BIND(StringToHex);
|
BIND(StringToHex);
|
||||||
logger::info("{}", "Registered StringToHex"sv);
|
logger::info("{}", "Registered StringToHex"sv);
|
||||||
|
BIND(DisableDialogueQuitting);
|
||||||
|
logger::info("{}", "Registered DisableDialogueQuitting"sv);
|
||||||
|
BIND(EnableDialogueQuitting);
|
||||||
|
logger::info("{}", "Registered EnableDialogueQuitting"sv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
7
source/scripts/_00e_confirmdialogquit.psc
Normal file
7
source/scripts/_00e_confirmdialogquit.psc
Normal file
@ -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
|
@ -139,58 +139,21 @@ Function DisableDialogueQuitting() Global
|
|||||||
{Disables the TAB Key during dialogue. Resets automatically upon dialogue exit via Goodbye.}
|
{Disables the TAB Key during dialogue. Resets automatically upon dialogue exit via Goodbye.}
|
||||||
|
|
||||||
if ! UI.IsMenuOpen("Dialogue Menu")
|
if ! UI.IsMenuOpen("Dialogue Menu")
|
||||||
UnregisterDialogQuitKey()
|
EnableDialogueQuitting()
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if UI.GetBool("Dialogue Menu", "_root.DialogueMenu_mc.bEnableTab") != true
|
EnderalFunctions.DisableDialogueQuitting()
|
||||||
; 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")
|
|
||||||
|
|
||||||
EndFunction
|
EndFunction
|
||||||
|
|
||||||
Function EnableDialogueQuitting() Global
|
Function EnableDialogueQuitting() Global
|
||||||
{Disables the TAB Key during dialogue. Resets automatically upon dialogue exit via Goodbye.}
|
{Disables the TAB Key during dialogue. Resets automatically upon dialogue exit via Goodbye.}
|
||||||
|
|
||||||
UI.InvokeBool("Dialogue Menu", "_root.DialogueMenu_mc.SetVariable", True)
|
EnderalFunctions.EnableDialogueQuitting()
|
||||||
UnregisterDialogQuitKey()
|
|
||||||
|
|
||||||
EndFunction
|
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
|
Function RefreshFace() Global
|
||||||
|
|
||||||
String facegen = "bUseFaceGenPreprocessedHeads:General"
|
String facegen = "bUseFaceGenPreprocessedHeads:General"
|
||||||
@ -934,8 +897,6 @@ Event OnMenuClose(String MenuName)
|
|||||||
If MenuName == "Book Menu"
|
If MenuName == "Book Menu"
|
||||||
MTToRemove.Remove()
|
MTToRemove.Remove()
|
||||||
UnregisterForMenu("Book Menu")
|
UnregisterForMenu("Book Menu")
|
||||||
elseif MenuName == "Dialogue Menu"
|
|
||||||
UnregisterDialogQuitKey()
|
|
||||||
EndIf
|
EndIf
|
||||||
|
|
||||||
EndEvent
|
EndEvent
|
||||||
|
@ -19,6 +19,10 @@ String Function GetPlayerHash() Global Native
|
|||||||
; RETURN - Returns the hexadecimal equivalent of the passed string.
|
; RETURN - Returns the hexadecimal equivalent of the passed string.
|
||||||
String Function StringToHex(String a_string) Global Native
|
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
|
bool function IsDLLLoaded() global
|
||||||
int iVer = SKSE.GetPluginVersion("EnderalSE")
|
int iVer = SKSE.GetPluginVersion("EnderalSE")
|
||||||
return iVer > 0
|
return iVer > 0
|
||||||
|
Loading…
Reference in New Issue
Block a user