From 135df58e0afffe74a6f0feb71bac1aa475aecaeb Mon Sep 17 00:00:00 2001 From: James Dansereau <106631954+jkdansereau@users.noreply.github.com> Date: Sun, 13 Sep 2026 15:57:46 -0400 Subject: [PATCH] input: modern dual-stick controller layout (Xbox re-release scheme) Steam Deck target. LS move/strafe + RS look + RT fire + LT aim were already in place (D194/D238 naturalPitchMode); this completes the scheme with shoulder weapon cycling per the console re-releases: RB rising edge = next weapon, LB rising edge = previous weapon. Cycling rides the game's own A-line formulas (bondview2.c weaponForwardOffset/weaponBackOffset): a fresh A edge cycles forward; A+Z presented on the same tick cycles backward -- the identical trick the mouse-wheel path uses (D223), which also guarantees no stray shot (triggerOn is gated off while invButtons is held). Edges are emitted for exactly one poll so holding a shoulder cannot latch A and block firing; both are suppressed in menu mode where A means select. A/X = action/use, B/Y = crouch/cancel (unchanged); LB no longer maps to lean. README controls table + release notes updated. --- .github/release-notes.md | 3 +++ README.md | 13 +++++++++---- port/src/input.c | 23 +++++++++++++++++++---- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/.github/release-notes.md b/.github/release-notes.md index d0eeb370..b3223087 100644 --- a/.github/release-notes.md +++ b/.github/release-notes.md @@ -23,6 +23,9 @@ - **QoL**: F10 in-game options overlay (fullscreen, resolution, frame cap, MSAA, texture filtering, FOV/draw distance, sensitivity), mute-on-focus-loss, F12 screenshot. +- **Modern dual-stick controller layout** (the scheme used by the console + re-releases): left stick move/strafe, right stick look, right trigger fire, + left trigger aim, A/X use, B/Y crouch/cancel, **RB/LB cycle weapons**. - **Linux / Steam Deck**: the Linux bundle now ships its own SDL2 — it runs as-is on any distro, and sideloads onto a Steam Deck with nothing installed. diff --git a/README.md b/README.md index edee89a9..a26c3c27 100644 --- a/README.md +++ b/README.md @@ -267,15 +267,20 @@ Configuration is written to `ge007.ini` on first run. | Action | Keyboard / mouse | Controller | |---------------------|--------------------------|---------------| -| Move / strafe | `W` `A` `S` `D` / arrows | Left stick | +| Move / strafe | `W` `A` `S` `D` / arrows | Left stick (or D-pad) | | Aim / look | Mouse | Right stick | | Fire (Z) | Left mouse / `LCtrl` | Right trigger | | Aim mode (R) | Right mouse / `LShift` | Left trigger | -| Use / accept (A) | `Space` / `E` / `X` | A / X | -| Reload / cancel (B) | `R` / `F` | B / Y / RB | -| L trigger | `Q` | LB | +| Use / accept (A) | `Space` / `E` / `X` | A / X | +| Reload / cancel (B) | `R` / `F` | B / Y | +| Next weapon | Mouse wheel up | RB | +| Previous weapon | Mouse wheel down | LB | | Start | `Enter` / `Tab` | Start | +The controller layout follows the modern dual-stick scheme used by the +console re-releases (left stick move, right stick look, triggers fire/aim, +shoulders cycle weapons). + Mouse sensitivity, Y-inversion and the aim/turn split are tunable in the `[Input]` section of `ge007.ini`. diff --git a/port/src/input.c b/port/src/input.c index 6b4e8061..966d5b2d 100644 --- a/port/src/input.c +++ b/port/src/input.c @@ -274,6 +274,7 @@ static int numControllers = 1; static int connectedMask = 0x1; /* controller 0 always present */ static SDL_GameController *pads[MAX_PADS]; +static int padShoulderPrev[MAX_PADS]; /* LB/RB edge state for weapon cycling */ static int mouseEnabled = 1; static int mouseGrabbed = 1; /* released while the window is unfocused */ @@ -1120,15 +1121,29 @@ unsigned inputComputePad(int idx, signed char *stick_x, signed char *stick_y) if (SDL_GameControllerGetAxis(pad, SDL_CONTROLLER_AXIS_TRIGGERLEFT) > trigPt) button |= GE_CONT_R; + /* Modern dual-stick layout (Xbox re-release style; the Steam Deck + * target). A/X = action/use/reload (the game's context-sensitive A + * line), B/Y = crouch, and LB/RB rising edges cycle weapons. + * In-game cycling is an A edge (forward) or A+Z held on the same tick + * (backward -- bondview2.c weaponForwardOffset/weaponBackOffset, the + * same trick the mouse wheel uses above); emit for exactly one poll + * so holding RB cannot latch invButtons and block firing. */ if (SDL_GameControllerGetButton(pad, SDL_CONTROLLER_BUTTON_A) || SDL_GameControllerGetButton(pad, SDL_CONTROLLER_BUTTON_X)) button |= GE_CONT_A; if (SDL_GameControllerGetButton(pad, SDL_CONTROLLER_BUTTON_B) || - SDL_GameControllerGetButton(pad, SDL_CONTROLLER_BUTTON_Y) || - SDL_GameControllerGetButton(pad, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)) + SDL_GameControllerGetButton(pad, SDL_CONTROLLER_BUTTON_Y)) button |= GE_CONT_B; - if (SDL_GameControllerGetButton(pad, SDL_CONTROLLER_BUTTON_LEFTSHOULDER)) - button |= GE_CONT_L; + int padMenuMode = (current_menu != GE_MENU_RUN_STAGE && + current_menu != GE_MENU_INVALID); + if (!padMenuMode) { + int lbNow = SDL_GameControllerGetButton(pad, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); + int rbNow = SDL_GameControllerGetButton(pad, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); + int *prev = &padShoulderPrev[idx]; + if (rbNow && !(*prev & 1)) button |= GE_CONT_A; /* next weapon */ + if (lbNow && !(*prev & 2)) button |= GE_CONT_A | GE_CONT_G; /* prev weapon */ + *prev = (rbNow ? 1 : 0) | (lbNow ? 2 : 0); + } if (SDL_GameControllerGetButton(pad, SDL_CONTROLLER_BUTTON_START)) button |= GE_CONT_START; if (SDL_GameControllerGetButton(pad, SDL_CONTROLLER_BUTTON_DPAD_UP))