Propagate keybind changes

main
En Yi 2025-08-23 14:13:58 +08:00
parent 16de3bb70e
commit 44a1eba4de
7 changed files with 158 additions and 55 deletions

View File

@ -48,6 +48,18 @@ int get_keybind_or_default(GameEngine_t* engine, ActionType_t action, int defaul
return key; return key;
} }
void remap_scene_keys(GameEngine_t* engine)
{
// Assume all scene to be init'd
for(size_t i = 0; i < engine->max_scenes; ++i)
{
if (engine->scenes[i]->action_remap_function != NULL)
{
engine->scenes[i]->action_remap_function(engine->scenes[i]);
}
}
}
void process_inputs(GameEngine_t* engine, Scene_t* scene) void process_inputs(GameEngine_t* engine, Scene_t* scene)
{ {
engine->last_input_key = KEY_NULL; engine->last_input_key = KEY_NULL;
@ -121,6 +133,17 @@ void process_inputs(GameEngine_t* engine, Scene_t* scene)
} }
} }
Scene_t* change_scene_ext(GameEngine_t* engine, unsigned int idx, bool reset)
{
Scene_t * scene = change_active_scene(engine, idx);
if (reset && scene->reset_function != NULL)
{
scene->reset_function(scene);
}
return scene;
}
Scene_t* change_scene(GameEngine_t* engine, unsigned int idx) Scene_t* change_scene(GameEngine_t* engine, unsigned int idx)
{ {
// Backwards compat // Backwards compat
@ -201,6 +224,7 @@ void init_scene(Scene_t* scene, action_func_t action_func, uint32_t subsystem_in
scene->bg_colour = WHITE; scene->bg_colour = WHITE;
scene->action_function = action_func; scene->action_function = action_func;
scene->reset_function = NULL;
scene->state = SCENE_COMPLETE_ACTIVE; scene->state = SCENE_COMPLETE_ACTIVE;
scene->time_scale = 1.0f; scene->time_scale = 1.0f;
} }
@ -448,6 +472,8 @@ void remove_child_scene(GameEngine_t* engine, unsigned int idx)
Scene_t* change_active_scene(GameEngine_t* engine, unsigned int idx) Scene_t* change_active_scene(GameEngine_t* engine, unsigned int idx)
{ {
assert(idx < engine->max_scenes);
engine->scenes[engine->curr_scene]->state = 0; engine->scenes[engine->curr_scene]->state = 0;
engine->curr_scene = idx; engine->curr_scene = idx;
engine->scenes[engine->curr_scene]->state = SCENE_COMPLETE_ACTIVE; engine->scenes[engine->curr_scene]->state = SCENE_COMPLETE_ACTIVE;

View File

@ -31,7 +31,10 @@ typedef struct GameEngine {
SFXList_t sfx_list; SFXList_t sfx_list;
// Maintain own queue to handle key presses // Maintain own queue to handle key presses
Scene_t* focused_scene; // The one scene to receive key inputs Scene_t* focused_scene; // The one scene to receive key inputs
struct sc_map_64 keybinds; // Global action -> key mapping
// Global action->key map. Does not hold previous key
struct sc_map_64 keybinds;
struct sc_queue_32 key_buffer; struct sc_queue_32 key_buffer;
struct sc_queue_ptr scene_stack; struct sc_queue_ptr scene_stack;
struct sc_heap scenes_render_order; struct sc_heap scenes_render_order;
@ -70,9 +73,12 @@ struct Scene {
EntityManager_t ent_manager; EntityManager_t ent_manager;
Scene_t* parent_scene; Scene_t* parent_scene;
struct sc_map_64 action_map; // key -> actions struct sc_map_64 action_map; // key -> actions
struct sc_array_systems systems; struct sc_array_systems systems;
SceneRenderLayers_t layers; SceneRenderLayers_t layers;
Color bg_colour; Color bg_colour;
system_func_t reset_function;
system_func_t action_remap_function;
action_func_t action_function; action_func_t action_function;
float delta_time; float delta_time;
float time_scale; float time_scale;
@ -91,14 +97,22 @@ void process_inputs(GameEngine_t* engine, Scene_t* scene);
// Register a key bind. If already exists, will override. // Register a key bind. If already exists, will override.
// Does not support multi key to an action // Does not support multi key to an action
// Register keybind should be done before scene inits. This lets the engine
// know what are the available keybinds for the game runtime.
//
void register_keybind(GameEngine_t* engine, int key, ActionType_t action); void register_keybind(GameEngine_t* engine, int key, ActionType_t action);
int get_keybind_or_default(GameEngine_t* engine, ActionType_t action, int default_key); int get_keybind_or_default(GameEngine_t* engine, ActionType_t action, int default_key);
// If a key bind gets changed, this must be called to update the
// key -> action mapping for all scene
void remap_scene_keys(GameEngine_t* engine);
void process_active_scene_inputs(GameEngine_t* engine); void process_active_scene_inputs(GameEngine_t* engine);
void update_curr_scene(GameEngine_t* engine); void update_curr_scene(GameEngine_t* engine);
void render_curr_scene(GameEngine_t* engine); void render_curr_scene(GameEngine_t* engine);
Scene_t* change_scene(GameEngine_t* engine, unsigned int idx); Scene_t* change_scene(GameEngine_t* engine, unsigned int idx);
Scene_t* change_scene_ext(GameEngine_t* engine, unsigned int idx, bool reset);
Scene_t* change_active_scene(GameEngine_t* engine, unsigned int idx); Scene_t* change_active_scene(GameEngine_t* engine, unsigned int idx);
void change_focused_scene(GameEngine_t* engine, unsigned int idx); void change_focused_scene(GameEngine_t* engine, unsigned int idx);
bool load_sfx(GameEngine_t* engine, const char* snd_name, uint32_t tag_idx); bool load_sfx(GameEngine_t* engine, const char* snd_name, uint32_t tag_idx);

View File

@ -518,6 +518,20 @@ static void at_level_complete(Scene_t* scene)
} }
} }
static void game_action_remap_func(Scene_t* scene)
{
sc_map_clear_64(&scene->action_map);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_UP, KEY_UP), ACTION_UP);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_DOWN, KEY_DOWN), ACTION_DOWN);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_LEFT, KEY_LEFT), ACTION_LEFT);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_RIGHT, KEY_RIGHT), ACTION_RIGHT);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_JUMP, KEY_ENTER), ACTION_JUMP);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_EXIT, KEY_Q), ACTION_EXIT);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_LOOKAHEAD, KEY_Z), ACTION_LOOKAHEAD);
sc_map_put_64(&scene->action_map, KEY_R, ACTION_RESTART);
}
void init_game_scene(LevelScene_t* scene) void init_game_scene(LevelScene_t* scene)
{ {
init_scene(&scene->scene, &level_do_action, ENABLE_ENTITY_MANAGEMENT_SYSTEM | ENABLE_PARTICLE_SYSTEM); init_scene(&scene->scene, &level_do_action, ENABLE_ENTITY_MANAGEMENT_SYSTEM | ENABLE_PARTICLE_SYSTEM);
@ -526,6 +540,7 @@ void init_game_scene(LevelScene_t* scene)
init_entity_tag_map(&scene->scene.ent_manager, LEVEL_END_TAG, 16); init_entity_tag_map(&scene->scene.ent_manager, LEVEL_END_TAG, 16);
init_entity_tag_map(&scene->scene.ent_manager, DYNMEM_ENT_TAG, 16); init_entity_tag_map(&scene->scene.ent_manager, DYNMEM_ENT_TAG, 16);
scene->scene.action_remap_function = &game_action_remap_func;
scene->data.tilemap.tiles = all_tiles; scene->data.tilemap.tiles = all_tiles;
scene->data.tilemap.render_nodes = all_tile_rendernodes; scene->data.tilemap.render_nodes = all_tile_rendernodes;
init_level_scene_data( init_level_scene_data(
@ -600,15 +615,7 @@ void init_game_scene(LevelScene_t* scene)
// This avoid graphical glitch, not essential // This avoid graphical glitch, not essential
//sc_array_add(&scene->scene.systems, &update_tilemap_system); //sc_array_add(&scene->scene.systems, &update_tilemap_system);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_UP, KEY_UP), ACTION_UP); game_action_remap_func(&scene->scene);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_DOWN, KEY_DOWN), ACTION_DOWN);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_LEFT, KEY_LEFT), ACTION_LEFT);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_RIGHT, KEY_RIGHT), ACTION_RIGHT);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_JUMP, KEY_ENTER), ACTION_JUMP);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_EXIT, KEY_Q), ACTION_EXIT);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_LOOKAHEAD, KEY_Z), ACTION_LOOKAHEAD);
sc_map_put_64(&scene->scene.action_map, KEY_R, ACTION_RESTART);
} }
void free_game_scene(LevelScene_t* scene) void free_game_scene(LevelScene_t* scene)

View File

@ -245,6 +245,18 @@ static void level_select_do_action(Scene_t* scene, ActionType_t action, bool pre
} }
} }
static void level_select_action_remap_func(Scene_t* scene)
{
sc_map_clear_64(&scene->action_map);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_UP, KEY_UP), ACTION_UP);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_DOWN, KEY_DOWN), ACTION_DOWN);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_JUMP, KEY_ENTER), ACTION_CONFIRM);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_EXIT, KEY_Q), ACTION_EXIT);
sc_map_put_64(&scene->action_map, KEY_BACKSPACE, ACTION_EXIT);
sc_map_put_64(&scene->action_map, KEY_ENTER, ACTION_CONFIRM);
}
#define FONT_SIZE 22 #define FONT_SIZE 22
#define TEXT_PADDING 3 #define TEXT_PADDING 3
#define SCROLL_TOTAL_HEIGHT 800 #define SCROLL_TOTAL_HEIGHT 800
@ -253,6 +265,7 @@ void init_level_select_scene(LevelSelectScene_t* scene)
init_scene(&scene->scene, &level_select_do_action, 0); init_scene(&scene->scene, &level_select_do_action, 0);
scene->data.preview = LoadRenderTexture(LEVEL_PREVIEW_SIZE, LEVEL_PREVIEW_SIZE); scene->data.preview = LoadRenderTexture(LEVEL_PREVIEW_SIZE, LEVEL_PREVIEW_SIZE);
scene->data.update_preview = true; scene->data.update_preview = true;
scene->scene.action_remap_function = level_select_action_remap_func;
add_scene_layer( add_scene_layer(
&scene->scene, scene->scene.engine->intended_window_size.x, &scene->scene, scene->scene.engine->intended_window_size.x,
scene->scene.engine->intended_window_size.y, scene->scene.engine->intended_window_size.y,
@ -298,13 +311,7 @@ void init_level_select_scene(LevelSelectScene_t* scene)
sc_array_add(&scene->scene.systems, &level_preview_render_func); sc_array_add(&scene->scene.systems, &level_preview_render_func);
sc_array_add(&scene->scene.systems, &level_select_render_func); sc_array_add(&scene->scene.systems, &level_select_render_func);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_UP, KEY_UP), ACTION_UP); level_select_action_remap_func(&scene->scene);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_DOWN, KEY_DOWN), ACTION_DOWN);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_JUMP, KEY_ENTER), ACTION_CONFIRM);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_EXIT, KEY_Q), ACTION_EXIT);
sc_map_put_64(&scene->scene.action_map, KEY_BACKSPACE, ACTION_EXIT);
sc_map_put_64(&scene->scene.action_map, KEY_ENTER, ACTION_CONFIRM);
} }
void free_level_select_scene(LevelSelectScene_t* scene) void free_level_select_scene(LevelSelectScene_t* scene)
{ {

View File

@ -56,7 +56,7 @@ static void exec_component_function(Scene_t* scene, int sel)
change_scene(scene->engine, LEVEL_SELECT_SCENE); change_scene(scene->engine, LEVEL_SELECT_SCENE);
break; break;
case 1: case 1:
change_scene(scene->engine, OPTIONS_SCENE); change_scene_ext(scene->engine, OPTIONS_SCENE, true);
break; break;
case 2: case 2:
scene->state = 0; scene->state = 0;
@ -162,9 +162,24 @@ static void gui_loop(Scene_t* scene)
} }
} }
static void menu_action_remap_func(Scene_t* scene)
{
sc_map_clear_64(&scene->action_map);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_UP, KEY_UP), ACTION_UP);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_DOWN, KEY_DOWN), ACTION_DOWN);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_LEFT, KEY_LEFT), ACTION_LEFT);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_RIGHT, KEY_RIGHT), ACTION_RIGHT);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_JUMP, KEY_ENTER), ACTION_CONFIRM);
// Guarantee a enter key for selection
sc_map_put_64(&scene->action_map, KEY_ENTER, ACTION_CONFIRM);
}
void init_menu_scene(MenuScene_t* scene) void init_menu_scene(MenuScene_t* scene)
{ {
init_scene(&scene->scene, &menu_do_action, 0); init_scene(&scene->scene, &menu_do_action, 0);
scene->scene.action_remap_function = &menu_action_remap_func;
sc_array_add(&scene->scene.systems, &gui_loop); sc_array_add(&scene->scene.systems, &gui_loop);
sc_array_add(&scene->scene.systems, &menu_scene_render_func); sc_array_add(&scene->scene.systems, &menu_scene_render_func);
@ -205,14 +220,7 @@ void init_menu_scene(MenuScene_t* scene)
} }
); );
menu_action_remap_func(&scene->scene);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_UP, KEY_UP), ACTION_UP);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_DOWN, KEY_DOWN), ACTION_DOWN);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_LEFT, KEY_LEFT), ACTION_LEFT);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_RIGHT, KEY_RIGHT), ACTION_RIGHT);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_JUMP, KEY_ENTER), ACTION_CONFIRM);
// Guarantee a enter key for selection
sc_map_put_64(&scene->scene.action_map, KEY_ENTER, ACTION_CONFIRM);
} }
void free_menu_scene(MenuScene_t* scene) void free_menu_scene(MenuScene_t* scene)

View File

@ -69,7 +69,7 @@ static void options_scene_render_func(Scene_t* scene)
{ {
DrawText(">>", 0, y_offset, 12, WHITE); DrawText(">>", 0, y_offset, 12, WHITE);
} }
sprintf(buffer, "%s : %s %d", keybind_info.action_name, keybind_info.key_name, keybind_info.key); sprintf(buffer, "%s : %s", keybind_info.action_name, keybind_info.key_name);
DrawText(buffer, 32, y_offset, 12, WHITE); DrawText(buffer, 32, y_offset, 12, WHITE);
y_offset += 12; y_offset += 12;
line++; line++;
@ -132,8 +132,17 @@ static void wait_for_keymap_input(Scene_t* scene)
printf("Key inputted: %d\n", scene->engine->last_input_key); printf("Key inputted: %d\n", scene->engine->last_input_key);
ActionType_t selected_action = data->keybinds_info.elems[data->curr_selection - 1].action;
int last_key = sc_map_get_64(&scene->engine->keybinds, selected_action);
assert(sc_map_found(&scene->engine->keybinds));
// Register immediately only for this scene
sc_map_del_64(&scene->action_map, last_key);
sc_map_put_64(&scene->action_map, scene->engine->last_input_key, selected_action);
data->keybinds_info.elems[data->curr_selection - 1].key = scene->engine->last_input_key; data->keybinds_info.elems[data->curr_selection - 1].key = scene->engine->last_input_key;
data->keybinds_info.elems[data->curr_selection - 1].key_name = ExtGetKeyName(scene->engine->last_input_key); data->keybinds_info.elems[data->curr_selection - 1].key_name = ExtGetKeyName(scene->engine->last_input_key);
data->mode = OPTIONS_NORMAL_MODE; data->mode = OPTIONS_NORMAL_MODE;
} }
@ -153,8 +162,25 @@ static void exec_component_function(Scene_t* scene, uint16_t sel)
{ {
// Either OK or Cancel // Either OK or Cancel
// Both will return to menu scene // Both will return to menu scene
// OK does nothing since the keybinds are already modified
sel -= n_binds; sel -= n_binds;
// TODO: add check to save keybinds if (sel == 0)
{
printf("Keybind ready\n");
// OK will propagate to the engine
KeyBindInfo_t info;
sc_array_foreach(&data->keybinds_info, info)
{
register_keybind(scene->engine, info.key, info.action);
}
remap_scene_keys(scene->engine);
}
else
{
// Need to reset action map, otherwise will preserve over scene change
scene->action_remap_function(scene);
}
change_scene(scene->engine, MAIN_MENU_SCENE); change_scene(scene->engine, MAIN_MENU_SCENE);
} }
@ -221,10 +247,50 @@ static void options_do_action(Scene_t* scene, ActionType_t action, bool pressed)
} }
} }
static void reset_options_scene(Scene_t* scene)
{
OptionSceneData_t* data = &(CONTAINER_OF(scene, OptionScene_t, scene)->data);
sc_array_clear(&data->keybinds_info);
{
int key;
ActionType_t action;
sc_map_foreach(&scene->engine->keybinds, action, key)
{
KeyBindInfo_t keybind = {
.action_name = get_action_name(action),
.key_name = ExtGetKeyName(key),
.action = action,
.original_key = key,
.key = key,
};
sc_array_add(&data->keybinds_info, keybind);
}
}
data->curr_selection = 0;
data->mode = OPTIONS_NORMAL_MODE;
}
static void options_action_remap_func(Scene_t* scene)
{
sc_map_clear_64(&scene->action_map);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_UP, KEY_UP), ACTION_UP);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_DOWN, KEY_DOWN), ACTION_DOWN);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_LEFT, KEY_LEFT), ACTION_LEFT);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_RIGHT, KEY_RIGHT), ACTION_RIGHT);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_JUMP, KEY_ENTER), ACTION_CONFIRM);
sc_map_put_64(&scene->action_map, get_keybind_or_default(scene->engine, ACTION_EXIT, KEY_Q), ACTION_EXIT);
sc_map_put_64(&scene->action_map, KEY_ENTER, ACTION_CONFIRM);
sc_map_put_64(&scene->action_map, KEY_BACKSPACE, ACTION_EXIT);
}
void init_options_scene(OptionScene_t* scene) void init_options_scene(OptionScene_t* scene)
{ {
init_scene(&scene->scene, &options_do_action, 0); init_scene(&scene->scene, &options_do_action, 0);
scene->scene.bg_colour = BLACK; scene->scene.bg_colour = BLACK;
scene->scene.reset_function = &reset_options_scene;
scene->scene.action_remap_function = &options_action_remap_func;
add_scene_layer( add_scene_layer(
&scene->scene, scene->scene.engine->intended_window_size.x, &scene->scene, scene->scene.engine->intended_window_size.x,
scene->scene.engine->intended_window_size.y, scene->scene.engine->intended_window_size.y,
@ -244,36 +310,10 @@ void init_options_scene(OptionScene_t* scene)
sc_array_init(&scene->data.keybinds_info); sc_array_init(&scene->data.keybinds_info);
{
int key;
ActionType_t action;
sc_map_foreach(&scene->scene.engine->keybinds, action, key)
{
KeyBindInfo_t keybind = {
.action_name = get_action_name(action),
.key_name = ExtGetKeyName(key),
.action = action,
.key = key,
};
sc_array_add(&scene->data.keybinds_info, keybind);
}
}
// The number of actions that can be key-binds is constant.
// So can just init once.
sc_array_add(&scene->scene.systems, &wait_for_keymap_input); sc_array_add(&scene->scene.systems, &wait_for_keymap_input);
sc_array_add(&scene->scene.systems, &options_scene_render_func); sc_array_add(&scene->scene.systems, &options_scene_render_func);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_UP, KEY_UP), ACTION_UP); options_action_remap_func(&scene->scene);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_DOWN, KEY_DOWN), ACTION_DOWN);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_LEFT, KEY_LEFT), ACTION_LEFT);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_RIGHT, KEY_RIGHT), ACTION_RIGHT);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_JUMP, KEY_ENTER), ACTION_CONFIRM);
sc_map_put_64(&scene->scene.action_map, get_keybind_or_default(scene->scene.engine, ACTION_EXIT, KEY_Q), ACTION_EXIT);
sc_map_put_64(&scene->scene.action_map, KEY_ENTER, ACTION_CONFIRM);
sc_map_put_64(&scene->scene.action_map, KEY_BACKSPACE, ACTION_EXIT);
} }
void free_options_scene(OptionScene_t* scene) void free_options_scene(OptionScene_t* scene)

View File

@ -146,6 +146,7 @@ typedef struct KeyBindInfo {
const char* key_name; const char* key_name;
int action; int action;
int key; int key;
int original_key;
}KeyBindInfo_t; }KeyBindInfo_t;
sc_array_def(KeyBindInfo_t, keybinds); sc_array_def(KeyBindInfo_t, keybinds);