ref: b2e3ae80dd3e5f152cdf35bf58473b8d6cef0ef4
parent: 0ded93de55bac828276e8a16877d6edf1f924d95
author: Dominic Szablewski <dominic@phoboslab.org>
date: Tue Aug 15 19:40:12 EDT 2023
Add button bindings to save data, add controls menu; close #6
--- 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",
--- a/src/input.h
+++ b/src/input.h
@@ -51,7 +51,7 @@
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 @@
INPUT_KEY_LGUI = 103,
INPUT_KEY_RCTRL = 104,
INPUT_KEY_RSHIFT = 105,
- INPUT_KEY_RALT = 106,
+ INPUT_KEY_RALT = 106,
INPUT_KEY_MAX = 107,
--- a/src/wipeout/game.c
+++ b/src/wipeout/game.c
@@ -404,8 +404,20 @@
.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 @@
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 @@
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);
--- 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 @@
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 @@
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;
--- 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 @@
// -----------------------------------------------------------------------------
// 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 @@
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;