LostArtefacts / TRX

Open source re-implementation of Tomb Raider I and Tomb Raider II, along with additional enhancements and bugfixes
https://lostartefacts.dev/
GNU General Public License v3.0
586 stars 38 forks source link

Feature request: bring back Enter to main menu bindings #814

Closed rr- closed 1 year ago

rr- commented 1 year ago

Not being able to select stuff in the main menu with Enter is really confusing and disruptive. I also had reports of players relying on having the ability to use arrows AND WSAD bound at the same time.

One way out of this that I see is to maybe turning https://github.com/LostArtefacts/Tomb1Main/issues/564 to be optional? Another would be to allow multiple bindings but that's way more effort.

walkawayy commented 1 year ago

I still vote for implementing #804 personally. It might require adding more keybinds, but I don't think the current way makes sense where we link Lara's movement and menu movement together.

rr- commented 1 year ago

We have several complaints. Here's my take on them. Implementation hints at the bottom.

Example approach to sidestep our super generic control routines and hardcode the key roles

Remember that g_Input.select is for confirming stuff in the menus, g_Input.deselect is for exiting stuff and coincidentally firing the inventory. g_Input.action is for Lara grabbing stuff and shooting weapons. If you consider renaming things, prefer MENU_CONFIRM and MENU_EXIT over select / deselect – I think this name is easier to grok.

Example patch for the delayed assignments

Untested pseudocode, but I think it gets my point across, especially the memcpy parts:

diff --git a/src/config.c b/src/config.c
index a6ca8e93..fe078eba 100644
--- a/src/config.c
+++ b/src/config.c
@@ -267,6 +267,7 @@ bool Config_ReadFromJSON(const char *cfg_data)
     CLAMP(g_Config.ui.bar_scale, MIN_BAR_SCALE, MAX_BAR_SCALE);

     char layout_name[50];
+    Input_EnterAssignMode();
     for (INPUT_LAYOUT layout = INPUT_LAYOUT_CUSTOM_1;
          layout < INPUT_LAYOUT_NUMBER_OF; layout++) {
         sprintf(layout_name, "layout_%d", layout);
@@ -278,6 +279,7 @@ bool Config_ReadFromJSON(const char *cfg_data)
             Input_AssignScancode(layout, role, scancode);
         }
     }
+    Input_ExitAssignMode(true);
     Input_CheckConflicts(CM_KEYBOARD, g_Config.input.layout);

     for (INPUT_LAYOUT layout = INPUT_LAYOUT_CUSTOM_1;
diff --git a/src/game/input.c b/src/game/input.c
index 128508ce..ab40703c 100644
--- a/src/game/input.c
+++ b/src/game/input.c
@@ -205,6 +205,16 @@ int16_t Input_GetAssignedAxisDir(INPUT_LAYOUT layout_num, INPUT_ROLE role)
     return S_Input_GetAssignedAxisDir(layout_num, role);
 }

+void Input_EnterAssignMode(void)
+{
+    S_Input_EnterAssignMode();
+}
+
+void Input_ExitAssignMode(bool confirm)
+{
+    S_Input_ExitAssignMode(confirm);
+}
+
 void Input_AssignScancode(
     INPUT_LAYOUT layout_num, INPUT_ROLE role, INPUT_SCANCODE scancode)
 {
diff --git a/src/game/input.h b/src/game/input.h
index a4210f7a..d7f9283e 100644
--- a/src/game/input.h
+++ b/src/game/input.h
@@ -24,6 +24,14 @@ void Input_CheckControllerConflicts(INPUT_LAYOUT layout_num);
 // within the custom layout.
 bool Input_IsKeyConflicted(CONTROL_MODE mode, INPUT_ROLE role);

+// Start assigning keys and buttons. Do not make any assignments
+// effective until calling Input_ExitAssignMode(true). Calling
+// Input_ExitAssignMode(false) will discard any changes.
+void Input_EnterAssignMode(void);
+
+// Finish assigning keys and buttons.
+void Input_ExitAssignMode(bool confirm);
+
 // Assign a concrete scancode to the given layout and key role.
 void Input_AssignScancode(
     INPUT_LAYOUT layout_num, INPUT_ROLE role, INPUT_SCANCODE scancode);
diff --git a/src/game/option/option_control.c b/src/game/option/option_control.c
index 736b7f22..0dfb95d9 100644
--- a/src/game/option/option_control.c
+++ b/src/game/option/option_control.c
@@ -663,6 +663,8 @@ CONTROL_MODE Option_Control(INVENTORY_ITEM *inv_item, CONTROL_MODE mode)
         ? CtrlTextPlacementCheats
         : CtrlTextPlacementNormal;

+    Input_EnterAssignMode();
+
     switch (m_KeyMode) {
     case KM_BROWSE:
         if (layout_num > INPUT_LAYOUT_DEFAULT) {
@@ -683,6 +685,7 @@ CONTROL_MODE Option_Control(INVENTORY_ITEM *inv_item, CONTROL_MODE mode)
         if (g_InputDB.deselect
             || (g_InputDB.select && m_ControlMenu.cur_role == KC_TITLE)) {
             Option_ControlShutdownText();
+            Input_ExitAssignMode(/*?*/);
             m_KeyMode = KM_INACTIVE;
             g_Input = (INPUT_STATE) { 0 };
             g_InputDB = (INPUT_STATE) { 0 };
@@ -783,5 +786,6 @@ CONTROL_MODE Option_Control(INVENTORY_ITEM *inv_item, CONTROL_MODE mode)

     m_ControlMenu.prev_row_num = m_ControlMenu.row_num;

+    Input_ExitAssignMode(/*?*/);
     return mode;
 }
diff --git a/src/specific/s_input.c b/src/specific/s_input.c
index 6cafdd11..eaa10b2b 100644
--- a/src/specific/s_input.c
+++ b/src/specific/s_input.c
@@ -26,7 +26,7 @@ typedef struct CONTROLLER_MAP {
     int16_t axis_dir;
 } CONTROLLER_MAP;

-static INPUT_SCANCODE m_Layout[INPUT_LAYOUT_NUMBER_OF][INPUT_ROLE_NUMBER_OF] = {
+static INPUT_SCANCODE m_KeyboardLayout[INPUT_LAYOUT_NUMBER_OF][INPUT_ROLE_NUMBER_OF] = {
     // clang-format off
     // built-in controls
     {
@@ -325,6 +325,9 @@ static CONTROLLER_MAP
         // clang-format on
     };

+static INPUT_SCANCODE m_KeyboardLayoutAux[INPUT_LAYOUT_NUMBER_OF][INPUT_ROLE_NUMBER_OF];
+static CONTROLLER_MAP m_ControllerLayoutAux[INPUT_LAYOUT_NUMBER_OF][INPUT_ROLE_NUMBER_OF];
+
 const Uint8 *m_KeyboardState;
 static SDL_GameController *m_Controller = NULL;
 static const char *m_ControllerName = NULL;
@@ -765,7 +768,7 @@ static const char *S_Input_GetAxisName(

 static bool S_Input_Key(INPUT_ROLE role, INPUT_LAYOUT layout_num)
 {
-    INPUT_SCANCODE scancode = m_Layout[layout_num][role];
+    INPUT_SCANCODE scancode = m_KeyboardLayout[layout_num][role];
     if (KEY_DOWN(scancode)) {
         return true;
     }
@@ -958,7 +961,7 @@ INPUT_STATE S_Input_GetCurrentState(
 INPUT_SCANCODE S_Input_GetAssignedScancode(
     INPUT_LAYOUT layout_num, INPUT_ROLE role)
 {
-    return m_Layout[layout_num][role];
+    return m_KeyboardLayout[layout_num][role];
 }

 int16_t S_Input_GetUniqueBind(INPUT_LAYOUT layout_num, INPUT_ROLE role)
@@ -997,10 +1000,26 @@ int16_t S_Input_GetAssignedAxisDir(INPUT_LAYOUT layout_num, INPUT_ROLE role)
     return m_ControllerLayout[layout_num][role].axis_dir;
 }

+void S_Input_EnterAssignMode(void)
+{
+    m_AssignMode = true;
+    memcpy(m_KeyboardLayoutAux, m_KeyboardLayout, sizeof(INPUT_SCANCODE) * INPUT_LAYOUT_NUMBER_OF * INPUT_ROLE_NUMBER_OF);
+    memcpy(m_ControllerLayoutAux, m_ControllerLayout, sizeof(CONTROLLER_MAP) * INPUT_LAYOUT_NUMBER_OF * INPUT_ROLE_NUMBER_OF);
+}
+
+void S_Input_ExitAssignMode(bool confirm)
+{
+    m_AssignMode = false;
+    if (confirm) {
+        memcpy(m_KeyboardLayout, m_KeyboardLayoutAux, sizeof(INPUT_SCANCODE) * INPUT_LAYOUT_NUMBER_OF * INPUT_ROLE_NUMBER_OF);
+        memcpy(m_ControllerLayout, m_ControllerLayoutAux, sizeof(CONTROLLER_MAP) * INPUT_LAYOUT_NUMBER_OF * INPUT_ROLE_NUMBER_OF);
+    }
+}
+
 void S_Input_AssignScancode(
     INPUT_LAYOUT layout_num, INPUT_ROLE role, INPUT_SCANCODE scancode)
 {
-    m_Layout[layout_num][role] = scancode;
+    m_KeyboardLayoutAux[layout_num][role] = scancode;
 }

 void S_Input_AssignButton(
@@ -1036,7 +1055,7 @@ bool S_Input_ReadAndAssignKey(
         for (INPUT_SCANCODE scancode = 0; scancode < SDL_NUM_SCANCODES;
              scancode++) {
             if (KEY_DOWN(scancode)) {
-                m_Layout[layout_num][role] = scancode;
+                m_KeyboardLayout[layout_num][role] = scancode;
                 return true;
             }
         }
@@ -1064,9 +1083,11 @@ const char *S_Input_GetKeyName(
     CONTROL_MODE mode, INPUT_LAYOUT layout_num, INPUT_ROLE role)
 {
     if (mode == CM_KEYBOARD) {
-        return S_Input_GetScancodeName(m_Layout[layout_num][role]);
+        const blabla layout = m_EditMode ? m_KeyboardLayoutAux : m_KeyboardLayout;
+        return S_Input_GetScancodeName(layout[layout_num][role]);
     } else {
-        CONTROLLER_MAP check = m_ControllerLayout[layout_num][role];
+        const blabla layout = m_EditMode ? m_ControllerLayoutAux : m_ControllerLayout;
+        CONTROLLER_MAP check = layout[layout_num][role];
         if (check.type == BT_BUTTON) {
             return S_Input_GetButtonName(check.bind.button);
         } else {
diff --git a/src/specific/s_input.h b/src/specific/s_input.h
index b3140555..c0cc2f37 100644
--- a/src/specific/s_input.h
+++ b/src/specific/s_input.h
@@ -23,6 +23,10 @@ int16_t S_Input_GetAssignedBind(INPUT_LAYOUT layout_num, INPUT_ROLE role);

 int16_t S_Input_GetAssignedAxisDir(INPUT_LAYOUT layout_num, INPUT_ROLE role);

+void S_Input_EnterAssignMode(void);
+
+void S_Input_ExitAssignMode(bool confirm);
+
 void S_Input_AssignScancode(
     INPUT_LAYOUT layout_num, INPUT_ROLE role, INPUT_SCANCODE scancode);