SRB2/src/g_input.c
Eidolon 696e2ab909 Merge branch 'gamepad-refactor' into next
Some hacks from the snake minigame patch have been removed as they are
no longer needed.

See STJr/SRB2!1870

# Conflicts:
#	src/g_game.c
#	src/g_input.c
2022-12-08 20:00:41 -06:00

1723 lines
47 KiB
C

// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2022 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file g_input.c
/// \brief handle mouse/keyboard/gamepad inputs,
/// maps inputs to game controls (forward, spin, jump...)
#include "doomdef.h"
#include "doomstat.h"
#include "g_game.h"
#include "g_input.h"
#include "i_gamepad.h"
#include "keys.h"
#include "hu_stuff.h" // need HUFONT start & end
#include "d_net.h"
#include "console.h"
#define MAXMOUSESENSITIVITY 100 // sensitivity steps
static CV_PossibleValue_t mousesens_cons_t[] = {{1, "MIN"}, {MAXMOUSESENSITIVITY, "MAX"}, {0, NULL}};
static CV_PossibleValue_t onecontrolperkey_cons_t[] = {{1, "One"}, {2, "Several"}, {0, NULL}};
// mouse values are used once
consvar_t cv_mousesens = CVAR_INIT ("mousesens", "20", CV_SAVE, mousesens_cons_t, NULL);
consvar_t cv_mousesens2 = CVAR_INIT ("mousesens2", "20", CV_SAVE, mousesens_cons_t, NULL);
consvar_t cv_mouseysens = CVAR_INIT ("mouseysens", "20", CV_SAVE, mousesens_cons_t, NULL);
consvar_t cv_mouseysens2 = CVAR_INIT ("mouseysens2", "20", CV_SAVE, mousesens_cons_t, NULL);
consvar_t cv_controlperkey = CVAR_INIT ("controlperkey", "One", CV_SAVE, onecontrolperkey_cons_t, NULL);
mouse_t mouse;
mouse_t mouse2;
gamepad_t gamepads[NUM_GAMEPADS];
// current state of the keys: true if pushed
UINT8 gamekeydown[NUMINPUTS];
// two key codes (or virtual key) per game control
INT32 gamecontrol[NUM_GAMECONTROLS][2];
INT32 gamecontrolbis[NUM_GAMECONTROLS][2]; // secondary splitscreen player
INT32 gamecontroldefault[num_gamecontrolschemes][NUM_GAMECONTROLS][2]; // default control storage, use 0 (gcs_custom) for memory retention
INT32 gamecontrolbisdefault[num_gamecontrolschemes][NUM_GAMECONTROLS][2];
// lists of GC codes for selective operation
const INT32 gcl_tutorial_check[num_gcl_tutorial_check] = {
GC_FORWARD, GC_BACKWARD, GC_STRAFELEFT, GC_STRAFERIGHT,
GC_TURNLEFT, GC_TURNRIGHT
};
const INT32 gcl_tutorial_used[num_gcl_tutorial_used] = {
GC_FORWARD, GC_BACKWARD, GC_STRAFELEFT, GC_STRAFERIGHT,
GC_TURNLEFT, GC_TURNRIGHT,
GC_JUMP, GC_SPIN
};
const INT32 gcl_tutorial_full[num_gcl_tutorial_full] = {
GC_FORWARD, GC_BACKWARD, GC_STRAFELEFT, GC_STRAFERIGHT,
GC_LOOKUP, GC_LOOKDOWN, GC_TURNLEFT, GC_TURNRIGHT, GC_CENTERVIEW,
GC_JUMP, GC_SPIN,
GC_FIRE, GC_FIRENORMAL
};
const INT32 gcl_movement[num_gcl_movement] = {
GC_FORWARD, GC_BACKWARD, GC_STRAFELEFT, GC_STRAFERIGHT
};
const INT32 gcl_camera[num_gcl_camera] = {
GC_TURNLEFT, GC_TURNRIGHT
};
const INT32 gcl_movement_camera[num_gcl_movement_camera] = {
GC_FORWARD, GC_BACKWARD, GC_STRAFELEFT, GC_STRAFERIGHT,
GC_TURNLEFT, GC_TURNRIGHT
};
const INT32 gcl_jump[num_gcl_jump] = { GC_JUMP };
const INT32 gcl_spin[num_gcl_spin] = { GC_SPIN };
const INT32 gcl_jump_spin[num_gcl_jump_spin] = {
GC_JUMP, GC_SPIN
};
static boolean CheckInputDown(UINT8 which, gamecontrols_e gc, boolean checkaxes)
{
INT32 (*controls)[2] = which == 0 ? gamecontrol : gamecontrolbis;
for (unsigned i = 0; i < 2; i++)
{
INT32 key = controls[gc][i];
if (key >= KEY_GAMEPAD && key < KEY_AXES)
{
if (gamepads[which].buttons[key - KEY_GAMEPAD])
return true;
}
else if (checkaxes && (key >= KEY_AXES && key < KEY_INV_AXES + NUM_GAMEPAD_AXES))
{
const UINT16 jdeadzone = G_GetGamepadDigitalDeadZone(which);
const INT16 value = G_GetGamepadAxisValue(which, (key - KEY_AXES) % NUM_GAMEPAD_AXES);
if (abs(value) > jdeadzone)
return true;
}
else if (gamekeydown[key])
return true;
}
return false;
}
boolean G_PlayerInputDown(UINT8 which, gamecontrols_e gc)
{
return CheckInputDown(which, gc, true);
}
boolean G_CheckDigitalPlayerInput(UINT8 which, gamecontrols_e gc)
{
return CheckInputDown(which, gc, false);
}
SINT8 G_PlayerInputIsAnalog(UINT8 which, gamecontrols_e gc, UINT8 settings)
{
INT32 (*controls)[2] = which == 0 ? gamecontrol : gamecontrolbis;
INT32 key = controls[gc][settings];
if (key >= KEY_AXES && key < KEY_AXES + NUM_GAMEPAD_AXES)
return 1;
else if (key >= KEY_INV_AXES && key < KEY_INV_AXES + NUM_GAMEPAD_AXES)
return -1;
return 0;
}
INT16 G_GetAnalogPlayerInput(UINT8 which, gamecontrols_e gc, UINT8 settings)
{
INT32 (*controls)[2] = which == 0 ? gamecontrol : gamecontrolbis;
INT32 key = controls[gc][settings];
if (key >= KEY_AXES && key < KEY_INV_AXES + NUM_GAMEPAD_AXES)
return G_GetGamepadAxisValue(which, (key - KEY_AXES) % NUM_GAMEPAD_AXES);
return 0;
}
typedef struct
{
UINT8 time;
UINT8 state;
UINT8 clicks;
} dclick_t;
static dclick_t mousedclicks[MOUSEBUTTONS];
static dclick_t mouse2dclicks[MOUSEBUTTONS];
//
// General double-click detection routine for any kind of input.
//
static UINT8 G_CheckDoubleClick(UINT8 state, dclick_t *dt)
{
if (state != dt->state && dt->time > 1)
{
dt->state = state;
if (state)
dt->clicks++;
if (dt->clicks == 2)
{
dt->clicks = 0;
return true;
}
else
dt->time = 0;
}
else
{
dt->time++;
if (dt->time > 20)
{
dt->clicks = 0;
dt->state = 0;
}
}
return false;
}
//
// Remaps the inputs to game controls.
//
// A game control can be triggered by one or more keys/buttons.
//
// Each key/mouse button/gamepad button triggers ONLY ONE game control.
//
void G_MapEventsToControls(event_t *ev)
{
INT32 i;
UINT8 flag;
switch (ev->type)
{
case ev_keydown:
if (ev->key < NUMINPUTS)
gamekeydown[ev->key] = 1;
#ifdef PARANOIA
else
CONS_Debug(DBG_GAMELOGIC, "Bad downkey input %d\n",ev->key);
#endif
break;
case ev_keyup:
if (ev->key < NUMINPUTS)
gamekeydown[ev->key] = 0;
#ifdef PARANOIA
else
CONS_Debug(DBG_GAMELOGIC, "Bad upkey input %d\n",ev->key);
#endif
break;
case ev_gamepad_down:
case ev_gamepad_up:
#ifdef PARANOIA
if (ev->which < NUM_GAMEPADS)
#endif
gamepads[ev->which].buttons[ev->key] = ev->type == ev_gamepad_down ? 1 : 0;
break;
case ev_gamepad_axis:
#ifdef PARANOIA
if (ev->which < NUM_GAMEPADS)
#endif
gamepads[ev->which].axes[ev->key] = ev->x;
break;
case ev_mouse:
mouse.rdx = ev->x;
mouse.rdy = ev->y;
break;
case ev_mouse2:
mouse2.rdx = ev->x;
mouse2.rdy = ev->y;
break;
default:
break;
}
// ALWAYS check for mouse double-clicks even if there were no such events
for (i = 0; i < MOUSEBUTTONS; i++)
{
flag = G_CheckDoubleClick(gamekeydown[KEY_MOUSE1+i], &mousedclicks[i]);
gamekeydown[KEY_DBLMOUSE1+i] = flag;
}
for (i = 0; i < MOUSEBUTTONS; i++)
{
flag = G_CheckDoubleClick(gamekeydown[KEY_2MOUSE1+i], &mouse2dclicks[i]);
gamekeydown[KEY_DBL2MOUSE1+i] = flag;
}
}
const char *const gamepad_button_names[NUM_GAMEPAD_BUTTONS + 1] = {
"a",
"b",
"x",
"y",
"back",
"guide",
"start",
"left-stick",
"right-stick",
"left-shoulder",
"right-shoulder",
"dpad-up",
"dpad-down",
"dpad-left",
"dpad-right",
"misc1",
"paddle1",
"paddle2",
"paddle3",
"paddle4",
"touchpad",
NULL};
const char *const gamepad_axis_names[NUM_GAMEPAD_AXES + 1] = {
"left-x",
"left-y",
"right-x",
"right-y",
"trigger-left",
"trigger-right",
NULL};
boolean G_GamepadTypeIsXbox(gamepadtype_e type)
{
switch (type)
{
case GAMEPAD_TYPE_XBOX360:
case GAMEPAD_TYPE_XBOXONE:
case GAMEPAD_TYPE_XBOX_SERIES_XS:
case GAMEPAD_TYPE_XBOX_ELITE:
return true;
default:
return false;
}
}
boolean G_GamepadTypeIsPlayStation(gamepadtype_e type)
{
switch (type)
{
case GAMEPAD_TYPE_PS3:
case GAMEPAD_TYPE_PS4:
case GAMEPAD_TYPE_PS5:
return true;
default:
return false;
}
}
boolean G_GamepadTypeIsNintendoSwitch(gamepadtype_e type)
{
switch (type)
{
case GAMEPAD_TYPE_NINTENDO_SWITCH_PRO:
case GAMEPAD_TYPE_NINTENDO_SWITCH_JOY_CON_GRIP:
return true;
default:
return G_GamepadTypeIsJoyCon(type);
}
}
boolean G_GamepadTypeIsJoyCon(gamepadtype_e type)
{
switch (type)
{
case GAMEPAD_TYPE_NINTENDO_SWITCH_JOY_CON_LEFT:
case GAMEPAD_TYPE_NINTENDO_SWITCH_JOY_CON_RIGHT:
return true;
default:
return false;
}
}
boolean G_RumbleSupported(UINT8 which)
{
if (!I_RumbleSupported() || which >= NUM_GAMEPADS)
return 0;
return I_GetGamepadRumbleSupported(which);
}
boolean G_RumbleGamepad(UINT8 which, fixed_t large_magnitude, fixed_t small_magnitude, tic_t duration)
{
haptic_t effect;
if (!G_RumbleSupported(which))
return false;
effect.large_magnitude = large_magnitude;
effect.small_magnitude = small_magnitude;
effect.duration = duration;
return I_RumbleGamepad(which, &effect);
}
void G_StopGamepadRumble(UINT8 which)
{
if (G_RumbleSupported(which))
I_StopGamepadRumble(which);
}
fixed_t G_GetLargeMotorFreq(UINT8 which)
{
if (!G_RumbleSupported(which) || which >= NUM_GAMEPADS)
return 0;
gamepad_t *gamepad = &gamepads[which];
return gamepad->rumble.data.large_magnitude;
}
fixed_t G_GetSmallMotorFreq(UINT8 which)
{
if (!G_RumbleSupported(which) || which >= NUM_GAMEPADS)
return 0;
gamepad_t *gamepad = &gamepads[which];
return gamepad->rumble.data.small_magnitude;
}
boolean G_GetGamepadRumblePaused(UINT8 which)
{
return I_GetGamepadRumblePaused(which);
}
boolean G_SetLargeMotorFreq(UINT8 which, fixed_t freq)
{
return I_SetGamepadLargeMotorFreq(which, freq);
}
boolean G_SetSmallMotorFreq(UINT8 which, fixed_t freq)
{
return I_SetGamepadSmallMotorFreq(which, freq);
}
void G_SetGamepadRumblePaused(UINT8 which, boolean pause)
{
if (G_RumbleSupported(which))
I_SetGamepadRumblePaused(which, pause);
}
// Obtains the value of an axis, and makes it digital if needed
INT16 G_GamepadAxisEventValue(UINT8 which, INT16 value)
{
gamepad_t *gamepad = &gamepads[which];
if (gamepad->digital)
{
const UINT16 jdeadzone = G_GetGamepadDigitalDeadZone(which);
if (value < -jdeadzone)
value = -JOYAXISRANGE - 1;
else if (value > jdeadzone)
value = JOYAXISRANGE;
else
value = 0;
}
return value;
}
INT16 G_GetGamepadAxisValue(UINT8 which, gamepad_axis_e axis)
{
gamepad_t *gamepad = &gamepads[which];
if (axis >= NUM_GAMEPAD_AXES)
return 0;
return G_GamepadAxisEventValue(which, gamepad->axes[axis]);
}
fixed_t G_GetAdjustedGamepadAxis(UINT8 which, gamepad_axis_e axis, boolean applyDeadzone)
{
gamepad_t *gamepad = &gamepads[which];
if (axis >= NUM_GAMEPAD_AXES)
return 0;
INT32 value = gamepad->axes[axis];
if (applyDeadzone && gamepad->digital)
{
INT16 deadzone = G_GetGamepadDigitalDeadZone(which);
if (value < -deadzone)
value = -JOYAXISRANGE;
else if (value > deadzone)
value = JOYAXISRANGE;
else
value = 0;
}
else if (applyDeadzone)
{
INT32 sign = value < 0 ? -1 : 1;
INT16 deadzone = G_GetGamepadDeadZone(which);
INT32 magnitude = value * value;
INT32 nAxis = magnitude / JOYAXISRANGE;
INT32 nMagnitude = G_BasicDeadZoneCalculation(magnitude, deadzone);
value = (nAxis * nMagnitude) / JOYAXISRANGE;
value = min(value * sign, JOYAXISRANGE);
value = max(value, -JOYAXISRANGE);
}
return (value / 32767.0) * FRACUNIT;
}
static UINT16 CalcGamepadDeadZone(fixed_t deadzone)
{
INT32 value = (JOYAXISRANGE * deadzone) / FRACUNIT;
if (value < 0)
value = 0;
else if (value > JOYAXISRANGE)
value = JOYAXISRANGE;
return value;
}
UINT16 G_GetGamepadDeadZone(UINT8 which)
{
return CalcGamepadDeadZone(cv_deadzone[which].value);
}
UINT16 G_GetGamepadDigitalDeadZone(UINT8 which)
{
return CalcGamepadDeadZone(cv_digitaldeadzone[which].value);
}
// Take a magnitude of two axes, and adjust it to take out the deadzone
// Will return a value between 0 and JOYAXISRANGE
INT32 G_BasicDeadZoneCalculation(INT32 magnitude, const UINT16 jdeadzone)
{
INT32 deadzoneAppliedValue = 0;
INT32 adjustedMagnitude = abs(magnitude);
if (jdeadzone >= JOYAXISRANGE && adjustedMagnitude >= JOYAXISRANGE) // If the deadzone and magnitude are both 100%...
return JOYAXISRANGE; // ...return 100% input directly, to avoid dividing by 0
else if (adjustedMagnitude > jdeadzone) // Otherwise, calculate how much the magnitude exceeds the deadzone
{
adjustedMagnitude = min(adjustedMagnitude, JOYAXISRANGE);
adjustedMagnitude -= jdeadzone;
deadzoneAppliedValue = (adjustedMagnitude * JOYAXISRANGE) / (JOYAXISRANGE - jdeadzone);
}
return deadzoneAppliedValue;
}
INT32 G_RemapGamepadEvent(event_t *event, INT32 *type)
{
if (event->type == ev_gamepad_down)
{
*type = ev_keydown;
return KEY_GAMEPAD + event->key;
}
else if (event->type == ev_gamepad_up)
{
*type = ev_keyup;
return KEY_GAMEPAD + event->key;
}
else if (event->type == ev_gamepad_axis)
{
const UINT16 jdeadzone = G_GetGamepadDigitalDeadZone(event->which);
const INT16 value = G_GetGamepadAxisValue(event->which, event->key);
if (value < -jdeadzone || value > jdeadzone)
*type = ev_keyup;
else
*type = ev_keydown;
if (value < -jdeadzone)
return KEY_INV_AXES + event->key;
else
return KEY_AXES + event->key;
}
return event->key;
}
typedef struct
{
const char *name;
const char *menu1;
const char *menu2;
} button_strings_t;
#define DEF_NAME_BUTTON(str) {.name = str, .menu1 = str " Button", .menu2 = "the " str " Button"}
#define DEF_NAME_SIMPLE(str) {.name = str, .menu1 = NULL, .menu2 = "the " str}
#define DEF_NAME_DPAD(a, b) {.name = "D-Pad " a, .menu1 = "D-Pad " b, .menu2 = a}
#define PARTIAL_DEF_START [GAMEPAD_BUTTON_A] = { NULL }
#define PARTIAL_DEF_END [NUM_GAMEPAD_BUTTONS - 1] = { NULL }
static const char *GetStringFromButtonList(const button_strings_t *names, gamepad_button_e button, gamepad_string_e type)
{
switch (type)
{
case GAMEPAD_STRING_DEFAULT:
return names[button].name;
case GAMEPAD_STRING_MENU1:
if (names[button].menu1)
return names[button].menu1;
else
return names[button].name;
case GAMEPAD_STRING_MENU2:
if (names[button].menu2)
return names[button].menu2;
else
return names[button].name;
}
return NULL;
}
const char *G_GetGamepadButtonString(gamepadtype_e type, gamepad_button_e button, gamepad_string_e strtype)
{
static const button_strings_t base_names[] = {
[GAMEPAD_BUTTON_A] = DEF_NAME_BUTTON("A"),
[GAMEPAD_BUTTON_B] = DEF_NAME_BUTTON("B"),
[GAMEPAD_BUTTON_X] = DEF_NAME_BUTTON("X"),
[GAMEPAD_BUTTON_Y] = DEF_NAME_BUTTON("Y"),
[GAMEPAD_BUTTON_BACK] = DEF_NAME_BUTTON("Back"),
[GAMEPAD_BUTTON_GUIDE] = DEF_NAME_BUTTON("Guide"),
[GAMEPAD_BUTTON_START] = DEF_NAME_BUTTON("Start"),
[GAMEPAD_BUTTON_LEFTSTICK] = DEF_NAME_SIMPLE("Left Stick"),
[GAMEPAD_BUTTON_RIGHTSTICK] = DEF_NAME_SIMPLE("Right Stick"),
[GAMEPAD_BUTTON_LEFTSHOULDER] = DEF_NAME_SIMPLE("Left Shoulder"),
[GAMEPAD_BUTTON_RIGHTSHOULDER] = DEF_NAME_SIMPLE("Right Shoulder"),
[GAMEPAD_BUTTON_DPAD_UP] = DEF_NAME_DPAD("Up", "\x1A"),
[GAMEPAD_BUTTON_DPAD_DOWN] = DEF_NAME_DPAD("Down", "\x1B"),
[GAMEPAD_BUTTON_DPAD_LEFT] = DEF_NAME_DPAD("Left", "\x1C"),
[GAMEPAD_BUTTON_DPAD_RIGHT] = DEF_NAME_DPAD("Right", "\x1D"),
[GAMEPAD_BUTTON_PADDLE1] = DEF_NAME_SIMPLE("Paddle 1"),
[GAMEPAD_BUTTON_PADDLE2] = DEF_NAME_SIMPLE("Paddle 2"),
[GAMEPAD_BUTTON_PADDLE3] = DEF_NAME_SIMPLE("Paddle 3"),
[GAMEPAD_BUTTON_PADDLE4] = DEF_NAME_SIMPLE("Paddle 4"),
[GAMEPAD_BUTTON_TOUCHPAD] = DEF_NAME_SIMPLE("Touchpad"),
// This one's a bit weird
// Suffix the numbers in the event SDL adds more misc buttons
[GAMEPAD_BUTTON_MISC1] = {
.name = "Misc. Button",
.menu1 = "Gamepad Misc.",
.menu2 = "the Misc. Button"
},
};
button_strings_t const *names = NULL;
if (G_GamepadTypeIsXbox(type))
{
#define BASE_XBOX_NAMES \
[GAMEPAD_BUTTON_LEFTSHOULDER] = DEF_NAME_SIMPLE("Left Bumper"), \
[GAMEPAD_BUTTON_RIGHTSHOULDER] = DEF_NAME_SIMPLE("Right Bumper")
static const button_strings_t xbox_names[] = { PARTIAL_DEF_START,
BASE_XBOX_NAMES,
PARTIAL_DEF_END };
static const button_strings_t series_xs_names[] = { PARTIAL_DEF_START,
BASE_XBOX_NAMES,
[GAMEPAD_BUTTON_MISC1] = DEF_NAME_BUTTON("Share"),
PARTIAL_DEF_END };
static const button_strings_t elite_names[] = { PARTIAL_DEF_START,
BASE_XBOX_NAMES,
[GAMEPAD_BUTTON_PADDLE1] = DEF_NAME_SIMPLE("P1 Paddle"),
[GAMEPAD_BUTTON_PADDLE2] = DEF_NAME_SIMPLE("P2 Paddle"),
[GAMEPAD_BUTTON_PADDLE3] = DEF_NAME_SIMPLE("P3 Paddle"),
[GAMEPAD_BUTTON_PADDLE4] = DEF_NAME_SIMPLE("P4 Paddle"),
PARTIAL_DEF_END };
if (type == GAMEPAD_TYPE_XBOX_SERIES_XS) // X|S controllers have a Share button
names = series_xs_names;
else if (type == GAMEPAD_TYPE_XBOX_ELITE) // Elite controller has paddles
names = elite_names;
else
names = xbox_names;
#undef BASE_XBOX_NAMES
}
else if (G_GamepadTypeIsPlayStation(type))
{
#define BASE_PS_NAMES \
[GAMEPAD_BUTTON_A] = DEF_NAME_BUTTON("Cross"), \
[GAMEPAD_BUTTON_B] = DEF_NAME_BUTTON("Circle"), \
[GAMEPAD_BUTTON_X] = DEF_NAME_BUTTON("Square"), \
[GAMEPAD_BUTTON_Y] = DEF_NAME_BUTTON("Triangle"), \
[GAMEPAD_BUTTON_BACK] = DEF_NAME_BUTTON("Select"), \
[GAMEPAD_BUTTON_GUIDE] = DEF_NAME_BUTTON("PS"), \
[GAMEPAD_BUTTON_LEFTSTICK] = DEF_NAME_BUTTON("L3"), \
[GAMEPAD_BUTTON_RIGHTSTICK] = DEF_NAME_BUTTON("R3"), \
[GAMEPAD_BUTTON_LEFTSHOULDER] = DEF_NAME_BUTTON("L1"), \
[GAMEPAD_BUTTON_RIGHTSHOULDER] = DEF_NAME_BUTTON("R1")
static const button_strings_t ps_names[] = {
BASE_PS_NAMES,
PARTIAL_DEF_END };
static const button_strings_t ps5_names[] = {
BASE_PS_NAMES,
[GAMEPAD_BUTTON_MISC1] = DEF_NAME_BUTTON("Microphone"),
PARTIAL_DEF_END };
names = type == GAMEPAD_TYPE_PS5 ? ps5_names : ps_names;
#undef BASE_PS_NAMES
}
else if (G_GamepadTypeIsNintendoSwitch(type))
{
static const button_strings_t switch_names[] = { PARTIAL_DEF_START,
[GAMEPAD_BUTTON_BACK] = DEF_NAME_BUTTON("-"),
[GAMEPAD_BUTTON_GUIDE] = DEF_NAME_BUTTON("HOME"),
[GAMEPAD_BUTTON_START] = DEF_NAME_BUTTON("+"),
[GAMEPAD_BUTTON_LEFTSHOULDER] = DEF_NAME_BUTTON("L"),
[GAMEPAD_BUTTON_RIGHTSHOULDER] = DEF_NAME_BUTTON("R"),
[GAMEPAD_BUTTON_MISC1] = DEF_NAME_BUTTON("Capture"),
PARTIAL_DEF_END };
names = switch_names;
}
else if (type == GAMEPAD_TYPE_AMAZON_LUNA)
{
static const button_strings_t luna_names[] = { PARTIAL_DEF_START,
[GAMEPAD_BUTTON_MISC1] = DEF_NAME_BUTTON("Microphone"),
PARTIAL_DEF_END };
names = luna_names;
}
const char *str = NULL;
if (names)
str = GetStringFromButtonList(names, button, strtype);
if (str == NULL)
str = GetStringFromButtonList(base_names, button, strtype);
if (str)
return str;
return "Unknown";
}
#undef DEF_NAME_BUTTON
#undef DEF_NAME_SIMPLE
#undef DEF_NAME_DPAD
#undef PARTIAL_DEF_START
#undef PARTIAL_DEF_END
typedef struct
{
const char *name;
const char *menu1;
const char *menu2;
const char *name_inv;
const char *menu1_inv;
const char *menu2_inv;
} axis_strings_t;
#define DEF_NAME_AXIS(str, a, inv_a, b, inv_b) {\
str " " a, str " " b, "the " str " " b, \
str " " inv_a, str " " inv_b, "the " str " " inv_b}
#define DEF_NAME_TRIGGER(str) {str, NULL, "the " str, NULL, NULL, NULL}
#define DEF_NAME_BUTTON(str) {str, str " Button", "the " str " Button", NULL, NULL, NULL}
#define PARTIAL_DEF_START [GAMEPAD_AXIS_LEFTX] = { NULL }
static const char *GetStringFromAxisList(const axis_strings_t *names, gamepad_axis_e axis, gamepad_string_e type, boolean inv)
{
switch (type)
{
case GAMEPAD_STRING_DEFAULT:
if (inv && names[axis].name_inv)
return names[axis].name_inv;
else
return names[axis].name;
break;
case GAMEPAD_STRING_MENU1:
if (inv && names[axis].menu1_inv)
return names[axis].menu1_inv;
if (names[axis].menu1)
return names[axis].menu1;
else
return names[axis].name;
break;
case GAMEPAD_STRING_MENU2:
if (inv && names[axis].menu2_inv)
return names[axis].menu2_inv;
if (names[axis].menu2)
return names[axis].menu2;
else
return names[axis].name;
break;
}
return NULL;
}
const char *G_GetGamepadAxisString(gamepadtype_e type, gamepad_axis_e axis, gamepad_string_e strtype, boolean inv)
{
static const axis_strings_t base_names[] = {
[GAMEPAD_AXIS_LEFTX] = DEF_NAME_AXIS("Left Stick", "X", "X-", "\x1D", "\x1C"),
[GAMEPAD_AXIS_LEFTY] = DEF_NAME_AXIS("Left Stick", "Y", "Y-", "\x1B", "\x1A"),
[GAMEPAD_AXIS_RIGHTX] = DEF_NAME_AXIS("Right Stick", "X", "X-", "\x1D", "\x1C"),
[GAMEPAD_AXIS_RIGHTY] = DEF_NAME_AXIS("Right Stick", "Y", "Y-", "\x1B", "\x1A"),
[GAMEPAD_AXIS_TRIGGERLEFT] = DEF_NAME_TRIGGER("Left Trigger"),
[GAMEPAD_AXIS_TRIGGERRIGHT] = DEF_NAME_TRIGGER("Right Trigger")
};
axis_strings_t const *names = NULL;
if (G_GamepadTypeIsPlayStation(type))
{
static const axis_strings_t ps_names[] = { PARTIAL_DEF_START,
[GAMEPAD_AXIS_TRIGGERLEFT] = DEF_NAME_BUTTON("L2"),
[GAMEPAD_AXIS_TRIGGERRIGHT] = DEF_NAME_BUTTON("R2"),
};
names = ps_names;
}
else if (G_GamepadTypeIsNintendoSwitch(type))
{
static const axis_strings_t switch_names[] = { PARTIAL_DEF_START,
[GAMEPAD_AXIS_TRIGGERLEFT] = DEF_NAME_BUTTON("ZL"),
[GAMEPAD_AXIS_TRIGGERRIGHT] = DEF_NAME_BUTTON("ZR"),
};
names = switch_names;
}
const char *str = NULL;
if (names)
str = GetStringFromAxisList(names, axis, strtype, inv);
if (str == NULL)
str = GetStringFromAxisList(base_names, axis, strtype, inv);
if (str)
return str;
return "Unknown";
}
#undef DEF_NAME_AXIS
#undef DEF_NAME_TRIGGER
#undef DEF_NAME_BUTTON
#undef PARTIAL_DEF_START
typedef struct
{
INT32 keynum;
const char *name;
} keyname_t;
static keyname_t keynames[] =
{
{KEY_SPACE, "space"},
{KEY_CAPSLOCK, "caps lock"},
{KEY_ENTER, "enter"},
{KEY_TAB, "tab"},
{KEY_ESCAPE, "escape"},
{KEY_BACKSPACE, "backspace"},
{KEY_NUMLOCK, "numlock"},
{KEY_SCROLLLOCK, "scrolllock"},
// satya nadella keys
{KEY_LEFTWIN, "leftwin"},
{KEY_RIGHTWIN, "rightwin"},
{KEY_MENU, "menu"},
{KEY_LSHIFT, "lshift"},
{KEY_RSHIFT, "rshift"},
{KEY_LCTRL, "lctrl"},
{KEY_RCTRL, "rctrl"},
{KEY_LALT, "lalt"},
{KEY_RALT, "ralt"},
// keypad keys
{KEY_KPADSLASH, "keypad /"},
{KEY_KEYPAD7, "keypad 7"},
{KEY_KEYPAD8, "keypad 8"},
{KEY_KEYPAD9, "keypad 9"},
{KEY_MINUSPAD, "keypad -"},
{KEY_KEYPAD4, "keypad 4"},
{KEY_KEYPAD5, "keypad 5"},
{KEY_KEYPAD6, "keypad 6"},
{KEY_PLUSPAD, "keypad +"},
{KEY_KEYPAD1, "keypad 1"},
{KEY_KEYPAD2, "keypad 2"},
{KEY_KEYPAD3, "keypad 3"},
{KEY_KEYPAD0, "keypad 0"},
{KEY_KPADDEL, "keypad ."},
// extended keys (not keypad)
{KEY_HOME, "home"},
{KEY_UPARROW, "up arrow"},
{KEY_PGUP, "pgup"},
{KEY_LEFTARROW, "left arrow"},
{KEY_RIGHTARROW, "right arrow"},
{KEY_END, "end"},
{KEY_DOWNARROW, "down arrow"},
{KEY_PGDN, "pgdn"},
{KEY_INS, "ins"},
{KEY_DEL, "del"},
// other keys
{KEY_F1, "f1"},
{KEY_F2, "f2"},
{KEY_F3, "f3"},
{KEY_F4, "f4"},
{KEY_F5, "f5"},
{KEY_F6, "f6"},
{KEY_F7, "f7"},
{KEY_F8, "f8"},
{KEY_F9, "f9"},
{KEY_F10, "f10"},
{KEY_F11, "f11"},
{KEY_F12, "f12"},
// KEY_CONSOLE has an exception in the keyname code
{'`', "TILDE"},
{KEY_PAUSE, "pause/break"},
// virtual keys for mouse buttons and gamepad buttons
{KEY_MOUSE1+0,"mouse1"},
{KEY_MOUSE1+1,"mouse2"},
{KEY_MOUSE1+2,"mouse3"},
{KEY_MOUSE1+3,"mouse4"},
{KEY_MOUSE1+4,"mouse5"},
{KEY_MOUSE1+5,"mouse6"},
{KEY_MOUSE1+6,"mouse7"},
{KEY_MOUSE1+7,"mouse8"},
{KEY_2MOUSE1+0,"sec_mouse1"},
{KEY_2MOUSE1+1,"sec_mouse2"},
{KEY_2MOUSE1+2,"sec_mouse3"},
{KEY_2MOUSE1+3,"sec_mouse4"},
{KEY_2MOUSE1+4,"sec_mouse5"},
{KEY_2MOUSE1+5,"sec_mouse6"},
{KEY_2MOUSE1+6,"sec_mouse7"},
{KEY_2MOUSE1+7,"sec_mouse8"},
{KEY_MOUSEWHEELUP, "wheel 1 up"},
{KEY_MOUSEWHEELDOWN, "wheel 1 down"},
{KEY_2MOUSEWHEELUP, "wheel 2 up"},
{KEY_2MOUSEWHEELDOWN, "wheel 2 down"},
#define DEF_GAMEPAD_NAME(btn, name) {KEY_GAMEPAD+GAMEPAD_BUTTON_##btn, name}
#define DEF_GAMEPAD_AXIS(ax, name) \
{KEY_AXES+GAMEPAD_AXIS_##ax, name}, \
{KEY_INV_AXES+GAMEPAD_AXIS_##ax, name "-"}
DEF_GAMEPAD_NAME(A, "a button"),
DEF_GAMEPAD_NAME(B, "b button"),
DEF_GAMEPAD_NAME(X, "x button"),
DEF_GAMEPAD_NAME(Y, "y button"),
DEF_GAMEPAD_NAME(BACK, "back button"),
DEF_GAMEPAD_NAME(GUIDE, "guide button"),
DEF_GAMEPAD_NAME(START, "start button"),
DEF_GAMEPAD_NAME(LEFTSTICK, "left stick"),
DEF_GAMEPAD_NAME(RIGHTSTICK, "right stick"),
DEF_GAMEPAD_NAME(LEFTSHOULDER, "left shoulder"),
DEF_GAMEPAD_NAME(RIGHTSHOULDER, "right shoulder"),
DEF_GAMEPAD_NAME(DPAD_UP, "d-pad up"),
DEF_GAMEPAD_NAME(DPAD_DOWN, "d-pad down"),
DEF_GAMEPAD_NAME(DPAD_LEFT, "d-pad left"),
DEF_GAMEPAD_NAME(DPAD_RIGHT, "d-pad right"),
DEF_GAMEPAD_NAME(MISC1, "gamepad misc 1"),
DEF_GAMEPAD_NAME(PADDLE1, "paddle 1"),
DEF_GAMEPAD_NAME(PADDLE2, "paddle 2"),
DEF_GAMEPAD_NAME(PADDLE3, "paddle 3"),
DEF_GAMEPAD_NAME(PADDLE4, "paddle 4"),
DEF_GAMEPAD_NAME(TOUCHPAD, "touchpad"),
DEF_GAMEPAD_AXIS(LEFTX, "left stick x"),
DEF_GAMEPAD_AXIS(LEFTY, "left stick y"),
DEF_GAMEPAD_AXIS(RIGHTX, "right stick x"),
DEF_GAMEPAD_AXIS(RIGHTY, "right stick y"),
DEF_GAMEPAD_AXIS(TRIGGERLEFT, "left trigger"),
DEF_GAMEPAD_AXIS(TRIGGERRIGHT, "right trigger"),
#undef DEF_GAMEPAD_NAME
#undef DEF_GAMEPAD_AXIS
{KEY_DBLMOUSE1+0, "dblmouse1"},
{KEY_DBLMOUSE1+1, "dblmouse2"},
{KEY_DBLMOUSE1+2, "dblmouse3"},
{KEY_DBLMOUSE1+3, "dblmouse4"},
{KEY_DBLMOUSE1+4, "dblmouse5"},
{KEY_DBLMOUSE1+5, "dblmouse6"},
{KEY_DBLMOUSE1+6, "dblmouse7"},
{KEY_DBLMOUSE1+7, "dblmouse8"},
{KEY_DBL2MOUSE1+0, "dblsec_mouse1"},
{KEY_DBL2MOUSE1+1, "dblsec_mouse2"},
{KEY_DBL2MOUSE1+2, "dblsec_mouse3"},
{KEY_DBL2MOUSE1+3, "dblsec_mouse4"},
{KEY_DBL2MOUSE1+4, "dblsec_mouse5"},
{KEY_DBL2MOUSE1+5, "dblsec_mouse6"},
{KEY_DBL2MOUSE1+6, "dblsec_mouse7"},
{KEY_DBL2MOUSE1+7, "dblsec_mouse8"}
};
#define NUMKEYNAMES (sizeof(keynames) / sizeof(keyname_t))
static keyname_t displaykeynames[] =
{
{KEY_SPACE, "Space Bar"},
{KEY_CAPSLOCK, "Caps Lock"},
{KEY_ENTER, "Enter"},
{KEY_TAB, "Tab"},
{KEY_ESCAPE, "Escape"},
{KEY_BACKSPACE, "Backspace"},
{KEY_NUMLOCK, "Num Lock"},
{KEY_SCROLLLOCK, "Scroll Lock"},
#ifdef _WIN32
{KEY_LEFTWIN, "Left Windows"},
{KEY_RIGHTWIN, "Right Windows"},
#else
{KEY_LEFTWIN, "Left Super"},
{KEY_RIGHTWIN, "Right Super"},
#endif
{KEY_MENU, "Menu"},
{KEY_LSHIFT, "Left Shift"},
{KEY_RSHIFT, "Right Shift"},
{KEY_LCTRL, "Left Ctrl"},
{KEY_RCTRL, "Right Ctrl"},
{KEY_LALT, "Left Alt"},
{KEY_RALT, "Right Alt"},
{KEY_KEYPAD0, "Keypad 0"},
{KEY_KEYPAD1, "Keypad 1"},
{KEY_KEYPAD2, "Keypad 2"},
{KEY_KEYPAD3, "Keypad 3"},
{KEY_KEYPAD4, "Keypad 4"},
{KEY_KEYPAD5, "Keypad 5"},
{KEY_KEYPAD6, "Keypad 6"},
{KEY_KEYPAD7, "Keypad 7"},
{KEY_KEYPAD8, "Keypad 8"},
{KEY_KEYPAD9, "Keypad 9"},
{KEY_PLUSPAD, "Keypad +"},
{KEY_MINUSPAD, "Keypad -"},
{KEY_KPADSLASH, "Keypad /"},
{KEY_KPADDEL, "Keypad ."},
{KEY_UPARROW, "Up Arrow"},
{KEY_DOWNARROW, "Down Arrow"},
{KEY_LEFTARROW, "Left Arrow"},
{KEY_RIGHTARROW, "Right Arrow"},
{KEY_HOME, "Home"},
{KEY_END, "End"},
{KEY_PGUP, "Page Up"},
{KEY_PGDN, "Page Down"},
{KEY_INS, "Insert"},
{KEY_DEL, "Delete"},
{KEY_F1, "F1"},
{KEY_F2, "F2"},
{KEY_F3, "F3"},
{KEY_F4, "F4"},
{KEY_F5, "F5"},
{KEY_F6, "F6"},
{KEY_F7, "F7"},
{KEY_F8, "F8"},
{KEY_F9, "F9"},
{KEY_F10, "F10"},
{KEY_F11, "F11"},
{KEY_F12, "F12"},
{'`', "Tilde"},
{KEY_PAUSE, "Pause/Break"},
{KEY_MOUSE1+0, "Left Mouse Button"},
{KEY_MOUSE1+1, "Right Mouse Button"},
{KEY_MOUSE1+2, "Middle Mouse Button"},
{KEY_MOUSE1+3, "X1 Mouse Button"},
{KEY_MOUSE1+4, "X2 Mouse Button"},
{KEY_2MOUSE1+0, "Sec. Mouse Left Button"},
{KEY_2MOUSE1+1, "Sec. Mouse Right Button"},
{KEY_2MOUSE1+2, "Sec. Mouse Middle Button"},
{KEY_2MOUSE1+3, "Sec. Mouse X1 Button"},
{KEY_2MOUSE1+4, "Sec. Mouse X2 Button"},
{KEY_MOUSEWHEELUP, "Mouse Wheel Up"},
{KEY_MOUSEWHEELDOWN, "Mouse Wheel Down"},
{KEY_2MOUSEWHEELUP, "Sec. Mouse Wheel Up"},
{KEY_2MOUSEWHEELDOWN, "Sec. Mouse Wheel Down"},
#define DEF_GAMEPAD_NAME(btn, name) {KEY_GAMEPAD+GAMEPAD_BUTTON_##btn, name}
#define DEF_GAMEPAD_AXIS(ax, name) {KEY_AXES+GAMEPAD_AXIS_##ax, name}
DEF_GAMEPAD_NAME(A, "A Button"),
DEF_GAMEPAD_NAME(B, "B Button"),
DEF_GAMEPAD_NAME(X, "X Button"),
DEF_GAMEPAD_NAME(Y, "Y Button"),
DEF_GAMEPAD_NAME(BACK, "Back Button"),
DEF_GAMEPAD_NAME(GUIDE, "Guide Button"),
DEF_GAMEPAD_NAME(START, "Start Button"),
DEF_GAMEPAD_NAME(LEFTSTICK, "Left Stick Button"),
DEF_GAMEPAD_NAME(RIGHTSTICK, "Right Stick Button"),
DEF_GAMEPAD_NAME(LEFTSHOULDER, "Left Shoulder"),
DEF_GAMEPAD_NAME(RIGHTSHOULDER, "Right Shoulder"),
DEF_GAMEPAD_NAME(DPAD_UP, "D-Pad Up"),
DEF_GAMEPAD_NAME(DPAD_DOWN, "D-Pad Down"),
DEF_GAMEPAD_NAME(DPAD_LEFT, "D-Pad Left"),
DEF_GAMEPAD_NAME(DPAD_RIGHT, "D-Pad Right"),
DEF_GAMEPAD_NAME(MISC1, "Gamepad Misc. 1"),
DEF_GAMEPAD_NAME(PADDLE1, "Paddle 1"),
DEF_GAMEPAD_NAME(PADDLE2, "Paddle 2"),
DEF_GAMEPAD_NAME(PADDLE3, "Paddle 3"),
DEF_GAMEPAD_NAME(PADDLE4, "Paddle 4"),
DEF_GAMEPAD_NAME(TOUCHPAD, "Touchpad"),
{KEY_INV_AXES + GAMEPAD_AXIS_LEFTX, "Left Stick \x1C"},
{KEY_AXES + GAMEPAD_AXIS_LEFTX, "Left Stick \x1D"},
{KEY_INV_AXES + GAMEPAD_AXIS_LEFTY, "Left Stick \x1A"},
{KEY_AXES + GAMEPAD_AXIS_LEFTY, "Left Stick \x1B"},
{KEY_INV_AXES + GAMEPAD_AXIS_RIGHTX, "Right Stick \x1C"},
{KEY_AXES + GAMEPAD_AXIS_RIGHTX, "Right Stick \x1D"},
{KEY_INV_AXES + GAMEPAD_AXIS_RIGHTY, "Right Stick \x1A"},
{KEY_AXES + GAMEPAD_AXIS_RIGHTY, "Right Stick \x1B"},
DEF_GAMEPAD_AXIS(TRIGGERLEFT, "Left Trigger"),
DEF_GAMEPAD_AXIS(TRIGGERRIGHT, "Right Trigger"),
#undef DEF_GAMEPAD_NAME
#undef DEF_GAMEPAD_AXIS
};
#define NUMDISPLAYKEYNAMES (sizeof(displaykeynames) / sizeof(keyname_t))
static const char *gamecontrolname[NUM_GAMECONTROLS] =
{
"nothing", // a key/button mapped to GC_NULL has no effect
"forward",
"backward",
"strafeleft",
"straferight",
"turnleft",
"turnright",
"weaponnext",
"weaponprev",
"weapon1",
"weapon2",
"weapon3",
"weapon4",
"weapon5",
"weapon6",
"weapon7",
"weapon8",
"weapon9",
"weapon10",
"fire",
"firenormal",
"tossflag",
"spin",
"camtoggle",
"camreset",
"lookup",
"lookdown",
"centerview",
"mouseaiming",
"talkkey",
"teamtalkkey",
"scores",
"jump",
"console",
"pause",
"systemmenu",
"screenshot",
"recordgif",
"viewpoint", // Rename this to "viewpointnext" for the next major version
"viewpointprev",
"custom1",
"custom2",
"custom3",
};
//
// Detach any keys associated to the given game control
// - pass the pointer to the gamecontrol table for the player being edited
void G_ClearControlKeys(INT32 (*setupcontrols)[2], INT32 control)
{
setupcontrols[control][0] = KEY_NULL;
setupcontrols[control][1] = KEY_NULL;
}
void G_ClearAllControlKeys(void)
{
INT32 i;
for (i = 0; i < NUM_GAMECONTROLS; i++)
{
G_ClearControlKeys(gamecontrol, i);
G_ClearControlKeys(gamecontrolbis, i);
}
}
//
// Returns the name of a key (or virtual key for mouse and gamepad)
// the input value being an keynum
//
const char *G_KeyNumToName(INT32 keynum)
{
static char keynamestr[8];
UINT32 j;
// return a string with the ascii char if displayable
if (keynum > ' ' && keynum <= 'z' && keynum != KEY_CONSOLE)
{
keynamestr[0] = (char)keynum;
keynamestr[1] = '\0';
return keynamestr;
}
// find a description for special keys
for (j = 0; j < NUMKEYNAMES; j++)
if (keynames[j].keynum == keynum)
return keynames[j].name;
// create a name for unknown keys
snprintf(keynamestr, sizeof keynamestr, "KEY%d", keynum);
return keynamestr;
}
const char *G_GetDisplayNameForKey(INT32 keynum)
{
static char keynamestr[32];
UINT32 j;
// find a description for special keys
for (j = 0; j < NUMDISPLAYKEYNAMES; j++)
if (displaykeynames[j].keynum == keynum)
return displaykeynames[j].name;
// return a string with the ascii char if displayable
if (keynum > ' ' && keynum <= 'z' && keynum != KEY_CONSOLE)
{
snprintf(keynamestr, sizeof keynamestr, "%c Key", toupper((char)keynum));
return keynamestr;
}
// unnamed mouse buttons
if (keynum >= KEY_MOUSE1 && keynum <= KEY_MOUSE1+7)
{
j = (keynum - KEY_MOUSE1) + 1;
snprintf(keynamestr, sizeof keynamestr, "Mouse Button #%d", j);
return keynamestr;
}
else if (keynum >= KEY_2MOUSE1 && keynum <= KEY_2MOUSE1+7)
{
j = (keynum - KEY_2MOUSE1) + 1;
snprintf(keynamestr, sizeof keynamestr, "Sec. Mouse Button #%d", j);
return keynamestr;
}
// create a name for unknown keys
snprintf(keynamestr, sizeof keynamestr, "Unknown Key %d", keynum);
return keynamestr;
}
INT32 G_KeyNameToNum(const char *keystr)
{
UINT32 j;
if (!keystr[1] && keystr[0] > ' ' && keystr[0] <= 'z')
return keystr[0];
if (!strncmp(keystr, "KEY", 3) && keystr[3] >= '0' && keystr[3] <= '9')
{
/* what if we out of range bruh? */
j = atoi(&keystr[3]);
if (j < NUMINPUTS)
return j;
return 0;
}
for (j = 0; j < NUMKEYNAMES; j++)
if (!stricmp(keynames[j].name, keystr))
return keynames[j].keynum;
return 0;
}
const char *G_GamepadTypeToString(gamepadtype_e type)
{
static const char *names[] = {
"xbox-360",
"xbox-one",
"xbox-series-xs",
"xbox-elite",
"ps3",
"ps4",
"ps5",
"switch-pro",
"switch-joy-con-grip",
"switch-joy-con-left",
"switch-joy-con-right",
"stadia",
"amazon-luna",
"steam-controller",
"virtual",
"unknown"
};
return names[type];
}
void G_InitGamepads(void)
{
for (UINT8 i = 0; i < NUM_GAMEPADS; i++)
gamepads[i].num = i;
}
void G_DefineDefaultControls(void)
{
INT32 i;
// FPS game controls (WASD)
gamecontroldefault[gcs_fps][GC_FORWARD ][0] = 'w';
gamecontroldefault[gcs_fps][GC_BACKWARD ][0] = 's';
gamecontroldefault[gcs_fps][GC_STRAFELEFT ][0] = 'a';
gamecontroldefault[gcs_fps][GC_STRAFERIGHT][0] = 'd';
gamecontroldefault[gcs_fps][GC_LOOKUP ][0] = KEY_UPARROW;
gamecontroldefault[gcs_fps][GC_LOOKDOWN ][0] = KEY_DOWNARROW;
gamecontroldefault[gcs_fps][GC_TURNLEFT ][0] = KEY_LEFTARROW;
gamecontroldefault[gcs_fps][GC_TURNRIGHT ][0] = KEY_RIGHTARROW;
gamecontroldefault[gcs_fps][GC_CENTERVIEW ][0] = KEY_LCTRL;
gamecontroldefault[gcs_fps][GC_JUMP ][0] = KEY_SPACE;
gamecontroldefault[gcs_fps][GC_SPIN ][0] = KEY_LSHIFT;
gamecontroldefault[gcs_fps][GC_FIRE ][0] = KEY_RCTRL;
gamecontroldefault[gcs_fps][GC_FIRE ][1] = KEY_MOUSE1+0;
gamecontroldefault[gcs_fps][GC_FIRENORMAL ][0] = KEY_RALT;
gamecontroldefault[gcs_fps][GC_FIRENORMAL ][1] = KEY_MOUSE1+1;
gamecontroldefault[gcs_fps][GC_CUSTOM1 ][0] = 'z';
gamecontroldefault[gcs_fps][GC_CUSTOM2 ][0] = 'x';
gamecontroldefault[gcs_fps][GC_CUSTOM3 ][0] = 'c';
// Platform game controls (arrow keys), currently unused
gamecontroldefault[gcs_platform][GC_FORWARD ][0] = KEY_UPARROW;
gamecontroldefault[gcs_platform][GC_BACKWARD ][0] = KEY_DOWNARROW;
gamecontroldefault[gcs_platform][GC_STRAFELEFT ][0] = 'a';
gamecontroldefault[gcs_platform][GC_STRAFERIGHT][0] = 'd';
gamecontroldefault[gcs_platform][GC_LOOKUP ][0] = KEY_PGUP;
gamecontroldefault[gcs_platform][GC_LOOKDOWN ][0] = KEY_PGDN;
gamecontroldefault[gcs_platform][GC_TURNLEFT ][0] = KEY_LEFTARROW;
gamecontroldefault[gcs_platform][GC_TURNRIGHT ][0] = KEY_RIGHTARROW;
gamecontroldefault[gcs_platform][GC_CENTERVIEW ][0] = KEY_END;
gamecontroldefault[gcs_platform][GC_JUMP ][0] = KEY_SPACE;
gamecontroldefault[gcs_platform][GC_SPIN ][0] = KEY_LSHIFT;
gamecontroldefault[gcs_platform][GC_FIRE ][0] = 's';
gamecontroldefault[gcs_platform][GC_FIRE ][1] = KEY_MOUSE1+0;
gamecontroldefault[gcs_platform][GC_FIRENORMAL ][0] = 'w';
for (i = 1; i < num_gamecontrolschemes; i++) // skip gcs_custom (0)
{
gamecontroldefault[i][GC_WEAPONNEXT ][0] = KEY_MOUSEWHEELUP+0;
gamecontroldefault[i][GC_WEAPONPREV ][0] = KEY_MOUSEWHEELDOWN+0;
gamecontroldefault[i][GC_WEPSLOT1 ][0] = '1';
gamecontroldefault[i][GC_WEPSLOT2 ][0] = '2';
gamecontroldefault[i][GC_WEPSLOT3 ][0] = '3';
gamecontroldefault[i][GC_WEPSLOT4 ][0] = '4';
gamecontroldefault[i][GC_WEPSLOT5 ][0] = '5';
gamecontroldefault[i][GC_WEPSLOT6 ][0] = '6';
gamecontroldefault[i][GC_WEPSLOT7 ][0] = '7';
gamecontroldefault[i][GC_WEPSLOT8 ][0] = '8';
gamecontroldefault[i][GC_WEPSLOT9 ][0] = '9';
gamecontroldefault[i][GC_WEPSLOT10 ][0] = '0';
gamecontroldefault[i][GC_TOSSFLAG ][0] = '\'';
gamecontroldefault[i][GC_CAMTOGGLE ][0] = 'v';
gamecontroldefault[i][GC_CAMRESET ][0] = 'r';
gamecontroldefault[i][GC_TALKKEY ][0] = 't';
gamecontroldefault[i][GC_TEAMKEY ][0] = 'y';
gamecontroldefault[i][GC_SCORES ][0] = KEY_TAB;
gamecontroldefault[i][GC_CONSOLE ][0] = KEY_CONSOLE;
gamecontroldefault[i][GC_PAUSE ][0] = 'p';
gamecontroldefault[i][GC_SCREENSHOT ][0] = KEY_F8;
gamecontroldefault[i][GC_RECORDGIF ][0] = KEY_F9;
gamecontroldefault[i][GC_VIEWPOINTNEXT][0] = KEY_F12;
// Gamepad controls -- same for both schemes
gamecontroldefault[i][GC_JUMP ][1] = GAMEPAD_KEY(A); // A
gamecontroldefault[i][GC_SPIN ][1] = GAMEPAD_KEY(X); // X
gamecontroldefault[i][GC_CUSTOM1 ][1] = GAMEPAD_KEY(B); // B
gamecontroldefault[i][GC_CUSTOM2 ][1] = GAMEPAD_KEY(Y); // Y
gamecontroldefault[i][GC_CUSTOM3 ][1] = GAMEPAD_KEY(LEFTSTICK); // Left Stick
gamecontroldefault[i][GC_CENTERVIEW ][1] = GAMEPAD_KEY(RIGHTSTICK); // Right Stick
gamecontroldefault[i][GC_WEAPONPREV ][1] = GAMEPAD_KEY(LEFTSHOULDER); // LB
gamecontroldefault[i][GC_WEAPONNEXT ][1] = GAMEPAD_KEY(RIGHTSHOULDER); // RB
gamecontroldefault[i][GC_SCREENSHOT ][1] = GAMEPAD_KEY(BACK); // Back
gamecontroldefault[i][GC_SYSTEMMENU ][0] = GAMEPAD_KEY(START); // Start
gamecontroldefault[i][GC_CAMTOGGLE ][1] = GAMEPAD_KEY(DPAD_UP); // D-Pad Up
gamecontroldefault[i][GC_VIEWPOINTNEXT][1] = GAMEPAD_KEY(DPAD_DOWN); // D-Pad Down
gamecontroldefault[i][GC_TOSSFLAG ][1] = GAMEPAD_KEY(DPAD_LEFT); // D-Pad Left
gamecontroldefault[i][GC_SCORES ][1] = GAMEPAD_KEY(DPAD_RIGHT); // D-Pad Right
// Second player only has gamepad defaults
gamecontrolbisdefault[i][GC_JUMP ][1] = GAMEPAD_KEY(A); // A
gamecontrolbisdefault[i][GC_SPIN ][1] = GAMEPAD_KEY(X); // X
gamecontrolbisdefault[i][GC_CUSTOM1 ][1] = GAMEPAD_KEY(B); // B
gamecontrolbisdefault[i][GC_CUSTOM2 ][1] = GAMEPAD_KEY(Y); // Y
gamecontrolbisdefault[i][GC_CUSTOM3 ][1] = GAMEPAD_KEY(LEFTSTICK); // Left Stick
gamecontrolbisdefault[i][GC_CENTERVIEW ][1] = GAMEPAD_KEY(RIGHTSTICK); // Right Stick
gamecontrolbisdefault[i][GC_WEAPONPREV ][1] = GAMEPAD_KEY(LEFTSHOULDER); // LB
gamecontrolbisdefault[i][GC_WEAPONNEXT ][1] = GAMEPAD_KEY(RIGHTSHOULDER); // RB
gamecontrolbisdefault[i][GC_SCREENSHOT ][1] = GAMEPAD_KEY(BACK); // Back
//gamecontrolbisdefault[i][GC_SYSTEMMENU ][0] = GAMEPAD_KEY(START); // Start
gamecontrolbisdefault[i][GC_CAMTOGGLE ][1] = GAMEPAD_KEY(DPAD_UP); // D-Pad Up
gamecontrolbisdefault[i][GC_VIEWPOINTNEXT][1] = GAMEPAD_KEY(DPAD_DOWN); // D-Pad Down
gamecontrolbisdefault[i][GC_TOSSFLAG ][1] = GAMEPAD_KEY(DPAD_LEFT); // D-Pad Left
//gamecontrolbisdefault[i][GC_SCORES ][1] = GAMEPAD_KEY(DPAD_RIGHT); // D-Pad Right
}
}
INT32 G_GetControlScheme(INT32 (*fromcontrols)[2], const INT32 *gclist, INT32 gclen)
{
INT32 i, j, gc;
boolean skipscheme;
for (i = 1; i < num_gamecontrolschemes; i++) // skip gcs_custom (0)
{
skipscheme = false;
for (j = 0; j < (gclist && gclen ? gclen : NUM_GAMECONTROLS); j++)
{
gc = (gclist && gclen) ? gclist[j] : j;
if (((fromcontrols[gc][0] && gamecontroldefault[i][gc][0]) ? fromcontrols[gc][0] != gamecontroldefault[i][gc][0] : true) &&
((fromcontrols[gc][0] && gamecontroldefault[i][gc][1]) ? fromcontrols[gc][0] != gamecontroldefault[i][gc][1] : true) &&
((fromcontrols[gc][1] && gamecontroldefault[i][gc][0]) ? fromcontrols[gc][1] != gamecontroldefault[i][gc][0] : true) &&
((fromcontrols[gc][1] && gamecontroldefault[i][gc][1]) ? fromcontrols[gc][1] != gamecontroldefault[i][gc][1] : true))
{
skipscheme = true;
break;
}
}
if (!skipscheme)
return i;
}
return gcs_custom;
}
void G_CopyControls(INT32 (*setupcontrols)[2], INT32 (*fromcontrols)[2], const INT32 *gclist, INT32 gclen)
{
INT32 i, gc;
for (i = 0; i < (gclist && gclen ? gclen : NUM_GAMECONTROLS); i++)
{
gc = (gclist && gclen) ? gclist[i] : i;
setupcontrols[gc][0] = fromcontrols[gc][0];
setupcontrols[gc][1] = fromcontrols[gc][1];
}
}
void G_SaveKeySetting(FILE *f, INT32 (*fromcontrols)[2], INT32 (*fromcontrolsbis)[2])
{
INT32 i;
for (i = 1; i < NUM_GAMECONTROLS; i++)
{
fprintf(f, "setcontrol \"%s\" \"%s\"", gamecontrolname[i],
G_KeyNumToName(fromcontrols[i][0]));
if (fromcontrols[i][1])
fprintf(f, " \"%s\"\n", G_KeyNumToName(fromcontrols[i][1]));
else
fprintf(f, "\n");
}
for (i = 1; i < NUM_GAMECONTROLS; i++)
{
fprintf(f, "setcontrol2 \"%s\" \"%s\"", gamecontrolname[i],
G_KeyNumToName(fromcontrolsbis[i][0]));
if (fromcontrolsbis[i][1])
fprintf(f, " \"%s\"\n", G_KeyNumToName(fromcontrolsbis[i][1]));
else
fprintf(f, "\n");
}
}
INT32 G_CheckDoubleUsage(INT32 keynum, boolean modify)
{
INT32 result = GC_NULL;
if (cv_controlperkey.value == 1)
{
INT32 i;
for (i = 0; i < NUM_GAMECONTROLS; i++)
{
if (gamecontrol[i][0] == keynum)
{
result = i;
if (modify) gamecontrol[i][0] = KEY_NULL;
}
if (gamecontrol[i][1] == keynum)
{
result = i;
if (modify) gamecontrol[i][1] = KEY_NULL;
}
if (gamecontrolbis[i][0] == keynum)
{
result = i;
if (modify) gamecontrolbis[i][0] = KEY_NULL;
}
if (gamecontrolbis[i][1] == keynum)
{
result = i;
if (modify) gamecontrolbis[i][1] = KEY_NULL;
}
if (result && !modify)
return result;
}
}
return result;
}
static INT32 G_FilterKeyByVersion(INT32 numctrl, INT32 keyidx, INT32 player, INT32 *keynum1, INT32 *keynum2, boolean *nestedoverride)
{
// Special case: ignore KEY_PAUSE because it's hardcoded
if (keyidx == 0 && *keynum1 == KEY_PAUSE)
{
if (*keynum2 != KEY_PAUSE)
{
*keynum1 = *keynum2; // shift down keynum2 and continue
*keynum2 = 0;
}
else
return -1; // skip setting control
}
else if (keyidx == 1 && *keynum2 == KEY_PAUSE)
return -1; // skip setting control
if (GETMAJOREXECVERSION(cv_execversion.value) < 27 && ( // v2.1.22
numctrl == GC_WEAPONNEXT || numctrl == GC_WEAPONPREV || numctrl == GC_TOSSFLAG ||
numctrl == GC_SPIN || numctrl == GC_CAMRESET || numctrl == GC_JUMP ||
numctrl == GC_PAUSE || numctrl == GC_SYSTEMMENU || numctrl == GC_CAMTOGGLE ||
numctrl == GC_SCREENSHOT || numctrl == GC_TALKKEY || numctrl == GC_SCORES ||
numctrl == GC_CENTERVIEW
))
{
INT32 keynum = 0, existingctrl = 0;
INT32 defaultkey;
boolean defaultoverride = false;
// get the default gamecontrol
if (player == 0 && numctrl == GC_SYSTEMMENU)
defaultkey = gamecontrol[numctrl][0];
else
defaultkey = (player == 1 ? gamecontrolbis[numctrl][0] : gamecontrol[numctrl][1]);
// Assign joypad button defaults if there is an open slot.
// At this point, gamecontrol/bis should have the default controls
// (unless LOADCONFIG is being run)
//
// If the player runs SETCONTROL in-game, this block should not be reached
// because EXECVERSION is locked onto the latest version.
if (keyidx == 0 && !*keynum1)
{
if (*keynum2) // push keynum2 down; this is an edge case
{
*keynum1 = *keynum2;
*keynum2 = 0;
keynum = *keynum1;
}
else
{
keynum = defaultkey;
defaultoverride = true;
}
}
else if (keyidx == 1 && (!*keynum2 || (!*keynum1 && *keynum2))) // last one is the same edge case as above
{
keynum = defaultkey;
defaultoverride = true;
}
else // default to the specified keynum
keynum = (keyidx == 1 ? *keynum2 : *keynum1);
// Did our last call override keynum2?
if (*nestedoverride)
{
defaultoverride = true;
*nestedoverride = false;
}
// Fill keynum2 with the default control
if (keyidx == 0 && !*keynum2)
{
*keynum2 = defaultkey;
// Tell the next call that this is an override
*nestedoverride = true;
// if keynum2 already matches keynum1, we probably recursed
// so unset it
if (*keynum1 == *keynum2)
{
*keynum2 = 0;
*nestedoverride = false;
}
}
// check if the key is being used somewhere else before passing it
// pass it through if it's the same numctrl. This is an edge case -- when using
// LOADCONFIG, gamecontrol is not reset with default.
//
// Also, only check if we're actually overriding, to preserve behavior where
// config'd keys overwrite default keys.
if (defaultoverride)
existingctrl = G_CheckDoubleUsage(keynum, false);
if (keynum && (!existingctrl || existingctrl == numctrl))
return keynum;
else if (keyidx == 0 && *keynum2)
{
// try it again and push down keynum2
*keynum1 = *keynum2;
*keynum2 = 0;
return G_FilterKeyByVersion(numctrl, keyidx, player, keynum1, keynum2, nestedoverride);
// recursion *should* be safe because we only assign keynum2 to a joy default
// and then clear it if we find that keynum1 already has the joy default.
}
else
return 0;
}
// All's good, so pass the keynum as-is
if (keyidx == 1)
return *keynum2;
else //if (keyidx == 0)
return *keynum1;
}
static void setcontrol(INT32 (*gc)[2])
{
INT32 numctrl;
const char *namectrl;
INT32 keynum, keynum1, keynum2;
INT32 player = ((void*)gc == (void*)&gamecontrolbis ? 1 : 0);
boolean nestedoverride = false;
// Update me for 2.3
namectrl = (stricmp(COM_Argv(1), "use")) ? COM_Argv(1) : "spin";
for (numctrl = 0; numctrl < NUM_GAMECONTROLS && stricmp(namectrl, gamecontrolname[numctrl]);
numctrl++)
;
if (numctrl == NUM_GAMECONTROLS)
{
CONS_Printf(M_GetText("Control '%s' unknown\n"), namectrl);
return;
}
keynum1 = G_KeyNameToNum(COM_Argv(2));
keynum2 = G_KeyNameToNum(COM_Argv(3));
keynum = G_FilterKeyByVersion(numctrl, 0, player, &keynum1, &keynum2, &nestedoverride);
if (keynum >= 0)
{
(void)G_CheckDoubleUsage(keynum, true);
// if keynum was rejected, try it again with keynum2
if (!keynum && keynum2)
{
keynum1 = keynum2; // push down keynum2
keynum2 = 0;
keynum = G_FilterKeyByVersion(numctrl, 0, player, &keynum1, &keynum2, &nestedoverride);
if (keynum >= 0)
(void)G_CheckDoubleUsage(keynum, true);
}
}
if (keynum >= 0)
gc[numctrl][0] = keynum;
if (keynum2)
{
keynum = G_FilterKeyByVersion(numctrl, 1, player, &keynum1, &keynum2, &nestedoverride);
if (keynum >= 0)
{
if (keynum != gc[numctrl][0])
gc[numctrl][1] = keynum;
else
gc[numctrl][1] = 0;
}
}
else
gc[numctrl][1] = 0;
}
void Command_Setcontrol_f(void)
{
INT32 na;
na = (INT32)COM_Argc();
if (na != 3 && na != 4)
{
CONS_Printf(M_GetText("setcontrol <controlname> <keyname> [<2nd keyname>]: set controls for player 1\n"));
return;
}
setcontrol(gamecontrol);
}
void Command_Setcontrol2_f(void)
{
INT32 na;
na = (INT32)COM_Argc();
if (na != 3 && na != 4)
{
CONS_Printf(M_GetText("setcontrol2 <controlname> <keyname> [<2nd keyname>]: set controls for player 2\n"));
return;
}
setcontrol(gamecontrolbis);
}
void G_SetMouseDeltas(INT32 dx, INT32 dy, UINT8 ssplayer)
{
mouse_t *m = ssplayer == 1 ? &mouse : &mouse2;
consvar_t *cvsens, *cvysens;
cvsens = ssplayer == 1 ? &cv_mousesens : &cv_mousesens2;
cvysens = ssplayer == 1 ? &cv_mouseysens : &cv_mouseysens2;
m->rdx = dx;
m->rdy = dy;
m->dx = (INT32)(m->rdx*((cvsens->value*cvsens->value)/110.0f + 0.1f));
m->dy = (INT32)(m->rdy*((cvsens->value*cvsens->value)/110.0f + 0.1f));
m->mlookdy = (INT32)(m->rdy*((cvysens->value*cvsens->value)/110.0f + 0.1f));
}