diff --git a/src/input.c b/src/input.c index 0cdfa3a..a4f52a8 100644 --- a/src/input.c +++ b/src/input.c @@ -4,9 +4,9 @@ #include "utils.h" static const char *button_names[] = { - NULL, - NULL, - NULL, + NULL, + NULL, + NULL, NULL, [INPUT_KEY_A] = "A", [INPUT_KEY_B] = "B", diff --git a/src/input.h b/src/input.h index 8fdc446..bd1f227 100644 --- a/src/input.h +++ b/src/input.h @@ -51,7 +51,7 @@ typedef enum { INPUT_KEY_LEFTBRACKET = 47, INPUT_KEY_RIGHTBRACKET = 48, INPUT_KEY_BACKSLASH = 49, - INPUT_KEY_HASH = 50, + INPUT_KEY_HASH = 50, INPUT_KEY_SEMICOLON = 51, INPUT_KEY_APOSTROPHE = 52, INPUT_KEY_TILDE = 53, @@ -108,7 +108,7 @@ typedef enum { INPUT_KEY_LGUI = 103, INPUT_KEY_RCTRL = 104, INPUT_KEY_RSHIFT = 105, - INPUT_KEY_RALT = 106, + INPUT_KEY_RALT = 106, INPUT_KEY_MAX = 107, diff --git a/src/wipeout/game.c b/src/wipeout/game.c index 592a124..b7acadd 100755 --- a/src/wipeout/game.c +++ b/src/wipeout/game.c @@ -404,8 +404,20 @@ save_t save = { .has_rapier_class = true, // for testing; should be false in prod .has_bonus_circuts = true, // for testing; should be false in prod - .highscores_name = {0,0,0,0}, + .buttons = { + [A_UP] = {INPUT_KEY_UP, INPUT_GAMEPAD_DPAD_UP}, + [A_DOWN] = {INPUT_KEY_DOWN, INPUT_GAMEPAD_DPAD_DOWN}, + [A_LEFT] = {INPUT_KEY_LEFT, INPUT_GAMEPAD_DPAD_LEFT}, + [A_RIGHT] = {INPUT_KEY_RIGHT, INPUT_GAMEPAD_DPAD_RIGHT}, + [A_BRAKE_LEFT] = {INPUT_KEY_C, INPUT_GAMEPAD_L_SHOULDER}, + [A_BRAKE_RIGHT] = {INPUT_KEY_V, INPUT_GAMEPAD_R_SHOULDER}, + [A_THRUST] = {INPUT_KEY_X, INPUT_GAMEPAD_A}, + [A_FIRE] = {INPUT_KEY_Z, INPUT_GAMEPAD_X}, + [A_CHANGE_VIEW] = {INPUT_KEY_A, INPUT_GAMEPAD_Y}, + }, + + .highscores_name = {0,0,0,0}, .highscores = { [RACE_CLASS_VENOM] = { { @@ -534,7 +546,7 @@ void game_init() { input_bind(INPUT_LAYER_SYSTEM, INPUT_KEY_X, A_MENU_SELECT); input_bind(INPUT_LAYER_SYSTEM, INPUT_KEY_RETURN, A_MENU_START); - input_bind(INPUT_LAYER_SYSTEM, INPUT_KEY_ESCAPE, A_MENU_START); + input_bind(INPUT_LAYER_SYSTEM, INPUT_KEY_ESCAPE, A_MENU_QUIT); // Gamepad input_bind(INPUT_LAYER_SYSTEM, INPUT_GAMEPAD_DPAD_UP, A_MENU_UP); @@ -552,41 +564,17 @@ void game_init() { input_bind(INPUT_LAYER_SYSTEM, INPUT_GAMEPAD_A, A_MENU_SELECT); input_bind(INPUT_LAYER_SYSTEM, INPUT_GAMEPAD_START, A_MENU_START); + - - - // User defined - // TODO: these should be configurable and stored in the save struct - // Keyboard - input_bind(INPUT_LAYER_USER, INPUT_KEY_UP, A_UP); - input_bind(INPUT_LAYER_USER, INPUT_KEY_DOWN, A_DOWN); - input_bind(INPUT_LAYER_USER, INPUT_KEY_LEFT, A_LEFT); - input_bind(INPUT_LAYER_USER, INPUT_KEY_RIGHT, A_RIGHT); - input_bind(INPUT_LAYER_USER, INPUT_KEY_C, A_BRAKE_LEFT); - input_bind(INPUT_LAYER_USER, INPUT_KEY_V, A_BRAKE_RIGHT); - - input_bind(INPUT_LAYER_USER, INPUT_KEY_X, A_THRUST); - input_bind(INPUT_LAYER_USER, INPUT_KEY_Z, A_FIRE); - input_bind(INPUT_LAYER_USER, INPUT_KEY_A, A_CHANGE_VIEW); - - // Gamepad - input_bind(INPUT_LAYER_USER, INPUT_GAMEPAD_DPAD_UP, A_UP); - input_bind(INPUT_LAYER_USER, INPUT_GAMEPAD_DPAD_DOWN, A_DOWN); - input_bind(INPUT_LAYER_USER, INPUT_GAMEPAD_DPAD_LEFT, A_LEFT); - input_bind(INPUT_LAYER_USER, INPUT_GAMEPAD_DPAD_RIGHT, A_RIGHT); - input_bind(INPUT_LAYER_USER, INPUT_GAMEPAD_L_STICK_UP, A_UP); - input_bind(INPUT_LAYER_USER, INPUT_GAMEPAD_L_STICK_DOWN, A_DOWN); - input_bind(INPUT_LAYER_USER, INPUT_GAMEPAD_L_STICK_LEFT, A_LEFT); - input_bind(INPUT_LAYER_USER, INPUT_GAMEPAD_L_STICK_RIGHT, A_RIGHT); - input_bind(INPUT_LAYER_USER, INPUT_GAMEPAD_L_TRIGGER, A_BRAKE_LEFT); - input_bind(INPUT_LAYER_USER, INPUT_GAMEPAD_R_TRIGGER, A_BRAKE_RIGHT); - input_bind(INPUT_LAYER_USER, INPUT_GAMEPAD_L_SHOULDER, A_BRAKE_LEFT); - input_bind(INPUT_LAYER_USER, INPUT_GAMEPAD_R_SHOULDER, A_BRAKE_RIGHT); - - input_bind(INPUT_LAYER_USER, INPUT_GAMEPAD_A, A_THRUST); - input_bind(INPUT_LAYER_USER, INPUT_GAMEPAD_X, A_FIRE); - input_bind(INPUT_LAYER_USER, INPUT_GAMEPAD_Y, A_CHANGE_VIEW); - input_bind(INPUT_LAYER_USER, INPUT_GAMEPAD_SELECT, A_CHANGE_VIEW); + // User defined, loaded from the save struct + for (int action = 0; action < len(save.buttons); action++) { + if (save.buttons[action][0] != INPUT_INVALID) { + input_bind(INPUT_LAYER_USER, save.buttons[action][0], action); + } + if (save.buttons[action][1] != INPUT_INVALID) { + input_bind(INPUT_LAYER_USER, save.buttons[action][1], action); + } + } game_set_scene(GAME_SCENE_INTRO); diff --git a/src/wipeout/game.h b/src/wipeout/game.h index f506f6e..2634e6c 100755 --- a/src/wipeout/game.h +++ b/src/wipeout/game.h @@ -20,15 +20,6 @@ #define SAVE_DATA_MAGIC 0x64736f77 typedef enum { - A_MENU_UP, - A_MENU_DOWN, - A_MENU_LEFT, - A_MENU_RIGHT, - A_MENU_BACK, - A_MENU_SELECT, - A_MENU_START, - A_MENU_QUIT, - A_UP, A_DOWN, A_LEFT, @@ -38,6 +29,16 @@ typedef enum { A_THRUST, A_FIRE, A_CHANGE_VIEW, + NUM_GAME_ACTIONS, + + A_MENU_UP, + A_MENU_DOWN, + A_MENU_LEFT, + A_MENU_RIGHT, + A_MENU_BACK, + A_MENU_SELECT, + A_MENU_START, + A_MENU_QUIT, } action_t; @@ -247,6 +248,9 @@ typedef struct { uint32_t has_rapier_class; uint32_t has_bonus_circuts; + + uint8_t buttons[NUM_GAME_ACTIONS][2]; + char highscores_name[4]; highscores_t highscores[NUM_RACE_CLASSES][NUM_CIRCUTS][NUM_HIGHSCORE_TABS]; } save_t; diff --git a/src/wipeout/main_menu.c b/src/wipeout/main_menu.c index 3bb6f62..d433d47 100755 --- a/src/wipeout/main_menu.c +++ b/src/wipeout/main_menu.c @@ -2,6 +2,7 @@ #include "../system.h" #include "../mem.h" #include "../platform.h" +#include "../input.h" #include "menu.h" #include "main_menu.h" @@ -133,8 +134,121 @@ static void page_options_init(menu_t *menu) { // ----------------------------------------------------------------------------- // Options Controls +static const char *button_names[NUM_GAME_ACTIONS][2] = {}; +static int control_current_action; +static float await_input_deadline; + +void button_capture(void *user, button_t button, int32_t ascii_char) { + if (button == INPUT_INVALID) { + return; + } + + menu_t *menu = (menu_t *)user; + if (button == INPUT_KEY_ESCAPE) { + input_capture(NULL, NULL); + menu_pop(menu); + return; + } + + int index = button < INPUT_KEY_MAX ? 0 : 1; // joypad or keyboard + + // unbind this button if it's bound anywhere + for (int i = 0; i < len(save.buttons); i++) { + if (save.buttons[i][index] == button) { + save.buttons[i][index] = INPUT_INVALID; + } + } + input_capture(NULL, NULL); + input_bind(INPUT_LAYER_USER, button, control_current_action); + save.buttons[control_current_action][index] = button; + save.is_dirty = true; + menu_pop(menu); +} + +static void page_options_control_set_draw(menu_t *menu, int data) { + float remaining = await_input_deadline - platform_now(); + + menu_page_t *page = &menu->pages[menu->index]; + char remaining_text[2] = { '0' + (uint8_t)clamp(remaining + 1, 0, 3), '\0'}; + vec2i_t pos = vec2i(page->items_pos.x, page->items_pos.y + 24); + ui_draw_text_centered(remaining_text, ui_scaled_pos(page->items_anchor, pos), UI_SIZE_16, UI_COLOR_DEFAULT); + + if (remaining <= 0) { + input_capture(NULL, NULL); + menu_pop(menu); + return; + } +} + +static void page_options_controls_set_init(menu_t *menu, int data) { + control_current_action = data; + await_input_deadline = platform_now() + 3; + + menu_page_t *page = menu_push(menu, "AWAITING INPUT", page_options_control_set_draw); + input_capture(button_capture, menu); +} + + +static void page_options_control_draw(menu_t *menu, int data) { + menu_page_t *page = &menu->pages[menu->index]; + + int left = page->items_pos.x + page->block_width - 100; + int right = page->items_pos.x + page->block_width; + int line_y = page->items_pos.y - 20; + + vec2i_t left_head_pos = vec2i(left - ui_text_width("KEYBOARD", UI_SIZE_8), line_y); + ui_draw_text("KEYBOARD", ui_scaled_pos(page->items_anchor, left_head_pos), UI_SIZE_8, UI_COLOR_DEFAULT); + + vec2i_t right_head_pos = vec2i(right - ui_text_width("JOYSTICK", UI_SIZE_8), line_y); + ui_draw_text("JOYSTICK", ui_scaled_pos(page->items_anchor, right_head_pos), UI_SIZE_8, UI_COLOR_DEFAULT); + line_y += 20; + + for (int action = 0; action < NUM_GAME_ACTIONS; action++) { + rgba_t text_color = UI_COLOR_DEFAULT; + if (action == data) { + text_color = UI_COLOR_ACCENT; + } + + if (save.buttons[action][0] != INPUT_INVALID) { + const char *name = input_button_to_name(save.buttons[action][0]); + if (!name) { + name = "UNKNWN"; + } + vec2i_t pos = vec2i(left - ui_text_width(name, UI_SIZE_8), line_y); + ui_draw_text(name, ui_scaled_pos(page->items_anchor, pos), UI_SIZE_8, text_color); + } + if (save.buttons[action][1] != INPUT_INVALID) { + const char *name = input_button_to_name(save.buttons[action][1]); + if (!name) { + name = "UNKNWN"; + } + vec2i_t pos = vec2i(right - ui_text_width(name, UI_SIZE_8), line_y); + ui_draw_text(name, ui_scaled_pos(page->items_anchor, pos), UI_SIZE_8, text_color); + } + line_y += 12; + } +} + static void page_options_controls_init(menu_t *menu) { - menu_page_t *page = menu_push(menu, "TODO", page_options_draw); + menu_page_t *page = menu_push(menu, "CONTROLS", page_options_control_draw); + flags_set(page->layout_flags, MENU_VERTICAL | MENU_FIXED); + page->title_pos = vec2i(-160, -100); + page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER; + page->items_pos = vec2i(-160, -50); + page->block_width = 320; + page->items_anchor = UI_POS_MIDDLE | UI_POS_CENTER; + + // const char *thrust_name = button_name(A_THRUST); + // printf("thrust: %s\n", thrust_name); + menu_page_add_button(page, A_UP, "UP", page_options_controls_set_init); + menu_page_add_button(page, A_DOWN, "DOWN", page_options_controls_set_init); + menu_page_add_button(page, A_LEFT, "LEFT", page_options_controls_set_init); + menu_page_add_button(page, A_RIGHT, "RIGHT", page_options_controls_set_init); + menu_page_add_button(page, A_BRAKE_LEFT, "BRAKE L", page_options_controls_set_init); + menu_page_add_button(page, A_BRAKE_RIGHT, "BRAKE R", page_options_controls_set_init); + menu_page_add_button(page, A_THRUST, "THRUST", page_options_controls_set_init); + menu_page_add_button(page, A_FIRE, "FIRE", page_options_controls_set_init); + menu_page_add_button(page, A_CHANGE_VIEW, "VIEW", page_options_controls_set_init); } // ----------------------------------------------------------------------------- @@ -178,7 +292,7 @@ static void page_options_video_init(menu_t *menu) { flags_set(page->layout_flags, MENU_VERTICAL | MENU_FIXED); page->title_pos = vec2i(-160, -100); page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER; - page->items_pos = vec2i(-160, -80); + page->items_pos = vec2i(-160, -60); page->block_width = 320; page->items_anchor = UI_POS_MIDDLE | UI_POS_CENTER;