An experimental fix for unresponsive dialogs
This commit is contained in:
parent
274ed7983f
commit
07787e82ad
@ -48,6 +48,7 @@ auto EventListener::ProcessEvent(
|
|||||||
if (a_event->menuName == RE::DialogueMenu::MENU_NAME) {
|
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.
|
// Make sure Tab is not blocked. If it should be, a Papyrus script will block it a few frames later.
|
||||||
DialogueMenuPatch::BlockTab(false);
|
DialogueMenuPatch::BlockTab(false);
|
||||||
|
DialogueMenuPatch::ResetProgressFailsafe();
|
||||||
} else if (a_event->menuName == "CustomMenu") {
|
} else if (a_event->menuName == "CustomMenu") {
|
||||||
auto uiMovie = RE::UI::GetSingleton()->GetMovieView("CustomMenu");
|
auto uiMovie = RE::UI::GetSingleton()->GetMovieView("CustomMenu");
|
||||||
if (uiMovie && std::strstr(uiMovie->GetMovieDef()->GetFileURL(), "_heromenu.swf") != NULL) {
|
if (uiMovie && std::strstr(uiMovie->GetMovieDef()->GetFileURL(), "_heromenu.swf") != NULL) {
|
||||||
@ -60,6 +61,7 @@ auto EventListener::ProcessEvent(
|
|||||||
} else {
|
} else {
|
||||||
if (a_event->menuName == RE::DialogueMenu::MENU_NAME) {
|
if (a_event->menuName == RE::DialogueMenu::MENU_NAME) {
|
||||||
DialogueMenuPatch::BlockTab(false);
|
DialogueMenuPatch::BlockTab(false);
|
||||||
|
DialogueMenuPatch::ResetProgressFailsafe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
inline bool bTabBlocked = false;
|
inline bool bTabBlocked = false;
|
||||||
|
|
||||||
class DialogueMenuPatch final : public RE::DialogueMenu
|
class DialogueMenuPatch final : public RE::DialogueMenu
|
||||||
@ -16,15 +18,80 @@ public:
|
|||||||
bTabBlocked = a_block;
|
bTabBlocked = a_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ResetProgressFailsafe()
|
||||||
|
{
|
||||||
|
progressBlockedSince = {};
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// dialoguemenu.swf gates every forward interaction - clicking a topic, Activate, and skipping
|
||||||
|
// a line - on DialogueMenu_mc.bAllowProgress. That flag is cleared after each of them, and is
|
||||||
|
// set back to true only by a setInterval callback firing ALLOW_PROGRESS_DELAY (750 ms) after
|
||||||
|
// the engine delivers the NotifyVoiceReady message. If that message never arrives, or the
|
||||||
|
// interval never fires, the topic list stays on screen but ignores every click and keypress
|
||||||
|
// for the rest of the session. Tab and Esc keep working, because onCancelPress() ->
|
||||||
|
// StartHideMenu() is not gated - which is exactly how this reaches us in bug reports:
|
||||||
|
// "clicking a dialogue option does nothing, but I can still leave with Tab".
|
||||||
|
static constexpr auto ALLOW_PROGRESS_VAR = "DialogueMenu_mc.bAllowProgress";
|
||||||
|
static constexpr auto MENU_STATE_VAR = "DialogueMenu_mc.eMenuState";
|
||||||
|
static constexpr auto TOPIC_LIST_SHOWN = 1; // DialogueMenu.TOPIC_LIST_SHOWN
|
||||||
|
static constexpr auto PROGRESS_TIMEOUT = std::chrono::milliseconds(2000);
|
||||||
|
|
||||||
|
// Re-enables input when the topic list has sat unresponsive for longer than the SWF's own
|
||||||
|
// debounce can account for.
|
||||||
|
void UnstickProgress()
|
||||||
|
{
|
||||||
|
auto* mtm = RE::MenuTopicManager::GetSingleton();
|
||||||
|
|
||||||
|
// currentTopicInfo is only set while the speaker is saying a line. Never touch the
|
||||||
|
// debounce mid-response, only once the game is idle and waiting for a selection.
|
||||||
|
if (!uiMovie || !mtm || mtm->currentTopicInfo) {
|
||||||
|
ResetProgressFailsafe();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only the topic list waits for input. Greeting, transition and post-click states are
|
||||||
|
// expected to be unresponsive.
|
||||||
|
RE::GFxValue menuState;
|
||||||
|
if (!uiMovie->GetVariable(&menuState, MENU_STATE_VAR) || !menuState.IsNumber() || static_cast<int>(menuState.GetNumber()) != TOPIC_LIST_SHOWN) {
|
||||||
|
ResetProgressFailsafe();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RE::GFxValue allowProgress;
|
||||||
|
if (!uiMovie->GetVariable(&allowProgress, ALLOW_PROGRESS_VAR) || !allowProgress.IsBool()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allowProgress.GetBool()) {
|
||||||
|
ResetProgressFailsafe();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto now = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
if (progressBlockedSince == std::chrono::steady_clock::time_point{}) {
|
||||||
|
progressBlockedSince = now;
|
||||||
|
} else if (now - progressBlockedSince >= PROGRESS_TIMEOUT) {
|
||||||
|
ResetProgressFailsafe();
|
||||||
|
allowProgress.SetBoolean(true);
|
||||||
|
uiMovie->SetVariable(ALLOW_PROGRESS_VAR, allowProgress);
|
||||||
|
logger::warn("Dialogue menu stopped accepting input, re-enabled bAllowProgress");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RE::UI_MESSAGE_RESULTS ProcessMessageEx(RE::UIMessage& a_message)
|
RE::UI_MESSAGE_RESULTS ProcessMessageEx(RE::UIMessage& a_message)
|
||||||
{
|
{
|
||||||
// Unblock Tab if Goodbye response has finished playing (End script has already run at this point).
|
if (a_message.type == RE::UI_MESSAGE_TYPE::kUpdate) {
|
||||||
// A failsafe when Goodbye does not close the dialogue menu.
|
UnstickProgress();
|
||||||
if (bTabBlocked && a_message.type == RE::UI_MESSAGE_TYPE::kUpdate) {
|
|
||||||
auto* mtm = RE::MenuTopicManager::GetSingleton();
|
// Unblock Tab if Goodbye response has finished playing (End script has already run at this point).
|
||||||
if (mtm && mtm->isSayingGoodbye && !mtm->currentTopicInfo && RE::UI::GetSingleton()->IsMenuOpen(MENU_NAME)) {
|
// A failsafe when Goodbye does not close the dialogue menu.
|
||||||
BlockTab(false);
|
if (bTabBlocked) {
|
||||||
|
auto* mtm = RE::MenuTopicManager::GetSingleton();
|
||||||
|
if (mtm && mtm->isSayingGoodbye && !mtm->currentTopicInfo && RE::UI::GetSingleton()->IsMenuOpen(MENU_NAME)) {
|
||||||
|
BlockTab(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,6 +126,8 @@ private:
|
|||||||
func(a_bool);
|
func(a_bool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline static std::chrono::steady_clock::time_point progressBlockedSince{};
|
||||||
|
|
||||||
using ProcessMessageFn = decltype(&ProcessMessage);
|
using ProcessMessageFn = decltype(&ProcessMessage);
|
||||||
inline static REL::Relocation<ProcessMessageFn> _ProcessMessageFn;
|
inline static REL::Relocation<ProcessMessageFn> _ProcessMessageFn;
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user