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))