themrdemonized / xray-monolith

Other
55 stars 46 forks source link

incorrect keystate from WINDOW_LBUTTON_DB_CLICK as a result from loading save files without prompt window #76

Closed Undisclosed-Information closed 6 days ago

Undisclosed-Information commented 1 week ago

_I tried reporting this bug to the G.A.M.M.A. discord, but I was told this it was engine related, even though it could be resolved from inside the [ui_loaddialog.script] file which is used by the modpack.

When a player dies and reloads a save file from the menu, the function call to key_state(DIK_keys.MOUSE_1) reports an incorrect state of 1 even though LMB is not held down. The state resolves itself if you press LMB again.

How to:

  1. die ingame
  2. press escape to open the menu
  3. click Load Game
  4. double-click on a save file (row)
  5. from this point on, dont click LMB
  6. once ingame, debug key_state(DIK_keys.MOUSE_1) and it will report 1, instead of 0.

Bug

At first sight, the bug occurs, because when you die and try to load a save file from the menu, no prompt window is triggered, which forces a on_pressed on the list_window clicks.

  1. The buttons from the prompt window execute based on a on_released key event.
  2. The items in the list window (without prompt) execute based on a on_pressed key event (bug).

Internally, its because the file ui_load_dialog.script adds a callback to WINDOW_LBUTTON_DB_CLICK that is an on_pressed key event that does not get updated right after a save file gets executed.

As to why the keystate is not updated right after loading a save file is unknown to me. Perhaps code is not executed during a loading screen? Unfortunately I have limited time to debug this in the engine as it is a time-consuming (and horrible) process.

The relevant code snippets I could find are listed below:

--ui_load_dialog.script

function UILoadDialog:InitCallBacks()
        self:AddCallback("button_load", ui_events.BUTTON_CLICKED,          self.OnButton_load_clicked, self)
    self:AddCallback("button_back", ui_events.BUTTON_CLICKED,          self.OnButton_back_clicked, self)
    self:AddCallback("button_del",  ui_events.BUTTON_CLICKED,          self.OnButton_del_clicked,  self)
    self:AddCallback("message_box", ui_events.MESSAGE_BOX_YES_CLICKED, self.OnMsgYes,              self)
    self:AddCallback("message_box", ui_events.MESSAGE_BOX_OK_CLICKED,  self.OnMsgYes,          self)

    self:AddCallback("list_window", ui_events.LIST_ITEM_CLICKED,       self.OnListItemClicked,     self)
    self:AddCallback("list_window", ui_events.WINDOW_LBUTTON_DB_CLICK, self.OnListItemDbClicked,   self) --this line!
end
// UIButton.cpp

bool CUIButton::OnMouseAction(float x, float y, EUIMessages mouse_action)
{
    if (inherited::OnMouseAction(x, y, mouse_action)) return true;

    switch (m_eButtonState)
    {
    case BUTTON_NORMAL:
        {
            if (mouse_action == WINDOW_LBUTTON_DOWN || mouse_action == WINDOW_LBUTTON_DB_CLICK)
            {
                SetButtonState(BUTTON_PUSHED); //this line!
                GetMessageTarget()->SendMessage(this, BUTTON_DOWN, NULL);
                return true;
            }
        }
        break;

Solution: 1

The 2nd best solution (1st being a fix to the engine's keystate function) would be to add a new key event to the engine, named WINDOW_LBUTTON_DB_CLICKED, that would trigger based of a on_released key event. Note the ED at the end.

That way we could modify the AddCallback line in ui_load_dialog.script from this:

--ui_load_dialog.script

self:AddCallback("list_window", ui_events.WINDOW_LBUTTON_DB_CLICK, self.OnListItemDbClicked, self)

to this:

--ui_load_dialog.script

self:AddCallback("list_window", ui_events.WINDOW_LBUTTON_DB_CLICKED, self.OnListItemDbClicked, self)

which will force the keystate to be saved as 0 and when the game loads it will have the correct (or better said, "initial") keystate.

Solution: 2

As an alternative solution one could also use a custom Lua implementation of keystate that forces LMB to always be 0 on game start until the engine itself registers a state of 1

local KEY_STATE_FIX = { [DIK_keys.MOUSE_1] = 0x1 }

local function GetKeyState(keycode)
    if (KEY_STATE_FIX[keycode] == 0x1) then 
        return false 
    end
    return key_state(keycode) == 1
end

local function WINDOW_LBUTTON_DB_CLICK_FIX(c)
    if not KEY_STATE_FIX[c] then return end
    KEY_STATE_FIX[c] = 0x2
    UnregisterScriptCallback("on_key_press", WINDOW_LBUTTON_DB_CLICK_FIX)
end
RegisterScriptCallback("on_key_press", WINDOW_LBUTTON_DB_CLICK_FIX)

Solution: 3

Finally, we could force a prompt window for ALL save-file loads, which will resolve the issue on its own, because the prompt buttons are based of on_released key events.

themrdemonized commented 1 week ago

Try the latest release please, i added reset state function on game loaded in the engine https://github.com/themrdemonized/xray-monolith/commit/e5072597e36846f19256aa3d4fc9e6e49cd995f7