- Free view: hold a mapped input (default: left grip) and that thumbstick drives the engine's mouse look, with the stick's normal bindings suspended while held. Also available for the right stick, unbound by default. - Cyberspace actions: eleven thrust/climb/dive/bank/roll actions with their own bindings, active only in cyberspace. The normal movement actions are inactive there, and capture exclusivity only compares actions that can be active together, so one input can be bound in both sets. - Cyberspace pitch and bank scale with thumbstick deflection instead of running at full strength. - Remove the toggle_freelook action; bind the automap view to the right stick click by default. - Engine patches: 24 exposes whether the player is in cyberspace, 25 lets the controller code scale cyberspace pitch and bank.
This commit is contained in:
@@ -79,7 +79,7 @@ startup, in addition to (and independent of) the engine's own `prefs.txt`.
|
|||||||
"mouse_double_click": "right_b",
|
"mouse_double_click": "right_b",
|
||||||
"stand": "right_thumbstick_up",
|
"stand": "right_thumbstick_up",
|
||||||
"prone": "right_thumbstick_down",
|
"prone": "right_thumbstick_down",
|
||||||
"toggle_freelook": "right_thumbstick_click",
|
"map_view": "right_thumbstick_click",
|
||||||
"fire_weapon": "right_trigger"
|
"fire_weapon": "right_trigger"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,10 +217,10 @@ Which way the player is virtually facing, in degrees.
|
|||||||
|
|
||||||
## Player Controls (`player_controls`)
|
## Player Controls (`player_controls`)
|
||||||
|
|
||||||
Maps each of 45 in-game actions to a physical controller input: 9
|
Maps each of 57 in-game actions to a physical controller input: 9
|
||||||
movement/click actions plus 36 for the engine's own keyboard hotkeys and
|
movement/click actions, 2 free-view actions, 11 cyberspace actions, and 35
|
||||||
firing (posture, save/load, weapon select, MFD panels, etc. - see the hotkey
|
for the engine's own keyboard hotkeys and firing (posture, save/load, weapon
|
||||||
table below). Some actions are bound by default (see the default-input
|
select, MFD panels, etc. - see the hotkey table below). Some actions are bound by default (see the default-input
|
||||||
columns); the rest are unbound and can be bound on demand. Assign them in the
|
columns); the rest are unbound and can be bound on demand. Assign them in the
|
||||||
**Controller Options** menu tab: tap an action's binding button, then press
|
**Controller Options** menu tab: tap an action's binding button, then press
|
||||||
the button or stick direction to use ("Press a button…"); tap it again, or
|
the button or stick direction to use ("Press a button…"); tap it again, or
|
||||||
@@ -258,6 +258,8 @@ wait 8 seconds, to cancel without changing it.
|
|||||||
| `jump` | `left_thumbstick_click` | Jump |
|
| `jump` | `left_thumbstick_click` | Jump |
|
||||||
| `mouse_click` | `right_a` | Click - operates the keyboard/game quad's cursor, whichever the pointing hand's ray is over |
|
| `mouse_click` | `right_a` | Click - operates the keyboard/game quad's cursor, whichever the pointing hand's ray is over |
|
||||||
| `mouse_double_click` | `right_b` | Double-click - game quad only (e.g. using an inventory item); has no effect over the menu or keyboard |
|
| `mouse_double_click` | `right_b` | Double-click - game quad only (e.g. using an inventory item); has no effect over the menu or keyboard |
|
||||||
|
| `free_left_thumbstick_view` | `left_squeeze` | Hold to look around with the left thumbstick (see below) |
|
||||||
|
| `free_right_thumbstick_view` | `none` | Hold to look around with the right thumbstick (see below) |
|
||||||
|
|
||||||
Movement actions bound to a thumbstick direction are suppressed while the
|
Movement actions bound to a thumbstick direction are suppressed while the
|
||||||
VR menu is open, since the same physical stick already snap-turns/smooth-
|
VR menu is open, since the same physical stick already snap-turns/smooth-
|
||||||
@@ -265,6 +267,45 @@ moves the player in that state (see `player_position` above) - this is the
|
|||||||
one case where a binding's effect depends on whether the menu is open, and
|
one case where a binding's effect depends on whether the menu is open, and
|
||||||
it only ever affects the 8 thumbstick-direction inputs below.
|
it only ever affects the 8 thumbstick-direction inputs below.
|
||||||
|
|
||||||
|
**Free view** (`free_left_thumbstick_view`, `free_right_thumbstick_view`) is
|
||||||
|
the engine's mouse look driven by a thumbstick. While the bound input is held,
|
||||||
|
that stick looks around instead of doing its normal job: left/right turns the
|
||||||
|
view, up/down looks up and down, in proportion to how far the stick is pushed.
|
||||||
|
The stick's own direction bindings (move, strafe, turn, stand/prone) are
|
||||||
|
suspended while it is held, and the cursor stays centred in the view. Release
|
||||||
|
the input to return to normal. It has no effect while the VR menu is open.
|
||||||
|
|
||||||
|
### Cyberspace actions
|
||||||
|
|
||||||
|
Cyberspace has its own set of movement actions, active only while the player
|
||||||
|
is in cyberspace. While in cyberspace the normal movement actions
|
||||||
|
(`move_*`, `strafe_*`, `turn_*`, `jump`, `stand`, `crouch`, `prone`) are
|
||||||
|
inactive, and the cyberspace actions are inactive everywhere else. Each set
|
||||||
|
has its own bindings, so one input can be bound in both (for example the left
|
||||||
|
stick up is `move_forward` on foot and `cyber_climb` in cyberspace);
|
||||||
|
the Controller Options tab only reports a conflict between actions that can
|
||||||
|
be active at the same time. Free view, clicking and firing work in both.
|
||||||
|
|
||||||
|
Pitch and bank (`cyber_climb`, `cyber_dive`, `cyber_bank_*` and the four
|
||||||
|
diagonals) are proportional when bound to a thumbstick direction: pushing the
|
||||||
|
stick further turns faster, from 20% of full speed just past the trigger point
|
||||||
|
to 60% at full deflection. Bound to a button they run at full speed. Thrust and
|
||||||
|
roll are always full speed.
|
||||||
|
|
||||||
|
| Action key | Default input | Keyboard equivalent | Effect |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `cyber_thrust` | `right_thumbstick_up` | `s` | Thrust forward |
|
||||||
|
| `cyber_climb` | `left_thumbstick_up` | `w` | Pitch up |
|
||||||
|
| `cyber_dive` | `left_thumbstick_down` | `x` | Pitch down |
|
||||||
|
| `cyber_bank_left` | `left_thumbstick_left` | `a` | Bank left |
|
||||||
|
| `cyber_bank_right` | `left_thumbstick_right` | `d` | Bank right |
|
||||||
|
| `cyber_roll_left` | `right_thumbstick_left` | `e` | Roll left |
|
||||||
|
| `cyber_roll_right` | `right_thumbstick_right` | `q` | Roll right |
|
||||||
|
| `cyber_climb_left` | `none` | Keypad 7 | Pitch up and bank left |
|
||||||
|
| `cyber_climb_right` | `none` | Keypad 9 | Pitch up and bank right |
|
||||||
|
| `cyber_dive_left` | `none` | Keypad 1 | Pitch down and bank left |
|
||||||
|
| `cyber_dive_right` | `none` | Keypad 3 | Pitch down and bank right |
|
||||||
|
|
||||||
`mouse_click` additionally behaves differently depending on the menu:
|
`mouse_click` additionally behaves differently depending on the menu:
|
||||||
while the VR menu is open, its "click" is always whichever hand's trigger
|
while the VR menu is open, its "click" is always whichever hand's trigger
|
||||||
is used to interact with the menu (so you can never lock yourself out of
|
is used to interact with the menu (so you can never lock yourself out of
|
||||||
@@ -277,8 +318,8 @@ bind the `trigger` identifier (see below) for either hand to click.
|
|||||||
### Hotkey actions
|
### Hotkey actions
|
||||||
|
|
||||||
The engine's own keyboard hotkeys (`HotKeyLookup[]` in `Prefs.c`) plus
|
The engine's own keyboard hotkeys (`HotKeyLookup[]` in `Prefs.c`) plus
|
||||||
firing, as 36 bindable actions. `stand` (`right_thumbstick_up`), `prone`
|
firing, as 35 bindable actions. `stand` (`right_thumbstick_up`), `prone`
|
||||||
(`right_thumbstick_down`), `toggle_freelook` (`right_thumbstick_click`) and
|
(`right_thumbstick_down`), `map_view` (`right_thumbstick_click`) and
|
||||||
`fire_weapon` (`right_trigger`) are bound by default; the rest are unbound
|
`fire_weapon` (`right_trigger`) are bound by default; the rest are unbound
|
||||||
(`none`). Each fires once per press, like pressing the key on a real
|
(`none`). Each fires once per press, like pressing the key on a real
|
||||||
keyboard (there is no hold behavior).
|
keyboard (there is no hold behavior).
|
||||||
@@ -288,7 +329,6 @@ keyboard (there is no hold behavior).
|
|||||||
| `stand` | `t` | Stand up |
|
| `stand` | `t` | Stand up |
|
||||||
| `crouch` | `g` | Crouch |
|
| `crouch` | `g` | Crouch |
|
||||||
| `prone` | `b` | Go prone |
|
| `prone` | `b` | Go prone |
|
||||||
| `toggle_freelook` | `f` | Toggle mouse-look |
|
|
||||||
| `pause` | `p` | Pause the game |
|
| `pause` | `p` | Pause the game |
|
||||||
| `full_view` | Ctrl+F | Switch to the fullscreen view loop |
|
| `full_view` | Ctrl+F | Switch to the fullscreen view loop |
|
||||||
| `normal_view` | Ctrl+D | Switch to the normal gameplay view |
|
| `normal_view` | Ctrl+D | Switch to the normal gameplay view |
|
||||||
@@ -317,6 +357,7 @@ keyboard (there is no hold behavior).
|
|||||||
**Not exposed** (other `HotKeyLookup[]` entries):
|
**Not exposed** (other `HotKeyLookup[]` entries):
|
||||||
- `keypad 0`-`keypad 9`: a controller button per digit is not useful; use the
|
- `keypad 0`-`keypad 9`: a controller button per digit is not useful; use the
|
||||||
on-screen keyboard.
|
on-screen keyboard.
|
||||||
|
- `toggle_freelook` (F): the free-view actions cover mouse look.
|
||||||
- The 4 `cheat_*` entries: debug cheats.
|
- The 4 `cheat_*` entries: debug cheats.
|
||||||
- `mac_help` (disabled) and `audiolog_cancel` (its build flag is not
|
- `mac_help` (disabled) and `audiolog_cancel` (its build flag is not
|
||||||
defined): they are not real hotkeys in this build.
|
defined): they are not real hotkeys in this build.
|
||||||
|
|||||||
@@ -94,9 +94,10 @@ static const char *kRawInputNames[QS_RAW_COUNT] = {
|
|||||||
"right_thumbstick_right", "trigger", "squeeze", "none",
|
"right_thumbstick_right", "trigger", "squeeze", "none",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Remappable game actions: movement, click and jump, the engine's own
|
// Remappable game actions: movement, click and jump, free view, a separate set
|
||||||
// keyboard hotkeys (Prefs.c's HotKeyLookup[]), and fire. The hotkey actions
|
// of cyberspace movement actions, the engine's own keyboard hotkeys (Prefs.c's
|
||||||
// default to QS_RAW_NONE (unbound). menu_toggle is not an action; it is fixed
|
// HotKeyLookup[]), and fire. The hotkey actions default to QS_RAW_NONE
|
||||||
|
// (unbound). menu_toggle is not an action; it is fixed
|
||||||
// (see xr_input_sync_and_draw()).
|
// (see xr_input_sync_and_draw()).
|
||||||
//
|
//
|
||||||
// HotKeyLookup[] entries not exposed:
|
// HotKeyLookup[] entries not exposed:
|
||||||
@@ -121,7 +122,6 @@ typedef enum {
|
|||||||
QS_ACTION_STAND,
|
QS_ACTION_STAND,
|
||||||
QS_ACTION_CROUCH,
|
QS_ACTION_CROUCH,
|
||||||
QS_ACTION_PRONE,
|
QS_ACTION_PRONE,
|
||||||
QS_ACTION_TOGGLE_FREELOOK,
|
|
||||||
QS_ACTION_PAUSE,
|
QS_ACTION_PAUSE,
|
||||||
QS_ACTION_FULL_VIEW,
|
QS_ACTION_FULL_VIEW,
|
||||||
QS_ACTION_NORMAL_VIEW,
|
QS_ACTION_NORMAL_VIEW,
|
||||||
@@ -154,6 +154,19 @@ typedef enum {
|
|||||||
QS_ACTION_MFD_RIGHT_4,
|
QS_ACTION_MFD_RIGHT_4,
|
||||||
QS_ACTION_MFD_RIGHT_5,
|
QS_ACTION_MFD_RIGHT_5,
|
||||||
QS_ACTION_FIRE_WEAPON,
|
QS_ACTION_FIRE_WEAPON,
|
||||||
|
QS_ACTION_FREE_LEFT_THUMBSTICK_VIEW,
|
||||||
|
QS_ACTION_FREE_RIGHT_THUMBSTICK_VIEW,
|
||||||
|
QS_ACTION_CYBER_THRUST,
|
||||||
|
QS_ACTION_CYBER_CLIMB,
|
||||||
|
QS_ACTION_CYBER_DIVE,
|
||||||
|
QS_ACTION_CYBER_BANK_LEFT,
|
||||||
|
QS_ACTION_CYBER_BANK_RIGHT,
|
||||||
|
QS_ACTION_CYBER_ROLL_LEFT,
|
||||||
|
QS_ACTION_CYBER_ROLL_RIGHT,
|
||||||
|
QS_ACTION_CYBER_CLIMB_LEFT,
|
||||||
|
QS_ACTION_CYBER_CLIMB_RIGHT,
|
||||||
|
QS_ACTION_CYBER_DIVE_LEFT,
|
||||||
|
QS_ACTION_CYBER_DIVE_RIGHT,
|
||||||
QS_ACTION_COUNT
|
QS_ACTION_COUNT
|
||||||
} QsControlAction;
|
} QsControlAction;
|
||||||
|
|
||||||
@@ -170,7 +183,6 @@ static const char *kActionNames[QS_ACTION_COUNT] = {
|
|||||||
"stand",
|
"stand",
|
||||||
"crouch",
|
"crouch",
|
||||||
"prone",
|
"prone",
|
||||||
"toggle_freelook",
|
|
||||||
"pause",
|
"pause",
|
||||||
"full_view",
|
"full_view",
|
||||||
"normal_view",
|
"normal_view",
|
||||||
@@ -203,14 +215,28 @@ static const char *kActionNames[QS_ACTION_COUNT] = {
|
|||||||
"mfd_right_4",
|
"mfd_right_4",
|
||||||
"mfd_right_5",
|
"mfd_right_5",
|
||||||
"fire_weapon",
|
"fire_weapon",
|
||||||
|
"free_left_thumbstick_view",
|
||||||
|
"free_right_thumbstick_view",
|
||||||
|
"cyber_thrust",
|
||||||
|
"cyber_climb",
|
||||||
|
"cyber_dive",
|
||||||
|
"cyber_bank_left",
|
||||||
|
"cyber_bank_right",
|
||||||
|
"cyber_roll_left",
|
||||||
|
"cyber_roll_right",
|
||||||
|
"cyber_climb_left",
|
||||||
|
"cyber_climb_right",
|
||||||
|
"cyber_dive_left",
|
||||||
|
"cyber_dive_right",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Current bindings; xr_input.c owns them (qs_config.c applies parsed config via
|
// Current bindings; xr_input.c owns them (qs_config.c applies parsed config via
|
||||||
// xr_input_set_action_binding() and reads them back for Save). Defaults: left
|
// xr_input_set_action_binding() and reads them back for Save). Defaults: left
|
||||||
// stick moves (up/down) and strafes (left/right), its click jumps; right stick
|
// stick moves (up/down) and strafes (left/right), its click jumps; right stick
|
||||||
// turns (left/right) and stands/goes prone (up/down), its click toggles free
|
// turns (left/right) and stands/goes prone (up/down), its click opens the
|
||||||
// look; right A/B click/double-click; right trigger fires. Everything else is
|
// automap; right A/B click/double-click; right trigger fires; left grip holds
|
||||||
// QS_RAW_NONE.
|
// free view on the left stick. In cyberspace: left stick climbs/dives and banks,
|
||||||
|
// right stick thrusts (up) and rolls. Everything else is QS_RAW_NONE.
|
||||||
static QsRawInput s_binding[QS_ACTION_COUNT] = {
|
static QsRawInput s_binding[QS_ACTION_COUNT] = {
|
||||||
QS_RAW_LEFT_THUMBSTICK_UP, // move_forward
|
QS_RAW_LEFT_THUMBSTICK_UP, // move_forward
|
||||||
QS_RAW_LEFT_THUMBSTICK_DOWN, // move_back
|
QS_RAW_LEFT_THUMBSTICK_DOWN, // move_back
|
||||||
@@ -224,11 +250,10 @@ static QsRawInput s_binding[QS_ACTION_COUNT] = {
|
|||||||
QS_RAW_RIGHT_THUMBSTICK_UP, // stand
|
QS_RAW_RIGHT_THUMBSTICK_UP, // stand
|
||||||
QS_RAW_NONE, // crouch
|
QS_RAW_NONE, // crouch
|
||||||
QS_RAW_RIGHT_THUMBSTICK_DOWN, // prone
|
QS_RAW_RIGHT_THUMBSTICK_DOWN, // prone
|
||||||
QS_RAW_RIGHT_THUMBSTICK_CLICK, // toggle_freelook
|
|
||||||
QS_RAW_NONE, // pause
|
QS_RAW_NONE, // pause
|
||||||
QS_RAW_NONE, // full_view
|
QS_RAW_NONE, // full_view
|
||||||
QS_RAW_NONE, // normal_view
|
QS_RAW_NONE, // normal_view
|
||||||
QS_RAW_NONE, // map_view
|
QS_RAW_RIGHT_THUMBSTICK_CLICK, // map_view
|
||||||
QS_RAW_NONE, // save_game
|
QS_RAW_NONE, // save_game
|
||||||
QS_RAW_NONE, // load_game
|
QS_RAW_NONE, // load_game
|
||||||
QS_RAW_NONE, // select_grenade
|
QS_RAW_NONE, // select_grenade
|
||||||
@@ -257,6 +282,19 @@ static QsRawInput s_binding[QS_ACTION_COUNT] = {
|
|||||||
QS_RAW_NONE, // mfd_right_4
|
QS_RAW_NONE, // mfd_right_4
|
||||||
QS_RAW_NONE, // mfd_right_5
|
QS_RAW_NONE, // mfd_right_5
|
||||||
QS_RAW_RIGHT_TRIGGER, // fire_weapon
|
QS_RAW_RIGHT_TRIGGER, // fire_weapon
|
||||||
|
QS_RAW_LEFT_SQUEEZE, // free_left_thumbstick_view
|
||||||
|
QS_RAW_NONE, // free_right_thumbstick_view
|
||||||
|
QS_RAW_RIGHT_THUMBSTICK_UP, // cyber_thrust
|
||||||
|
QS_RAW_LEFT_THUMBSTICK_UP, // cyber_climb
|
||||||
|
QS_RAW_LEFT_THUMBSTICK_DOWN, // cyber_dive
|
||||||
|
QS_RAW_LEFT_THUMBSTICK_LEFT, // cyber_bank_left
|
||||||
|
QS_RAW_LEFT_THUMBSTICK_RIGHT, // cyber_bank_right
|
||||||
|
QS_RAW_RIGHT_THUMBSTICK_LEFT, // cyber_roll_left
|
||||||
|
QS_RAW_RIGHT_THUMBSTICK_RIGHT, // cyber_roll_right
|
||||||
|
QS_RAW_NONE, // cyber_climb_left
|
||||||
|
QS_RAW_NONE, // cyber_climb_right
|
||||||
|
QS_RAW_NONE, // cyber_dive_left
|
||||||
|
QS_RAW_NONE, // cyber_dive_right
|
||||||
};
|
};
|
||||||
|
|
||||||
// One entry per key-injecting action. A plain, unmodified, printable key
|
// One entry per key-injecting action. A plain, unmodified, printable key
|
||||||
@@ -282,11 +320,10 @@ static const QsKeyAction kKeyActions[] = {
|
|||||||
{QS_ACTION_TURN_LEFT, SDL_SCANCODE_LEFT, SDLK_LEFT, KMOD_NONE, false, 0},
|
{QS_ACTION_TURN_LEFT, SDL_SCANCODE_LEFT, SDLK_LEFT, KMOD_NONE, false, 0},
|
||||||
{QS_ACTION_TURN_RIGHT, SDL_SCANCODE_RIGHT, SDLK_RIGHT, KMOD_NONE, false, 0},
|
{QS_ACTION_TURN_RIGHT, SDL_SCANCODE_RIGHT, SDLK_RIGHT, KMOD_NONE, false, 0},
|
||||||
{QS_ACTION_JUMP, SDL_SCANCODE_SPACE, SDLK_SPACE, KMOD_NONE, true, ' '},
|
{QS_ACTION_JUMP, SDL_SCANCODE_SPACE, SDLK_SPACE, KMOD_NONE, true, ' '},
|
||||||
// "stand"/"crouch"/"prone"/"toggle_freelook"/"pause" - DOWN('t'/'g'/'b'/'f'/'p').
|
// "stand"/"crouch"/"prone"/"pause" - DOWN('t'/'g'/'b'/'p').
|
||||||
{QS_ACTION_STAND, SDL_SCANCODE_T, SDLK_t, KMOD_NONE, true, 't'},
|
{QS_ACTION_STAND, SDL_SCANCODE_T, SDLK_t, KMOD_NONE, true, 't'},
|
||||||
{QS_ACTION_CROUCH, SDL_SCANCODE_G, SDLK_g, KMOD_NONE, true, 'g'},
|
{QS_ACTION_CROUCH, SDL_SCANCODE_G, SDLK_g, KMOD_NONE, true, 'g'},
|
||||||
{QS_ACTION_PRONE, SDL_SCANCODE_B, SDLK_b, KMOD_NONE, true, 'b'},
|
{QS_ACTION_PRONE, SDL_SCANCODE_B, SDLK_b, KMOD_NONE, true, 'b'},
|
||||||
{QS_ACTION_TOGGLE_FREELOOK, SDL_SCANCODE_F, SDLK_f, KMOD_NONE, true, 'f'},
|
|
||||||
{QS_ACTION_PAUSE, SDL_SCANCODE_P, SDLK_p, KMOD_NONE, true, 'p'},
|
{QS_ACTION_PAUSE, SDL_SCANCODE_P, SDLK_p, KMOD_NONE, true, 'p'},
|
||||||
// "full_view"/"normal_view"/"map_view" - CTRL('f'/'d'/'a') - jump
|
// "full_view"/"normal_view"/"map_view" - CTRL('f'/'d'/'a') - jump
|
||||||
// straight to the fullscreen/game/automap loop mode.
|
// straight to the fullscreen/game/automap loop mode.
|
||||||
@@ -335,6 +372,19 @@ static const QsKeyAction kKeyActions[] = {
|
|||||||
// view3d_key_handler()): Enter fires the readied weapon at the cursor and
|
// view3d_key_handler()): Enter fires the readied weapon at the cursor and
|
||||||
// re-arms on release. Clicking in the 3D view (mouse_click) also fires.
|
// re-arms on release. Clicking in the 3D view (mouse_click) also fires.
|
||||||
{QS_ACTION_FIRE_WEAPON, SDL_SCANCODE_RETURN, SDLK_RETURN, KMOD_NONE, false, 0},
|
{QS_ACTION_FIRE_WEAPON, SDL_SCANCODE_RETURN, SDLK_RETURN, KMOD_NONE, false, 0},
|
||||||
|
// Cyberspace movement (Prefs.c's MoveCyberKeybinds defaults): S thrust, W climb, X dive,
|
||||||
|
// A/D bank, E/Q roll, and the keypad 7/9/1/3 diagonal combinations.
|
||||||
|
{QS_ACTION_CYBER_THRUST, SDL_SCANCODE_S, SDLK_s, KMOD_NONE, true, 's'},
|
||||||
|
{QS_ACTION_CYBER_CLIMB, SDL_SCANCODE_W, SDLK_w, KMOD_NONE, true, 'w'},
|
||||||
|
{QS_ACTION_CYBER_DIVE, SDL_SCANCODE_X, SDLK_x, KMOD_NONE, true, 'x'},
|
||||||
|
{QS_ACTION_CYBER_BANK_LEFT, SDL_SCANCODE_A, SDLK_a, KMOD_NONE, true, 'a'},
|
||||||
|
{QS_ACTION_CYBER_BANK_RIGHT, SDL_SCANCODE_D, SDLK_d, KMOD_NONE, true, 'd'},
|
||||||
|
{QS_ACTION_CYBER_ROLL_LEFT, SDL_SCANCODE_E, SDLK_e, KMOD_NONE, true, 'e'},
|
||||||
|
{QS_ACTION_CYBER_ROLL_RIGHT, SDL_SCANCODE_Q, SDLK_q, KMOD_NONE, true, 'q'},
|
||||||
|
{QS_ACTION_CYBER_CLIMB_LEFT, SDL_SCANCODE_KP_7, SDLK_KP_7, KMOD_NONE, false, 0},
|
||||||
|
{QS_ACTION_CYBER_CLIMB_RIGHT, SDL_SCANCODE_KP_9, SDLK_KP_9, KMOD_NONE, false, 0},
|
||||||
|
{QS_ACTION_CYBER_DIVE_LEFT, SDL_SCANCODE_KP_1, SDLK_KP_1, KMOD_NONE, false, 0},
|
||||||
|
{QS_ACTION_CYBER_DIVE_RIGHT, SDL_SCANCODE_KP_3, SDLK_KP_3, KMOD_NONE, false, 0},
|
||||||
};
|
};
|
||||||
#define QS_NUM_KEY_ACTIONS (sizeof(kKeyActions) / sizeof(kKeyActions[0]))
|
#define QS_NUM_KEY_ACTIONS (sizeof(kKeyActions) / sizeof(kKeyActions[0]))
|
||||||
|
|
||||||
@@ -393,6 +443,8 @@ typedef struct {
|
|||||||
bool up, down, left, right;
|
bool up, down, left, right;
|
||||||
} XrStickDigital;
|
} XrStickDigital;
|
||||||
static XrStickDigital g_stick_digital[2];
|
static XrStickDigital g_stick_digital[2];
|
||||||
|
// Raw analog stick/trackpad position per hand, for the free-view actions.
|
||||||
|
static float g_stick_xy[2][2];
|
||||||
|
|
||||||
// Rebind capture state (see xr_input_capture_begin()/qs_config.c's own
|
// Rebind capture state (see xr_input_capture_begin()/qs_config.c's own
|
||||||
// comment on the cross-thread request that calls it) - g_capture_action is
|
// comment on the cross-thread request that calls it) - g_capture_action is
|
||||||
@@ -723,6 +775,8 @@ static void xr_input_update_raw_states(void) {
|
|||||||
xrGetActionStateVector2f(g_session, &info, &state);
|
xrGetActionStateVector2f(g_session, &info, &state);
|
||||||
xr_input_digitize_stick(&g_stick_digital[hand], state.isActive, state.currentState.x,
|
xr_input_digitize_stick(&g_stick_digital[hand], state.isActive, state.currentState.x,
|
||||||
state.currentState.y);
|
state.currentState.y);
|
||||||
|
g_stick_xy[hand][0] = state.isActive ? state.currentState.x : 0.0f;
|
||||||
|
g_stick_xy[hand][1] = state.isActive ? state.currentState.y : 0.0f;
|
||||||
}
|
}
|
||||||
g_raw_down[QS_RAW_LEFT_THUMBSTICK_UP] = g_stick_digital[LEFT].up;
|
g_raw_down[QS_RAW_LEFT_THUMBSTICK_UP] = g_stick_digital[LEFT].up;
|
||||||
g_raw_down[QS_RAW_LEFT_THUMBSTICK_DOWN] = g_stick_digital[LEFT].down;
|
g_raw_down[QS_RAW_LEFT_THUMBSTICK_DOWN] = g_stick_digital[LEFT].down;
|
||||||
@@ -768,11 +822,10 @@ void xr_input_reset_player_controls(void) {
|
|||||||
QS_RAW_RIGHT_THUMBSTICK_UP, // stand
|
QS_RAW_RIGHT_THUMBSTICK_UP, // stand
|
||||||
QS_RAW_NONE, // crouch
|
QS_RAW_NONE, // crouch
|
||||||
QS_RAW_RIGHT_THUMBSTICK_DOWN, // prone
|
QS_RAW_RIGHT_THUMBSTICK_DOWN, // prone
|
||||||
QS_RAW_RIGHT_THUMBSTICK_CLICK, // toggle_freelook
|
|
||||||
QS_RAW_NONE, // pause
|
QS_RAW_NONE, // pause
|
||||||
QS_RAW_NONE, // full_view
|
QS_RAW_NONE, // full_view
|
||||||
QS_RAW_NONE, // normal_view
|
QS_RAW_NONE, // normal_view
|
||||||
QS_RAW_NONE, // map_view
|
QS_RAW_RIGHT_THUMBSTICK_CLICK, // map_view
|
||||||
QS_RAW_NONE, // save_game
|
QS_RAW_NONE, // save_game
|
||||||
QS_RAW_NONE, // load_game
|
QS_RAW_NONE, // load_game
|
||||||
QS_RAW_NONE, // select_grenade
|
QS_RAW_NONE, // select_grenade
|
||||||
@@ -801,6 +854,19 @@ void xr_input_reset_player_controls(void) {
|
|||||||
QS_RAW_NONE, // mfd_right_4
|
QS_RAW_NONE, // mfd_right_4
|
||||||
QS_RAW_NONE, // mfd_right_5
|
QS_RAW_NONE, // mfd_right_5
|
||||||
QS_RAW_RIGHT_TRIGGER, // fire_weapon
|
QS_RAW_RIGHT_TRIGGER, // fire_weapon
|
||||||
|
QS_RAW_LEFT_SQUEEZE, // free_left_thumbstick_view
|
||||||
|
QS_RAW_NONE, // free_right_thumbstick_view
|
||||||
|
QS_RAW_RIGHT_THUMBSTICK_UP, // cyber_thrust
|
||||||
|
QS_RAW_LEFT_THUMBSTICK_UP, // cyber_climb
|
||||||
|
QS_RAW_LEFT_THUMBSTICK_DOWN, // cyber_dive
|
||||||
|
QS_RAW_LEFT_THUMBSTICK_LEFT, // cyber_bank_left
|
||||||
|
QS_RAW_LEFT_THUMBSTICK_RIGHT, // cyber_bank_right
|
||||||
|
QS_RAW_RIGHT_THUMBSTICK_LEFT, // cyber_roll_left
|
||||||
|
QS_RAW_RIGHT_THUMBSTICK_RIGHT, // cyber_roll_right
|
||||||
|
QS_RAW_NONE, // cyber_climb_left
|
||||||
|
QS_RAW_NONE, // cyber_climb_right
|
||||||
|
QS_RAW_NONE, // cyber_dive_left
|
||||||
|
QS_RAW_NONE, // cyber_dive_right
|
||||||
};
|
};
|
||||||
memcpy(s_binding, kDefaults, sizeof(s_binding));
|
memcpy(s_binding, kDefaults, sizeof(s_binding));
|
||||||
g_capture_active = false;
|
g_capture_active = false;
|
||||||
@@ -831,6 +897,39 @@ const char *xr_input_get_action_binding_name(int action_index) {
|
|||||||
return xr_input_raw_input_name((QsRawInput)xr_input_get_action_binding(action_index));
|
return xr_input_raw_input_name((QsRawInput)xr_input_get_action_binding(action_index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Where an action applies. NORMAL actions (walking, jumping, posture) are
|
||||||
|
// inactive in cyberspace and CYBER actions only work there, so each set has its
|
||||||
|
// own bindings and one input can be bound in both. Everything else (clicks,
|
||||||
|
// fire, free view, hotkeys) applies everywhere.
|
||||||
|
typedef enum { QS_CONTEXT_ALWAYS, QS_CONTEXT_NORMAL, QS_CONTEXT_CYBER } QsActionContext;
|
||||||
|
|
||||||
|
static QsActionContext xr_input_action_context(QsControlAction action) {
|
||||||
|
if (action >= QS_ACTION_CYBER_THRUST)
|
||||||
|
return QS_CONTEXT_CYBER;
|
||||||
|
switch (action) {
|
||||||
|
case QS_ACTION_MOVE_FORWARD:
|
||||||
|
case QS_ACTION_MOVE_BACK:
|
||||||
|
case QS_ACTION_STRAFE_LEFT:
|
||||||
|
case QS_ACTION_STRAFE_RIGHT:
|
||||||
|
case QS_ACTION_TURN_LEFT:
|
||||||
|
case QS_ACTION_TURN_RIGHT:
|
||||||
|
case QS_ACTION_JUMP:
|
||||||
|
case QS_ACTION_STAND:
|
||||||
|
case QS_ACTION_CROUCH:
|
||||||
|
case QS_ACTION_PRONE:
|
||||||
|
return QS_CONTEXT_NORMAL;
|
||||||
|
default:
|
||||||
|
return QS_CONTEXT_ALWAYS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// True if two actions can be active at the same time, so one input shared by
|
||||||
|
// both would fire both.
|
||||||
|
static bool xr_input_contexts_overlap(QsControlAction a, QsControlAction b) {
|
||||||
|
QsActionContext ca = xr_input_action_context(a), cb = xr_input_action_context(b);
|
||||||
|
return ca == QS_CONTEXT_ALWAYS || cb == QS_CONTEXT_ALWAYS || ca == cb;
|
||||||
|
}
|
||||||
|
|
||||||
// True if an action holding `binding` would still fire from a press of
|
// True if an action holding `binding` would still fire from a press of
|
||||||
// `matched`: the same concrete input, or a symmetric "trigger"/"squeeze"
|
// `matched`: the same concrete input, or a symmetric "trigger"/"squeeze"
|
||||||
// binding when `matched` is that hand's trigger/squeeze.
|
// binding when `matched` is that hand's trigger/squeeze.
|
||||||
@@ -849,9 +948,10 @@ static bool xr_input_binding_conflicts(QsRawInput binding, QsRawInput matched) {
|
|||||||
// xr_input_update_raw_states() and before the key-action dispatch loop, so
|
// xr_input_update_raw_states() and before the key-action dispatch loop, so
|
||||||
// a same-frame press isn't also consumed as this action's normal effect
|
// a same-frame press isn't also consumed as this action's normal effect
|
||||||
// (see xr_input_action_suppressed_by_capture() for the other half of that
|
// (see xr_input_action_suppressed_by_capture() for the other half of that
|
||||||
// guarantee). On a match, enforces exclusivity (at most one action may
|
// guarantee). On a match, enforces exclusivity (at most one action that can
|
||||||
// respond to a given raw input - see xr_input_binding_conflicts() above)
|
// be active together with it - see xr_input_contexts_overlap() - may respond
|
||||||
// by clearing any other action that would otherwise still fire from it,
|
// to a given raw input, see xr_input_binding_conflicts()) by clearing any
|
||||||
|
// other such action that would otherwise still fire from it,
|
||||||
// then reports both results in one call.
|
// then reports both results in one call.
|
||||||
static void xr_input_process_capture(void) {
|
static void xr_input_process_capture(void) {
|
||||||
if (!g_capture_active)
|
if (!g_capture_active)
|
||||||
@@ -867,7 +967,9 @@ static void xr_input_process_capture(void) {
|
|||||||
QsRawInput matched = (QsRawInput)i;
|
QsRawInput matched = (QsRawInput)i;
|
||||||
int displacedAction = -1;
|
int displacedAction = -1;
|
||||||
for (int a = 0; a < QS_ACTION_COUNT; a++) {
|
for (int a = 0; a < QS_ACTION_COUNT; a++) {
|
||||||
if (a != (int)g_capture_action && xr_input_binding_conflicts(s_binding[a], matched)) {
|
if (a != (int)g_capture_action &&
|
||||||
|
xr_input_contexts_overlap((QsControlAction)a, g_capture_action) &&
|
||||||
|
xr_input_binding_conflicts(s_binding[a], matched)) {
|
||||||
s_binding[a] = QS_RAW_NONE;
|
s_binding[a] = QS_RAW_NONE;
|
||||||
displacedAction = a;
|
displacedAction = a;
|
||||||
break; // exclusivity guarantees at most one prior holder
|
break; // exclusivity guarantees at most one prior holder
|
||||||
@@ -1671,6 +1773,162 @@ static bool xr_input_push_key(const QsKeyAction *keyAction, bool down) {
|
|||||||
return SDL_PushEvent(&event) == 1;
|
return SDL_PushEvent(&event) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Free view: while a free_{left,right}_thumbstick_view action's button is
|
||||||
|
// held (and the menu is closed), that hand's stick looks around instead of
|
||||||
|
// driving its normal bindings. It feeds the engine's own mouse look
|
||||||
|
// (mouselook.c): mlook_enabled is forced on for as long as a free view is
|
||||||
|
// held, and the stick adds to MouseChaosX/Y, the extra look velocity that
|
||||||
|
// get_mouselook_vel() (sdl_events.c) consumes once per physics tick.
|
||||||
|
// get_mouselook_vel() returns zero while SDL considers the cursor shown, so
|
||||||
|
// the cursor is kept hidden for the duration.
|
||||||
|
extern int mlook_enabled;
|
||||||
|
extern int qs_in_cyberspace(void); // engine patch 24 (mouselook.c)
|
||||||
|
extern unsigned char game_paused;
|
||||||
|
extern int MouseChaosX, MouseChaosY;
|
||||||
|
|
||||||
|
#define QS_FREE_VIEW_DEADZONE 0.15f
|
||||||
|
#define QS_FREE_VIEW_YAW_PER_SEC 500.0f
|
||||||
|
#define QS_FREE_VIEW_PITCH_PER_SEC 250.0f
|
||||||
|
|
||||||
|
static bool g_free_view_forced = false; // true while we have mlook_enabled forced on
|
||||||
|
static int g_free_view_saved_mlook = 0; // mlook_enabled before that
|
||||||
|
static float g_free_view_acc[2]; // fractional look counts not yet emitted
|
||||||
|
static int g_free_view_saved_cursor = 0; // SDL_ShowCursor() state before it
|
||||||
|
static Uint32 g_free_view_last_ticks = 0;
|
||||||
|
static Uint32 g_free_view_log_ticks = 0;
|
||||||
|
|
||||||
|
static void xr_input_apply_free_view(const bool active[2]) {
|
||||||
|
Uint32 now = SDL_GetTicks();
|
||||||
|
float dt = g_free_view_last_ticks ? (float)(now - g_free_view_last_ticks) / 1000.0f : 0.0f;
|
||||||
|
g_free_view_last_ticks = now;
|
||||||
|
if (dt > 0.1f)
|
||||||
|
dt = 0.1f;
|
||||||
|
|
||||||
|
if (!active[LEFT] && !active[RIGHT]) {
|
||||||
|
if (g_free_view_forced) {
|
||||||
|
mlook_enabled = g_free_view_saved_mlook;
|
||||||
|
SDL_ShowCursor(g_free_view_saved_cursor ? SDL_ENABLE : SDL_DISABLE);
|
||||||
|
g_free_view_forced = false;
|
||||||
|
g_free_view_acc[0] = g_free_view_acc[1] = 0.0f;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!g_free_view_forced) {
|
||||||
|
g_free_view_saved_mlook = mlook_enabled;
|
||||||
|
g_free_view_saved_cursor = SDL_ShowCursor(SDL_QUERY);
|
||||||
|
g_free_view_forced = true;
|
||||||
|
}
|
||||||
|
mlook_enabled = 1;
|
||||||
|
SDL_ShowCursor(SDL_DISABLE);
|
||||||
|
if (game_paused)
|
||||||
|
return;
|
||||||
|
|
||||||
|
float x = 0.0f, y = 0.0f;
|
||||||
|
for (int hand = 0; hand < 2; hand++) {
|
||||||
|
if (active[hand]) {
|
||||||
|
x += g_stick_xy[hand][0];
|
||||||
|
y += g_stick_xy[hand][1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float mag = sqrtf(x * x + y * y);
|
||||||
|
if (mag > QS_FREE_VIEW_DEADZONE) {
|
||||||
|
float scaled = (mag - QS_FREE_VIEW_DEADZONE) / (1.0f - QS_FREE_VIEW_DEADZONE);
|
||||||
|
if (scaled > 1.0f)
|
||||||
|
scaled = 1.0f;
|
||||||
|
// Quadratic response: fine control near the centre, full rate at the edge.
|
||||||
|
float factor = scaled * scaled / mag;
|
||||||
|
x *= factor;
|
||||||
|
y *= factor;
|
||||||
|
} else {
|
||||||
|
x = y = 0.0f;
|
||||||
|
}
|
||||||
|
// Stick right = look right; stick up = look up (mouse motion up is negative y).
|
||||||
|
g_free_view_acc[0] += x * QS_FREE_VIEW_YAW_PER_SEC * dt;
|
||||||
|
g_free_view_acc[1] += -y * QS_FREE_VIEW_PITCH_PER_SEC * dt;
|
||||||
|
int dx = (int)g_free_view_acc[0];
|
||||||
|
int dy = (int)g_free_view_acc[1];
|
||||||
|
g_free_view_acc[0] -= (float)dx;
|
||||||
|
g_free_view_acc[1] -= (float)dy;
|
||||||
|
// Bounded so a held stick can't pile up unconsumed velocity when the
|
||||||
|
// engine isn't running physics (e.g. outside gameplay).
|
||||||
|
if (MouseChaosX > -4096 && MouseChaosX < 4096)
|
||||||
|
MouseChaosX += dx;
|
||||||
|
if (MouseChaosY > -4096 && MouseChaosY < 4096)
|
||||||
|
MouseChaosY += dy;
|
||||||
|
|
||||||
|
if (now - g_free_view_log_ticks > 500) {
|
||||||
|
g_free_view_log_ticks = now;
|
||||||
|
LOGI("XR: free view mlook=%d cursorShown=%d paused=%d stick=(%.2f,%.2f) chaos=(%d,%d)",
|
||||||
|
mlook_enabled, SDL_ShowCursor(SDL_QUERY), (int)game_paused, x, y, MouseChaosX,
|
||||||
|
MouseChaosY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cyberspace pitch and bank are full-strength on/off keys in the engine. For an
|
||||||
|
// action bound to a thumbstick direction, engine patch 25 (movekeys.c) scales
|
||||||
|
// that strength by these globals, set every frame from how far the stick is
|
||||||
|
// pushed: MIN just past the digital threshold up to MAX at full deflection. A
|
||||||
|
// button-bound action keeps full strength.
|
||||||
|
extern float qs_cyber_pitch_scale;
|
||||||
|
extern float qs_cyber_bank_scale;
|
||||||
|
|
||||||
|
#define QS_CYBER_ROTATION_MIN_SCALE 0.2f
|
||||||
|
#define QS_CYBER_ROTATION_MAX_SCALE 0.6f
|
||||||
|
|
||||||
|
// Deflection (0..1) of the stick direction `raw` names, or -1 if `raw` is not a
|
||||||
|
// thumbstick direction. The 8 direction inputs are ordered up/down/left/right
|
||||||
|
// per hand.
|
||||||
|
static float xr_input_stick_direction_deflection(QsRawInput raw) {
|
||||||
|
if (!xr_input_raw_is_thumbstick_direction(raw))
|
||||||
|
return -1.0f;
|
||||||
|
const float *xy = g_stick_xy[xr_input_raw_input_hand(raw)];
|
||||||
|
float d;
|
||||||
|
switch ((raw - QS_RAW_LEFT_THUMBSTICK_UP) % 4) {
|
||||||
|
case 0: d = xy[1]; break;
|
||||||
|
case 1: d = -xy[1]; break;
|
||||||
|
case 2: d = -xy[0]; break;
|
||||||
|
default: d = xy[0]; break;
|
||||||
|
}
|
||||||
|
return d < 0.0f ? 0.0f : (d > 1.0f ? 1.0f : d);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float xr_input_cyber_rotation_scale(QsRawInput raw) {
|
||||||
|
float d = xr_input_stick_direction_deflection(raw);
|
||||||
|
if (d < 0.0f)
|
||||||
|
return 1.0f;
|
||||||
|
float t = (d - 0.5f) / 0.5f;
|
||||||
|
t = t < 0.0f ? 0.0f : (t > 1.0f ? 1.0f : t);
|
||||||
|
return QS_CYBER_ROTATION_MIN_SCALE + (QS_CYBER_ROTATION_MAX_SCALE - QS_CYBER_ROTATION_MIN_SCALE) * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the two globals from the cyberspace pitch/bank actions currently held.
|
||||||
|
static void xr_input_update_cyber_scales(const bool wantDown[]) {
|
||||||
|
float pitchScale = 1.0f, bankScale = 1.0f;
|
||||||
|
bool havePitch = false, haveBank = false;
|
||||||
|
for (size_t i = 0; i < QS_NUM_KEY_ACTIONS; i++) {
|
||||||
|
QsControlAction a = kKeyActions[i].action;
|
||||||
|
bool pitch = a == QS_ACTION_CYBER_CLIMB || a == QS_ACTION_CYBER_DIVE ||
|
||||||
|
a == QS_ACTION_CYBER_CLIMB_LEFT || a == QS_ACTION_CYBER_CLIMB_RIGHT ||
|
||||||
|
a == QS_ACTION_CYBER_DIVE_LEFT || a == QS_ACTION_CYBER_DIVE_RIGHT;
|
||||||
|
bool bank = a == QS_ACTION_CYBER_BANK_LEFT || a == QS_ACTION_CYBER_BANK_RIGHT ||
|
||||||
|
a == QS_ACTION_CYBER_CLIMB_LEFT || a == QS_ACTION_CYBER_CLIMB_RIGHT ||
|
||||||
|
a == QS_ACTION_CYBER_DIVE_LEFT || a == QS_ACTION_CYBER_DIVE_RIGHT;
|
||||||
|
if (!wantDown[i] || (!pitch && !bank))
|
||||||
|
continue;
|
||||||
|
float scale = xr_input_cyber_rotation_scale(s_binding[a]);
|
||||||
|
if (pitch) {
|
||||||
|
pitchScale = havePitch ? fmaxf(pitchScale, scale) : scale;
|
||||||
|
havePitch = true;
|
||||||
|
}
|
||||||
|
if (bank) {
|
||||||
|
bankScale = haveBank ? fmaxf(bankScale, scale) : scale;
|
||||||
|
haveBank = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qs_cyber_pitch_scale = pitchScale;
|
||||||
|
qs_cyber_bank_scale = bankScale;
|
||||||
|
}
|
||||||
|
|
||||||
void xr_input_sync_and_draw(XrSpace baseSpace, XrTime time, bool draw) {
|
void xr_input_sync_and_draw(XrSpace baseSpace, XrTime time, bool draw) {
|
||||||
if (g_action_set == XR_NULL_HANDLE)
|
if (g_action_set == XR_NULL_HANDLE)
|
||||||
return;
|
return;
|
||||||
@@ -1718,13 +1976,32 @@ void xr_input_sync_and_draw(XrSpace baseSpace, XrTime time, bool draw) {
|
|||||||
// ray-dispatch loop below.
|
// ray-dispatch loop below.
|
||||||
xr_input_update_raw_states();
|
xr_input_update_raw_states();
|
||||||
xr_input_process_capture();
|
xr_input_process_capture();
|
||||||
|
// A free-view action's button held: that hand's stick looks around and
|
||||||
|
// its own direction bindings are suspended (see xr_input_apply_free_view()).
|
||||||
|
bool freeView[2];
|
||||||
|
for (int hand = 0; hand < 2; hand++) {
|
||||||
|
QsControlAction viewAction =
|
||||||
|
hand == LEFT ? QS_ACTION_FREE_LEFT_THUMBSTICK_VIEW : QS_ACTION_FREE_RIGHT_THUMBSTICK_VIEW;
|
||||||
|
QsRawInput viewRaw = s_binding[viewAction];
|
||||||
|
freeView[hand] = viewRaw < QS_RAW_CONCRETE_COUNT && !menuVisible &&
|
||||||
|
!xr_input_action_suppressed_by_capture(viewAction) && g_raw_down[viewRaw];
|
||||||
|
}
|
||||||
|
bool inCyber = qs_in_cyberspace() != 0;
|
||||||
bool wantDown[QS_NUM_KEY_ACTIONS];
|
bool wantDown[QS_NUM_KEY_ACTIONS];
|
||||||
for (size_t i = 0; i < QS_NUM_KEY_ACTIONS; i++) {
|
for (size_t i = 0; i < QS_NUM_KEY_ACTIONS; i++) {
|
||||||
const QsKeyAction *keyAction = &kKeyActions[i];
|
const QsKeyAction *keyAction = &kKeyActions[i];
|
||||||
QsRawInput raw = s_binding[keyAction->action];
|
QsRawInput raw = s_binding[keyAction->action];
|
||||||
wantDown[i] = raw != QS_RAW_NONE && !xr_input_action_suppressed_by_capture(keyAction->action) &&
|
QsActionContext context = xr_input_action_context(keyAction->action);
|
||||||
!(xr_input_raw_is_thumbstick_direction(raw) && menuVisible) && g_raw_down[raw];
|
bool contextActive = context == QS_CONTEXT_ALWAYS ||
|
||||||
|
(context == QS_CONTEXT_CYBER) == inCyber;
|
||||||
|
wantDown[i] = raw != QS_RAW_NONE && contextActive &&
|
||||||
|
!xr_input_action_suppressed_by_capture(keyAction->action) &&
|
||||||
|
!(xr_input_raw_is_thumbstick_direction(raw) &&
|
||||||
|
(menuVisible || freeView[xr_input_raw_input_hand(raw)])) &&
|
||||||
|
g_raw_down[raw];
|
||||||
}
|
}
|
||||||
|
xr_input_update_cyber_scales(wantDown);
|
||||||
|
xr_input_apply_free_view(freeView);
|
||||||
for (size_t i = 0; i < QS_NUM_KEY_ACTIONS; i++) {
|
for (size_t i = 0; i < QS_NUM_KEY_ACTIONS; i++) {
|
||||||
const QsKeyAction *keyAction = &kKeyActions[i];
|
const QsKeyAction *keyAction = &kKeyActions[i];
|
||||||
bool down = wantDown[i];
|
bool down = wantDown[i];
|
||||||
@@ -1781,6 +2058,10 @@ void xr_input_sync_and_draw(XrSpace baseSpace, XrTime time, bool draw) {
|
|||||||
} kWatchedKeys[] = {
|
} kWatchedKeys[] = {
|
||||||
{SDLK_UP, 0x7E}, {SDLK_DOWN, 0x7D}, {SDLK_LEFT, 0x7B}, {SDLK_RIGHT, 0x7C}, {SDLK_SPACE, 0x31},
|
{SDLK_UP, 0x7E}, {SDLK_DOWN, 0x7D}, {SDLK_LEFT, 0x7B}, {SDLK_RIGHT, 0x7C}, {SDLK_SPACE, 0x31},
|
||||||
{SDLK_RETURN, 0x24},
|
{SDLK_RETURN, 0x24},
|
||||||
|
// Cyberspace movement keys (letters and keypad, movekeys.h's CODE_*).
|
||||||
|
{SDLK_w, 0x0D}, {SDLK_a, 0x00}, {SDLK_s, 0x01}, {SDLK_d, 0x02}, {SDLK_q, 0x0C},
|
||||||
|
{SDLK_e, 0x0E}, {SDLK_x, 0x07}, {SDLK_KP_7, 0x59}, {SDLK_KP_9, 0x5C}, {SDLK_KP_1, 0x53},
|
||||||
|
{SDLK_KP_3, 0x55},
|
||||||
};
|
};
|
||||||
static Uint32 s_stuck_since[sizeof(kWatchedKeys) / sizeof(kWatchedKeys[0])];
|
static Uint32 s_stuck_since[sizeof(kWatchedKeys) / sizeof(kWatchedKeys[0])];
|
||||||
for (size_t w = 0; w < sizeof(kWatchedKeys) / sizeof(kWatchedKeys[0]); w++) {
|
for (size_t w = 0; w < sizeof(kWatchedKeys) / sizeof(kWatchedKeys[0]); w++) {
|
||||||
@@ -2096,6 +2377,8 @@ void xr_input_sync_and_draw(XrSpace baseSpace, XrTime time, bool draw) {
|
|||||||
py = 0;
|
py = 0;
|
||||||
else if (py >= gameHeight)
|
else if (py >= gameHeight)
|
||||||
py = gameHeight - 1;
|
py = gameHeight - 1;
|
||||||
|
// Mouse look keeps the engine's cursor centred; don't fight it.
|
||||||
|
if (!freeView[LEFT] && !freeView[RIGHT])
|
||||||
xr_mouse_move(px, py);
|
xr_mouse_move(px, py);
|
||||||
}
|
}
|
||||||
if (selectDownEdge && gameHit) {
|
if (selectDownEdge && gameHit) {
|
||||||
@@ -2144,6 +2427,11 @@ bool xr_input_get_beam_layer(int hand, XrCompositionLayerQuad *out_quad) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void xr_input_shutdown(void) {
|
void xr_input_shutdown(void) {
|
||||||
|
if (g_free_view_forced) {
|
||||||
|
mlook_enabled = g_free_view_saved_mlook;
|
||||||
|
SDL_ShowCursor(g_free_view_saved_cursor ? SDL_ENABLE : SDL_DISABLE);
|
||||||
|
g_free_view_forced = false;
|
||||||
|
}
|
||||||
// Clearing g_key_action_prev below without releasing what it says is
|
// Clearing g_key_action_prev below without releasing what it says is
|
||||||
// held would leave the engine's own held-key state stuck on (nothing
|
// held would leave the engine's own held-key state stuck on (nothing
|
||||||
// else ever sends that key's release).
|
// else ever sends that key's release).
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ public class MenuOverlay extends OverlayPanel {
|
|||||||
private static final String[] ACTION_KEYS = {
|
private static final String[] ACTION_KEYS = {
|
||||||
"move_forward", "move_back", "strafe_left", "strafe_right",
|
"move_forward", "move_back", "strafe_left", "strafe_right",
|
||||||
"turn_left", "turn_right", "jump", "mouse_click", "mouse_double_click",
|
"turn_left", "turn_right", "jump", "mouse_click", "mouse_double_click",
|
||||||
"stand", "crouch", "prone", "toggle_freelook", "pause",
|
"stand", "crouch", "prone", "pause",
|
||||||
"full_view", "normal_view", "map_view", "save_game", "load_game",
|
"full_view", "normal_view", "map_view", "save_game", "load_game",
|
||||||
"select_grenade", "arm_grenade", "select_drug", "use_drug",
|
"select_grenade", "arm_grenade", "select_drug", "use_drug",
|
||||||
"toggle_online_help", "show_help", "toggle_music", "quit",
|
"toggle_online_help", "show_help", "toggle_music", "quit",
|
||||||
@@ -112,7 +112,14 @@ public class MenuOverlay extends OverlayPanel {
|
|||||||
"clear_fullscreen",
|
"clear_fullscreen",
|
||||||
"mfd_left_1", "mfd_left_2", "mfd_left_3", "mfd_left_4", "mfd_left_5",
|
"mfd_left_1", "mfd_left_2", "mfd_left_3", "mfd_left_4", "mfd_left_5",
|
||||||
"mfd_right_1", "mfd_right_2", "mfd_right_3", "mfd_right_4", "mfd_right_5",
|
"mfd_right_1", "mfd_right_2", "mfd_right_3", "mfd_right_4", "mfd_right_5",
|
||||||
"fire_weapon",
|
"fire_weapon", "free_left_thumbstick_view", "free_right_thumbstick_view",
|
||||||
|
"cyber_thrust", "cyber_climb", "cyber_dive", "cyber_bank_left", "cyber_bank_right",
|
||||||
|
"cyber_roll_left", "cyber_roll_right", "cyber_climb_left", "cyber_climb_right",
|
||||||
|
"cyber_dive_left", "cyber_dive_right",
|
||||||
|
};
|
||||||
|
// Shown first in the Controller Options list, in this order.
|
||||||
|
private static final String[] FIRST_ROW_ACTIONS = {
|
||||||
|
"fire_weapon", "free_left_thumbstick_view", "free_right_thumbstick_view",
|
||||||
};
|
};
|
||||||
private static final String CONTROLLER_PROFILES_DIR = QuestShockActivity.GAME_DIR + "/controller_profiles";
|
private static final String CONTROLLER_PROFILES_DIR = QuestShockActivity.GAME_DIR + "/controller_profiles";
|
||||||
// Shown (with its own explanatory note - see Strings.java's
|
// Shown (with its own explanatory note - see Strings.java's
|
||||||
@@ -599,12 +606,21 @@ public class MenuOverlay extends OverlayPanel {
|
|||||||
controlRows = new LinearLayout[ACTION_KEYS.length];
|
controlRows = new LinearLayout[ACTION_KEYS.length];
|
||||||
controlActionLabels = new TextView[ACTION_KEYS.length];
|
controlActionLabels = new TextView[ACTION_KEYS.length];
|
||||||
controlBindingButtons = new Button[ACTION_KEYS.length];
|
controlBindingButtons = new Button[ACTION_KEYS.length];
|
||||||
// Rows are created in display order (Fire Weapon first, then the
|
// Rows are created in display order (FIRST_ROW_ACTIONS first, then
|
||||||
// rest in ACTION_KEYS order); every array below stays indexed by
|
// the rest in ACTION_KEYS order); every array below stays indexed by
|
||||||
// the real action index, which must keep matching xr_input.c.
|
// the real action index, which must keep matching xr_input.c.
|
||||||
int fireIndex = Arrays.asList(ACTION_KEYS).indexOf("fire_weapon");
|
int[] displayOrder = new int[ACTION_KEYS.length];
|
||||||
|
int filled = 0;
|
||||||
|
for (String key : FIRST_ROW_ACTIONS) {
|
||||||
|
displayOrder[filled++] = Arrays.asList(ACTION_KEYS).indexOf(key);
|
||||||
|
}
|
||||||
|
for (int k = 0; k < ACTION_KEYS.length; k++) {
|
||||||
|
if (!Arrays.asList(FIRST_ROW_ACTIONS).contains(ACTION_KEYS[k])) {
|
||||||
|
displayOrder[filled++] = k;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (int d = 0; d < ACTION_KEYS.length; d++) {
|
for (int d = 0; d < ACTION_KEYS.length; d++) {
|
||||||
final int i = d == 0 ? fireIndex : (d - 1 < fireIndex ? d - 1 : d);
|
final int i = displayOrder[d];
|
||||||
final int actionIndex = i;
|
final int actionIndex = i;
|
||||||
LinearLayout row = new LinearLayout(activity);
|
LinearLayout row = new LinearLayout(activity);
|
||||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
|||||||
@@ -79,8 +79,6 @@ final class Strings {
|
|||||||
put("action_stand", "Stand", "Debout", "Stehen");
|
put("action_stand", "Stand", "Debout", "Stehen");
|
||||||
put("action_crouch", "Crouch", "Accroupi", "Ducken");
|
put("action_crouch", "Crouch", "Accroupi", "Ducken");
|
||||||
put("action_prone", "Prone", "Couché", "Liegen");
|
put("action_prone", "Prone", "Couché", "Liegen");
|
||||||
put("action_toggle_freelook", "Toggle Free Look", "Basculer la vue libre",
|
|
||||||
"Freies Umsehen umschalten");
|
|
||||||
put("action_pause", "Pause Game", "Mettre en pause", "Spiel pausieren");
|
put("action_pause", "Pause Game", "Mettre en pause", "Spiel pausieren");
|
||||||
put("action_full_view", "Fullscreen View", "Vue plein écran", "Vollbildansicht");
|
put("action_full_view", "Fullscreen View", "Vue plein écran", "Vollbildansicht");
|
||||||
put("action_normal_view", "Normal View", "Vue normale", "Normale Ansicht");
|
put("action_normal_view", "Normal View", "Vue normale", "Normale Ansicht");
|
||||||
@@ -121,6 +119,34 @@ final class Strings {
|
|||||||
put("action_mfd_right_4", "Right MFD: Target", "MFD droit : cible", "Rechtes MFD: Ziel");
|
put("action_mfd_right_4", "Right MFD: Target", "MFD droit : cible", "Rechtes MFD: Ziel");
|
||||||
put("action_mfd_right_5", "Right MFD: Info", "MFD droit : info", "Rechtes MFD: Info");
|
put("action_mfd_right_5", "Right MFD: Info", "MFD droit : info", "Rechtes MFD: Info");
|
||||||
put("action_fire_weapon", "Fire Weapon", "Tirer", "Waffe abfeuern");
|
put("action_fire_weapon", "Fire Weapon", "Tirer", "Waffe abfeuern");
|
||||||
|
put("action_free_left_thumbstick_view", "Free View (Left Stick)",
|
||||||
|
"Vue libre (stick gauche)", "Freie Ansicht (linker Stick)");
|
||||||
|
put("action_free_right_thumbstick_view", "Free View (Right Stick)",
|
||||||
|
"Vue libre (stick droit)", "Freie Ansicht (rechter Stick)");
|
||||||
|
|
||||||
|
// Cyberspace actions (active only in cyberspace, see xr_input.c).
|
||||||
|
put("action_cyber_thrust", "Cyberspace: Thrust", "Cyberespace : poussée",
|
||||||
|
"Cyberspace: Schub");
|
||||||
|
put("action_cyber_climb", "Cyberspace: Climb", "Cyberespace : monter",
|
||||||
|
"Cyberspace: Steigen");
|
||||||
|
put("action_cyber_dive", "Cyberspace: Dive", "Cyberespace : plonger",
|
||||||
|
"Cyberspace: Sinken");
|
||||||
|
put("action_cyber_bank_left", "Cyberspace: Bank Left", "Cyberespace : virage à gauche",
|
||||||
|
"Cyberspace: Links kippen");
|
||||||
|
put("action_cyber_bank_right", "Cyberspace: Bank Right", "Cyberespace : virage à droite",
|
||||||
|
"Cyberspace: Rechts kippen");
|
||||||
|
put("action_cyber_roll_left", "Cyberspace: Roll Left", "Cyberespace : roulis gauche",
|
||||||
|
"Cyberspace: Links rollen");
|
||||||
|
put("action_cyber_roll_right", "Cyberspace: Roll Right", "Cyberespace : roulis droit",
|
||||||
|
"Cyberspace: Rechts rollen");
|
||||||
|
put("action_cyber_climb_left", "Cyberspace: Climb Left", "Cyberespace : monter à gauche",
|
||||||
|
"Cyberspace: Links steigen");
|
||||||
|
put("action_cyber_climb_right", "Cyberspace: Climb Right", "Cyberespace : monter à droite",
|
||||||
|
"Cyberspace: Rechts steigen");
|
||||||
|
put("action_cyber_dive_left", "Cyberspace: Dive Left", "Cyberespace : plonger à gauche",
|
||||||
|
"Cyberspace: Links sinken");
|
||||||
|
put("action_cyber_dive_right", "Cyberspace: Dive Right", "Cyberespace : plonger à droite",
|
||||||
|
"Cyberspace: Rechts sinken");
|
||||||
put("capture_prompt", "Press a button…", "Appuyez sur un bouton…", "Taste drücken…");
|
put("capture_prompt", "Press a button…", "Appuyez sur un bouton…", "Taste drücken…");
|
||||||
put("reset_to_defaults", "Reset to Defaults", "Réinitialiser", "Auf Standard zurücksetzen");
|
put("reset_to_defaults", "Reset to Defaults", "Réinitialiser", "Auf Standard zurücksetzen");
|
||||||
put("generic_profile_note",
|
put("generic_profile_note",
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
--- a/src/GameSrc/mouselook.c
|
||||||
|
+++ b/src/GameSrc/mouselook.c
|
||||||
|
@@ -121,3 +121,10 @@
|
||||||
|
get_mouselook_vel(&mvelx, &mvely);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+#ifdef __ANDROID__
|
||||||
|
+// Whether the player is currently in cyberspace; read by the Android
|
||||||
|
+// controller input code (android/app/src/main/cpp/xr_input.c) to switch
|
||||||
|
+// between the normal and cyberspace action sets.
|
||||||
|
+int qs_in_cyberspace(void) { return global_fullmap != NULL && global_fullmap->cyber; }
|
||||||
|
+#endif
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
--- a/src/GameSrc/movekeys.c
|
||||||
|
+++ b/src/GameSrc/movekeys.c
|
||||||
|
@@ -44,6 +44,12 @@
|
||||||
|
extern bool gKeypadOverride;
|
||||||
|
|
||||||
|
uchar parse_motion_key(ushort code, short *cnum, short *cval);
|
||||||
|
+#ifdef __ANDROID__
|
||||||
|
+// Set every frame by android/app/src/main/cpp/xr_input.c.
|
||||||
|
+float qs_cyber_pitch_scale = 1.0f;
|
||||||
|
+float qs_cyber_bank_scale = 1.0f;
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
uchar parse_motion_key_cyber(ushort code, short *cnum, short *cval);
|
||||||
|
void init_motion_polling(void);
|
||||||
|
|
||||||
|
@@ -173,6 +179,20 @@
|
||||||
|
*cnum = -1;
|
||||||
|
*cval = 0;
|
||||||
|
|
||||||
|
+#ifdef __ANDROID__
|
||||||
|
+ // Pitch and bank strength, scaled by the controller thumbstick's
|
||||||
|
+ // deflection (see android/app/src/main/cpp/xr_input.c); 1.0 for a key.
|
||||||
|
+ short pitchMax = (short)(CONTROL_MAX_VAL * qs_cyber_pitch_scale + 0.5f);
|
||||||
|
+ short bankMax = (short)(CONTROL_MAX_VAL * qs_cyber_bank_scale + 0.5f);
|
||||||
|
+ if (pitchMax < 1)
|
||||||
|
+ pitchMax = 1;
|
||||||
|
+ if (bankMax < 1)
|
||||||
|
+ bankMax = 1;
|
||||||
|
+#else
|
||||||
|
+ short pitchMax = CONTROL_MAX_VAL;
|
||||||
|
+ short bankMax = CONTROL_MAX_VAL;
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
while (MoveCyberKeybinds[i].code != 255) {
|
||||||
|
if (code == MoveCyberKeybinds[i].code) {
|
||||||
|
move = MoveCyberKeybinds[i].move;
|
||||||
|
@@ -189,22 +209,22 @@
|
||||||
|
|
||||||
|
case M_CLIMB:
|
||||||
|
*cnum = CONTROL_YVEL;
|
||||||
|
- *cval = -CONTROL_MAX_VAL;
|
||||||
|
+ *cval = -pitchMax;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case M_BANKLEFT:
|
||||||
|
*cnum = CONTROL_XYROT;
|
||||||
|
- *cval = -CONTROL_MAX_VAL;
|
||||||
|
+ *cval = -bankMax;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case M_BANKRIGHT:
|
||||||
|
*cnum = CONTROL_XYROT;
|
||||||
|
- *cval = CONTROL_MAX_VAL;
|
||||||
|
+ *cval = bankMax;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case M_DIVE:
|
||||||
|
*cnum = CONTROL_YVEL;
|
||||||
|
- *cval = CONTROL_MAX_VAL;
|
||||||
|
+ *cval = pitchMax;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case M_ROLLRIGHT:
|
||||||
|
@@ -219,38 +239,38 @@
|
||||||
|
|
||||||
|
case M_CLIMBLEFT:
|
||||||
|
*cnum = CONTROL_YVEL;
|
||||||
|
- *cval = -CONTROL_MAX_VAL;
|
||||||
|
+ *cval = -pitchMax;
|
||||||
|
if (abs(poll_controls[*cnum]) < abs(*cval))
|
||||||
|
poll_controls[*cnum] = *cval;
|
||||||
|
*cnum = CONTROL_XYROT;
|
||||||
|
- *cval = -CONTROL_MAX_VAL;
|
||||||
|
+ *cval = -bankMax;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case M_CLIMBRIGHT:
|
||||||
|
*cnum = CONTROL_YVEL;
|
||||||
|
- *cval = -CONTROL_MAX_VAL;
|
||||||
|
+ *cval = -pitchMax;
|
||||||
|
if (abs(poll_controls[*cnum]) < abs(*cval))
|
||||||
|
poll_controls[*cnum] = *cval;
|
||||||
|
*cnum = CONTROL_XYROT;
|
||||||
|
- *cval = CONTROL_MAX_VAL;
|
||||||
|
+ *cval = bankMax;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case M_DIVERIGHT:
|
||||||
|
*cnum = CONTROL_YVEL;
|
||||||
|
- *cval = CONTROL_MAX_VAL;
|
||||||
|
+ *cval = pitchMax;
|
||||||
|
if (abs(poll_controls[*cnum]) < abs(*cval))
|
||||||
|
poll_controls[*cnum] = *cval;
|
||||||
|
*cnum = CONTROL_XYROT;
|
||||||
|
- *cval = CONTROL_MAX_VAL;
|
||||||
|
+ *cval = bankMax;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case M_DIVELEFT:
|
||||||
|
*cnum = CONTROL_YVEL;
|
||||||
|
- *cval = CONTROL_MAX_VAL;
|
||||||
|
+ *cval = pitchMax;
|
||||||
|
if (abs(poll_controls[*cnum]) < abs(*cval))
|
||||||
|
poll_controls[*cnum] = *cval;
|
||||||
|
*cnum = CONTROL_XYROT;
|
||||||
|
- *cval = -CONTROL_MAX_VAL;
|
||||||
|
+ *cval = -bankMax;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Unhandled movement. What I gonna do?
|
||||||
Reference in New Issue
Block a user