diff --git a/src/Input.c b/src/Input.c index b49aa89bb..28a9a12e6 100644 --- a/src/Input.c +++ b/src/Input.c @@ -377,38 +377,56 @@ void Pointer_SetPosition(int idx, int x, int y) { /*########################################################################################################################* *---------------------------------------------------------Keybinds--------------------------------------------------------* *#########################################################################################################################*/ -cc_uint8 PadBind_Mappings[BIND_COUNT]; -cc_uint8 KeyBind_Mappings[BIND_COUNT]; +BindMapping PadBind_Mappings[BIND_COUNT]; +BindMapping KeyBind_Mappings[BIND_COUNT]; -const cc_uint8 PadBind_Defaults[BIND_COUNT] = { - CCPAD_UP, CCPAD_DOWN, CCPAD_LEFT, CCPAD_RIGHT, /* Movement */ - CCPAD_A, 0, CCPAD_START, CCPAD_Y, /* Jump, SetSpawn, OpenChat */ - CCPAD_X, 0, CCPAD_START, 0, /* Inventory, EnterChat */ - 0, 0, 0, 0, 0, /* Hacks */ - 0, 0, 0, 0, /* LAlt - F11 */ - 0, 0, 0, 0, /* F5 - C */ - 0, CCPAD_L, 0, CCPAD_R, - 0, 0, 0, - 0,0,0, 0,0,0,0, - 0,0,0, 0,0,0, 0,0,0, /* Hotbar slots */ - CCPAD_ZL, CCPAD_ZR -}; -const cc_uint8 KeyBind_Defaults[BIND_COUNT] = { - 'W', 'S', 'A', 'D', - CCKEY_SPACE, 'R', CCKEY_ENTER, 'T', - 'B', 'F', CCKEY_ENTER, CCKEY_TAB, - CCKEY_LSHIFT, 'X', 'Z', 'Q', 'E', - CCKEY_LALT, CCKEY_F3, CCKEY_F12, CCKEY_F11, - CCKEY_F5, CCKEY_F1, CCKEY_F7, 'C', - CCKEY_LCTRL, CCMOUSE_L, CCMOUSE_M, CCMOUSE_R, - CCKEY_F6, CCKEY_LALT, CCKEY_F8, - 'G', CCKEY_F10, 0, - 0, 0, 0, 0, - '1','2','3', '4','5','6', '7','8','9', - 0, 0 +const BindMapping PadBind_Defaults[BIND_COUNT] = { + { CCPAD_UP, 0 }, { CCPAD_DOWN, 0 }, /* BIND_FORWARD, BIND_BACK */ + { CCPAD_LEFT, 0 }, { CCPAD_RIGHT, 0 }, /* BIND_LEFT, BIND_RIGHT */ + { CCPAD_A, 0 }, { 0, 0 }, /* BIND_JUMP, BIND_RESPAWN */ + { CCPAD_START, 0 }, { CCPAD_Y, 0 }, /* BIND_SET_SPAWN, BIND_CHAT */ + { CCPAD_X, 0 }, { 0, 0 }, /* BIND_INVENTORY, BIND_FOG */ + { CCPAD_START, 0 }, { 0, 0 }, /* BIND_SEND_CHAT, BIND_TABLIST */ + { CCPAD_B, CCPAD_L},{ CCPAD_B, CCPAD_X},/* BIND_SPEED, BIND_NOCLIP */ + { CCPAD_B, CCPAD_R }, /* BIND_FLY */ + {CCPAD_B,CCPAD_UP},{CCPAD_B,CCPAD_DOWN},/* BIND_FLY_UP, BIND_FLY_DOWN */ + { 0, 0 }, { 0, 0 }, /* BIND_EXT_INPUT, BIND_HIDE_FPS */ + { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, /* BIND_SCREENSHOT, BIND_FULLSCREEN, BIND_THIRD_PERSON, BIND_HIDE_GUI */ + { 0, 0 }, { 0, 0 }, { 0, 0 }, /* BIND_AXIS_LINES, BIND_ZOOM_SCROLL, BIND_HALF_SPEED */ + { CCPAD_L, 0 }, { 0, 0 },{ CCPAD_R, 0 },/* BIND_DELETE_BLOCK, BIND_PICK_BLOCK, BIND_PLACE_BLOCK */ + { 0, 0 }, { 0, 0 }, { 0, 0 }, /* BIND_AUTOROTATE, BIND_HOTBAR_SWITCH, BIND_SMOOTH_CAMERA */ + { 0, 0 }, { 0, 0 }, { 0, 0 }, /* BIND_DROP_BLOCK, BIND_IDOVERLAY, BIND_BREAK_LIQUIDS */ + { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, /* BIND_LOOK_UP, BIND_LOOK_DOWN, BIND_LOOK_RIGHT, BIND_LOOK_LEFT */ + { 0, 0 }, { 0, 0 }, { 0, 0 }, /* BIND_HOTBAR_1, BIND_HOTBAR_2, BIND_HOTBAR_3 */ + { 0, 0 }, { 0, 0 }, { 0, 0 }, /* BIND_HOTBAR_4, BIND_HOTBAR_5, BIND_HOTBAR_6 */ + { 0, 0 }, { 0, 0 }, { 0, 0 }, /* BIND_HOTBAR_7, BIND_HOTBAR_8, BIND_HOTBAR_9 */ + { CCPAD_ZL, 0 }, { CCPAD_ZR, 0 } /* BIND_HOTBAR_LEFT, BIND_HOTBAR_RIGHT */ }; -static const char* const keybindNames[BIND_COUNT] = { +const BindMapping KeyBind_Defaults[BIND_COUNT] = { + { 'W', 0 }, { 'S', 0 }, { 'A', 0 }, { 'D', 0 }, /* BIND_FORWARD - BIND_RIGHT */ + { CCKEY_SPACE, 0 }, { 'R', 0 }, /* BIND_JUMP, BIND_RESPAWN */ + { CCKEY_ENTER, 0 }, { 'T', 0 }, /* BIND_SET_SPAWN, BIND_CHAT */ + { 'B', 0 }, { 'F', 0 }, /* BIND_INVENTORY, BIND_FOG */ + { CCKEY_ENTER, 0 }, { CCKEY_TAB, 0 }, /* BIND_SEND_CHAT, BIND_TABLIST */ + { CCKEY_LSHIFT, 0 }, { 'X', 0}, { 'Z', 0 }, /* BIND_SPEED, BIND_NOCLIP, BIND_FLY */ + { 'Q', 0 }, { 'E', 0 }, /* BIND_FLY_UP, BIND_FLY_DOWN */ + { CCKEY_LALT, 0 }, { CCKEY_F3, 0 }, /* BIND_EXT_INPUT, BIND_HIDE_FPS */ + { CCKEY_F12, 0 }, { CCKEY_F11, 0 }, /* BIND_SCREENSHOT, BIND_FULLSCREEN */ + { CCKEY_F5, 0 }, { CCKEY_F1, 0 }, /* BIND_THIRD_PERSON, BIND_HIDE_GUI */ + { CCKEY_F7, 0 }, { 'C', 0 }, { CCKEY_LCTRL, 0 },/* BIND_AXIS_LINES, BIND_ZOOM_SCROLL, BIND_HALF_SPEED */ + { CCMOUSE_L, 0},{ CCMOUSE_M, 0},{ CCMOUSE_R, 0},/* BIND_DELETE_BLOCK, BIND_PICK_BLOCK, BIND_PLACE_BLOCK */ + { CCKEY_F6, 0 }, { CCKEY_LALT, 0 }, /* BIND_AUTOROTATE, BIND_HOTBAR_SWITCH */ + { CCKEY_F8, 0 }, { 'G', 0 }, /* BIND_SMOOTH_CAMERA, BIND_DROP_BLOCK */ + { CCKEY_F10, 0 }, { 0, 0 }, /* BIND_IDOVERLAY, BIND_BREAK_LIQUIDS */ + { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, /* BIND_LOOK_UP, BIND_LOOK_DOWN, BIND_LOOK_RIGHT, BIND_LOOK_LEFT */ + { '1', 0 }, { '2', 0 }, { '3', 0 }, /* BIND_HOTBAR_1, BIND_HOTBAR_2, BIND_HOTBAR_3 */ + { '4', 0 }, { '5', 0 }, { '6', 0 }, /* BIND_HOTBAR_4, BIND_HOTBAR_5, BIND_HOTBAR_6 */ + { '7', 0 }, { '8', 0 }, { '9', 0 }, /* BIND_HOTBAR_7, BIND_HOTBAR_8, BIND_HOTBAR_9 */ + { 0, 0 }, { 0, 0 } /* BIND_HOTBAR_LEFT, BIND_HOTBAR_RIGHT */ +}; + +static const char* const bindNames[BIND_COUNT] = { "Forward", "Back", "Left", "Right", "Jump", "Respawn", "SetSpawn", "Chat", "Inventory", "ToggleFog", "SendChat", "PlayerList", @@ -425,55 +443,120 @@ static const char* const keybindNames[BIND_COUNT] = { "HotbarLeft", "HotbarRight" }; + +#define BindMapping2_Claims(mapping, btn) (Input.Pressed[(mapping)->button1] && (mapping)->button2 == btn) +static cc_bool Mappings_DoesClaim(InputBind binding, int btn, BindMapping* mappings) { + BindMapping* bind = &mappings[binding]; + int i; + if (bind->button2) return BindMapping2_Claims(bind, btn); + + /* Two button mapping takes priority over one button mapping */ + for (i = 0; i < BIND_COUNT; i++) + { + if (mappings[i].button2 && BindMapping2_Claims(&mappings[i], btn)) return false; + } + return bind->button1 == btn; +} + +static cc_bool Mappings_IsPressed(InputBind binding, BindMapping* mappings) { + BindMapping* bind = &mappings[binding]; + int btn = bind->button1; + int i; + + if (!Input.Pressed[btn]) return false; + if (bind->button2) return Input.Pressed[bind->button2]; + + /* Two button mappings to the button takes priority one button mapping */ + for (i = 0; i < BIND_COUNT; i++) + { + bind = &mappings[i]; + if (!bind->button2) continue; + if (!(bind->button1 == btn || bind->button2 == btn)) continue; + + if (Input.Pressed[bind->button1] && Input.Pressed[bind->button2]) return false; + } + return true; +} + + cc_bool InputBind_Claims(InputBind binding, int btn) { - return KeyBind_Mappings[binding] == btn || PadBind_Mappings[binding] == btn; + return Mappings_DoesClaim(binding, btn, KeyBind_Mappings) || + Mappings_DoesClaim(binding, btn, PadBind_Mappings); } -cc_bool InputBind_IsPressed(InputBind binding) { - return Input.Pressed[KeyBind_Mappings[binding]] || - Input.Pressed[PadBind_Mappings[binding]]; +cc_bool InputBind_IsPressed(InputBind binding) { + return Mappings_IsPressed(binding, KeyBind_Mappings) || + Mappings_IsPressed(binding, PadBind_Mappings); } -static void KeyBind_Load(const char* prefix, cc_uint8* keybinds, const cc_uint8* defaults) { +static void KeyBind_Load(const char* prefix, BindMapping* keybinds, const BindMapping* defaults) { cc_string name; char nameBuffer[STRING_SIZE + 1]; - int mapping, i; + BindMapping mapping; + cc_string str, part1, part2; + int i; String_InitArray_NT(name, nameBuffer); for (i = 0; i < BIND_COUNT; i++) { name.length = 0; - String_Format1(&name, prefix, keybindNames[i]); + String_Format1(&name, prefix, bindNames[i]); name.buffer[name.length] = '\0'; - - mapping = Options_GetEnum(name.buffer, defaults[i], storageNames, INPUT_COUNT); - if (mapping == CCKEY_ESCAPE) mapping = defaults[i]; + if (!Options_UNSAFE_Get(name.buffer, &str)) { + keybinds[i] = defaults[i]; + continue; + } + + String_UNSAFE_Separate(&str, ',', &part1, &part2); + mapping.button1 = Utils_ParseEnum(&part1, defaults[i].button1, storageNames, INPUT_COUNT); + mapping.button2 = Utils_ParseEnum(&part2, defaults[i].button2, storageNames, INPUT_COUNT); + + if (mapping.button1 == CCKEY_ESCAPE) mapping = defaults[i]; keybinds[i] = mapping; } } -static void InputBind_Set(InputBind binding, int key, cc_uint8* binds, const char* fmt) { +static void InputBind_Set(InputBind binding, int btn, BindMapping* binds, const char* fmt) { cc_string name; char nameBuffer[STRING_SIZE]; cc_string value; String_InitArray(name, nameBuffer); - String_Format1(&name, fmt, keybindNames[binding]); - value = String_FromReadonly(storageNames[key]); + String_Format1(&name, fmt, bindNames[binding]); + value = String_FromReadonly(storageNames[btn]); Options_SetString(&name, &value); - binds[binding] = key; + + BindMapping_Set(&binds[binding], btn, 0); } -void KeyBind_Set(InputBind binding, int key) { - InputBind_Set(binding, key, KeyBind_Mappings, "key-%c"); +void KeyBind_Set(InputBind binding, int btn) { + InputBind_Set(binding, btn, KeyBind_Mappings, "key-%c"); } -void PadBind_Set(InputBind binding, int key) { - InputBind_Set(binding, key, PadBind_Mappings, "pad-%c"); +void PadBind_Set(InputBind binding, int btn) { + InputBind_Set(binding, btn, PadBind_Mappings, "pad-%c"); +} + +static void InputBind_ResetOption(InputBind binding, const char* fmt) { + cc_string name; char nameBuffer[STRING_SIZE]; + String_InitArray(name, nameBuffer); + + String_Format1(&name, fmt, bindNames[binding]); + Options_SetString(&name, &String_Empty); +} + +void KeyBind_Reset(InputBind binding) { + InputBind_ResetOption(binding, "key-%c"); + KeyBind_Mappings[binding] = KeyBind_Defaults[binding]; +} + +void PadBind_Reset(InputBind binding) { + InputBind_ResetOption(binding, "pad-%c"); + PadBind_Mappings[binding] = PadBind_Defaults[binding]; } /* Initialises and loads input bindings from options */ static void KeyBind_Init(void) { - KeyBind_Load("key-%c", KeyBind_Mappings, KeyBind_Defaults); + KeyBind_Load("key-%c", KeyBind_Mappings, KeyBind_Defaults); KeyBind_Load("pad-%c", PadBind_Mappings, PadBind_Defaults); } diff --git a/src/Input.h b/src/Input.h index 9aabf09ca..0d5efb5f0 100644 --- a/src/Input.h +++ b/src/Input.h @@ -174,24 +174,31 @@ enum InputBind_ { BIND_COUNT }; typedef int InputBind; +typedef struct BindMapping_ { cc_uint8 button1, button2; } BindMapping; +#define BindMapping_Set(mapping, btn1, btn2) (mapping)->button1 = btn1; (mapping)->button2 = btn2; /* The keyboard/mouse buttons that are bound to each input binding */ -extern cc_uint8 KeyBind_Mappings[BIND_COUNT]; +extern BindMapping KeyBind_Mappings[BIND_COUNT]; /* The gamepad buttons that are bound to each input binding */ -extern cc_uint8 PadBind_Mappings[BIND_COUNT]; +extern BindMapping PadBind_Mappings[BIND_COUNT]; /* Default keyboard/mouse button that each input binding is bound to */ -extern const cc_uint8 KeyBind_Defaults[BIND_COUNT]; +extern const BindMapping KeyBind_Defaults[BIND_COUNT]; /* Default gamepad button that each input binding is bound to */ -extern const cc_uint8 PadBind_Defaults[BIND_COUNT]; +extern const BindMapping PadBind_Defaults[BIND_COUNT]; /* Whether the given binding should be triggered in response to given input button being pressed */ CC_API cc_bool InputBind_Claims(InputBind binding, int btn); /* Gets whether the given input binding is currently being triggered */ CC_API cc_bool InputBind_IsPressed(InputBind binding); -/* Sets the key/mouse button that the given input binding is bound to (Also updates options list) */ -void KeyBind_Set(InputBind binding, int key); -/* Sets the gamepad button that the given input binding is bound to (Also updates options list) */ -void PadBind_Set(InputBind binding, int key); + +/* Sets the key/mouse button that the given input binding is bound to */ +void KeyBind_Set(InputBind binding, int btn); +/* Sets the gamepad button that the given input binding is bound to */ +void PadBind_Set(InputBind binding, int btn); +/* Resets the key/mouse button that the given input binding is bound to */ +void KeyBind_Reset(InputBind binding); +/* Resets the gamepad button that the given input binding is bound to*/ +void PadBind_Reset(InputBind binding); /* Gamepad axes. Default behaviour is: */ diff --git a/src/Menus.c b/src/Menus.c index baa8238d6..00b2f0627 100644 --- a/src/Menus.c +++ b/src/Menus.c @@ -1945,13 +1945,19 @@ static struct Widget* key_widgets[KEYBINDS_MAX_BTNS + 5]; static void KeyBindsScreen_Update(struct KeyBindsScreen* s, int i) { cc_string text; char textBuffer[STRING_SIZE]; - const cc_uint8* curBinds; + const BindMapping* curBinds; + BindMapping curBind; String_InitArray(text, textBuffer); curBinds = binds_gamepad ? PadBind_Mappings : KeyBind_Mappings; + curBind = curBinds[s->binds[i]]; - String_Format2(&text, s->curI == i ? "> %c: %c <" : "%c: %c", - s->descs[i], Input_DisplayNames[curBinds[s->binds[i]]]); + String_Format4(&text, s->curI == i ? "> %c: %c%c%c <" : "%c: %c%c%c", + s->descs[i], + Input_DisplayNames[curBind.button1], + curBind.button2 ? " + " : "", + curBind.button2 ? Input_DisplayNames[curBind.button2] : ""); + ButtonWidget_Set(&s->buttons[i], &text, &s->titleFont); s->dirty = true; } @@ -1969,22 +1975,35 @@ static void KeyBindsScreen_OnBindingClick(void* screen, void* widget) { if (old >= 0) KeyBindsScreen_Update(s, old); } -static int KeyBindsScreen_KeyDown(void* screen, int key) { - struct KeyBindsScreen* s = (struct KeyBindsScreen*)screen; - const cc_uint8* defaults; - InputBind bind; - int idx; +static void KeyBindsScreen_ResetBinding(InputBind bind) { + if (binds_gamepad) { + PadBind_Reset(bind); + } else { + KeyBind_Reset(bind); + } +} - if (s->curI == -1) return Menu_InputDown(s, key); - defaults = binds_gamepad ? PadBind_Defaults : KeyBind_Defaults; - bind = s->binds[s->curI]; - if (Input_IsEscapeButton(key)) key = defaults[bind]; - +static void KeyBindsScreen_UpdateBinding(InputBind bind, int key) { if (binds_gamepad) { PadBind_Set(bind, key); } else { KeyBind_Set(bind, key); } +} + +static int KeyBindsScreen_KeyDown(void* screen, int key) { + struct KeyBindsScreen* s = (struct KeyBindsScreen*)screen; + InputBind bind; + int idx; + + if (s->curI == -1) return Menu_InputDown(s, key); + bind = s->binds[s->curI]; + + if (Input_IsEscapeButton(key)) { + KeyBindsScreen_ResetBinding(bind); + } else { + KeyBindsScreen_UpdateBinding(bind, key); + } idx = s->curI; s->curI = -1; diff --git a/src/Screens.c b/src/Screens.c index 828ada995..8a5c46b56 100644 --- a/src/Screens.c +++ b/src/Screens.c @@ -1313,7 +1313,7 @@ static int ChatScreen_TextChanged(void* screen, const cc_string* str) { static int ChatScreen_KeyDown(void* screen, int key) { static const cc_string slash = String_FromConst("/"); struct ChatScreen* s = (struct ChatScreen*)screen; - int playerListKey = KeyBind_Mappings[BIND_TABLIST]; + int playerListKey = KeyBind_Mappings[BIND_TABLIST].button1; cc_bool handlesList = playerListKey != CCKEY_TAB || !Gui.TabAutocomplete || !s->grabsInput; if (InputBind_Claims(BIND_TABLIST, key) && handlesList) { diff --git a/src/Window_N64.c b/src/Window_N64.c index 2f0d385fb..029105a87 100644 --- a/src/Window_N64.c +++ b/src/Window_N64.c @@ -37,22 +37,22 @@ void Window_Init(void) { joypad_init(); // change defaults to make more sense for N64 - cc_uint8* binds = (cc_uint8*)PadBind_Defaults; - binds[BIND_JUMP] = CCPAD_A; - binds[BIND_INVENTORY] = CCPAD_B; - binds[BIND_PLACE_BLOCK] = CCPAD_Z; - binds[BIND_HOTBAR_RIGHT] = CCPAD_L; - binds[BIND_DELETE_BLOCK] = CCPAD_R; + BindMapping* binds = (BindMapping*)PadBind_Defaults; + BindMapping_Set(&binds[BIND_JUMP], CCPAD_A, 0); + BindMapping_Set(&binds[BIND_INVENTORY], CCPAD_B, 0); + BindMapping_Set(&binds[BIND_PLACE_BLOCK], CCPAD_Z, 0); + BindMapping_Set(&binds[BIND_HOTBAR_RIGHT], CCPAD_L, 0); + BindMapping_Set(&binds[BIND_DELETE_BLOCK], CCPAD_R, 0); - binds[BIND_FORWARD] = CCPAD_CUP; - binds[BIND_BACK] = CCPAD_CDOWN; - binds[BIND_LEFT] = CCPAD_CLEFT; - binds[BIND_RIGHT] = CCPAD_CRIGHT; + BindMapping_Set(&binds[BIND_FORWARD], CCPAD_CUP, 0); + BindMapping_Set(&binds[BIND_BACK], CCPAD_CDOWN, 0); + BindMapping_Set(&binds[BIND_LEFT], CCPAD_CLEFT, 0); + BindMapping_Set(&binds[BIND_RIGHT], CCPAD_CRIGHT, 0); - binds[BIND_FLY_UP] = CCPAD_UP; - binds[BIND_FLY_DOWN] = CCPAD_DOWN; - binds[BIND_SPEED] = CCPAD_LEFT; - binds[BIND_FLY] = CCPAD_RIGHT; + BindMapping_Set(&binds[BIND_FLY_UP], CCPAD_UP, 0); + BindMapping_Set(&binds[BIND_FLY_DOWN], CCPAD_DOWN, 0); + BindMapping_Set(&binds[BIND_SPEED], CCPAD_LEFT, 0); + BindMapping_Set(&binds[BIND_FLY], CCPAD_RIGHT, 0); } void Window_Free(void) { }