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) {
|
||||
// Make sure Tab is not blocked. If it should be, a Papyrus script will block it a few frames later.
|
||||
DialogueMenuPatch::BlockTab(false);
|
||||
DialogueMenuPatch::ResetProgressFailsafe();
|
||||
} else if (a_event->menuName == "CustomMenu") {
|
||||
auto uiMovie = RE::UI::GetSingleton()->GetMovieView("CustomMenu");
|
||||
if (uiMovie && std::strstr(uiMovie->GetMovieDef()->GetFileURL(), "_heromenu.swf") != NULL) {
|
||||
@ -60,6 +61,7 @@ auto EventListener::ProcessEvent(
|
||||
} else {
|
||||
if (a_event->menuName == RE::DialogueMenu::MENU_NAME) {
|
||||
DialogueMenuPatch::BlockTab(false);
|
||||
DialogueMenuPatch::ResetProgressFailsafe();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
inline bool bTabBlocked = false;
|
||||
|
||||
class DialogueMenuPatch final : public RE::DialogueMenu
|
||||
@ -16,17 +18,82 @@ public:
|
||||
bTabBlocked = a_block;
|
||||
}
|
||||
|
||||
static void ResetProgressFailsafe()
|
||||
{
|
||||
progressBlockedSince = {};
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (a_message.type == RE::UI_MESSAGE_TYPE::kUpdate) {
|
||||
UnstickProgress();
|
||||
|
||||
// Unblock Tab if Goodbye response has finished playing (End script has already run at this point).
|
||||
// A failsafe when Goodbye does not close the dialogue menu.
|
||||
if (bTabBlocked && a_message.type == RE::UI_MESSAGE_TYPE::kUpdate) {
|
||||
if (bTabBlocked) {
|
||||
auto* mtm = RE::MenuTopicManager::GetSingleton();
|
||||
if (mtm && mtm->isSayingGoodbye && !mtm->currentTopicInfo && RE::UI::GetSingleton()->IsMenuOpen(MENU_NAME)) {
|
||||
BlockTab(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bTabBlocked && a_message.type == RE::UI_MESSAGE_TYPE::kScaleformEvent) {
|
||||
RE::BSUIScaleformData* data = static_cast<RE::BSUIScaleformData*>(a_message.data);
|
||||
@ -59,6 +126,8 @@ private:
|
||||
func(a_bool);
|
||||
}
|
||||
|
||||
inline static std::chrono::steady_clock::time_point progressBlockedSince{};
|
||||
|
||||
using ProcessMessageFn = decltype(&ProcessMessage);
|
||||
inline static REL::Relocation<ProcessMessageFn> _ProcessMessageFn;
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user