Compare commits

..

5 Commits

Author SHA1 Message Date
En Yi 0088fd64de Spice up the option screen 2025-08-23 16:13:45 +08:00
En Yi 18064ddf3c Change key bind modification confirmation
Player needs to apply the changes before returning to menu
2025-08-23 15:35:29 +08:00
En Yi d8a84f6881 Fix restart visual bug 2025-08-23 15:34:40 +08:00
En Yi 44a1eba4de Propagate keybind changes 2025-08-23 14:13:58 +08:00
En Yi 16de3bb70e Implement key recording for rebind
Changelog:
- Update engine to record last key released
- Update option scene to get the last key pressed for binding
    - Does not actually change the keybind yet
2025-08-22 21:23:54 +08:00
9 changed files with 298 additions and 103 deletions

View File

@ -17,6 +17,7 @@ void init_engine(GameEngine_t* engine, Vector2 starting_win_size)
engine->intended_window_size = starting_win_size;
InitWindow(starting_win_size.x, starting_win_size.y, "raylib");
engine->base_canvas = LoadRenderTexture(starting_win_size.x, starting_win_size.y);
engine->last_input_key = KEY_NULL;
}
void deinit_engine(GameEngine_t* engine)
@ -47,24 +48,46 @@ int get_keybind_or_default(GameEngine_t* engine, ActionType_t action, int defaul
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)
{
engine->last_input_key = KEY_NULL;
Vector2 raw_mouse_pos = GetMousePosition();
scene->mouse_pos = raw_mouse_pos;
// For flexibility, key that are not mapped to an action are still recorded
// pay a little performance to do two checks.
unsigned int sz = sc_queue_size(&engine->key_buffer);
// Process any existing pressed key
for (size_t i = 0; i < sz; i++)
{
int button = sc_queue_del_first(&engine->key_buffer);
ActionType_t action = sc_map_get_64(&scene->action_map, button);
if (IsKeyReleased(button))
bool pressed = !IsKeyReleased(button);
if (!pressed)
{
do_action(scene, action, false);
engine->last_input_key = button;
}
else
ActionType_t action = sc_map_get_64(&scene->action_map, button);
if (sc_map_found(&scene->action_map))
{
do_action(scene, action, pressed);
}
if (pressed)
{
do_action(scene, action, true);
sc_queue_add_last(&engine->key_buffer, button);
}
}
@ -75,8 +98,10 @@ void process_inputs(GameEngine_t* engine, Scene_t* scene)
int button = GetKeyPressed();
if (button == 0) break;
ActionType_t action = sc_map_get_64(&scene->action_map, button);
if (!sc_map_found(&scene->action_map)) continue;
if (sc_map_found(&scene->action_map))
{
do_action(scene, action, true);
}
sc_queue_add_last(&engine->key_buffer, button);
}
@ -108,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)
{
// Backwards compat
@ -188,6 +224,7 @@ void init_scene(Scene_t* scene, action_func_t action_func, uint32_t subsystem_in
scene->bg_colour = WHITE;
scene->action_function = action_func;
scene->reset_function = NULL;
scene->state = SCENE_COMPLETE_ACTIVE;
scene->time_scale = 1.0f;
}
@ -435,6 +472,8 @@ void remove_child_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->curr_scene = idx;
engine->scenes[engine->curr_scene]->state = SCENE_COMPLETE_ACTIVE;

View File

@ -31,7 +31,10 @@ typedef struct GameEngine {
SFXList_t sfx_list;
// Maintain own queue to handle key presses
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_ptr scene_stack;
struct sc_heap scenes_render_order;
@ -40,6 +43,9 @@ typedef struct GameEngine {
// an absolute reference
Vector2 intended_window_size;
RenderTexture2D base_canvas;
// ad-hoc addition. Next version should be a configurable key buffer
int last_input_key;
} GameEngine_t;
#define SCENE_ACTIVE_BIT (1 << 0) // Systems Active
@ -67,9 +73,12 @@ struct Scene {
EntityManager_t ent_manager;
Scene_t* parent_scene;
struct sc_map_64 action_map; // key -> actions
struct sc_array_systems systems;
SceneRenderLayers_t layers;
Color bg_colour;
system_func_t reset_function;
system_func_t action_remap_function;
action_func_t action_function;
float delta_time;
float time_scale;
@ -88,14 +97,22 @@ void process_inputs(GameEngine_t* engine, Scene_t* scene);
// Register a key bind. If already exists, will override.
// 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);
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 update_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_ext(GameEngine_t* engine, unsigned int idx, bool reset);
Scene_t* change_active_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);

1
main.c
View File

@ -44,6 +44,7 @@ int main(void)
register_keybind(&engine, KEY_SPACE, ACTION_JUMP);
register_keybind(&engine, KEY_Q, ACTION_EXIT);
register_keybind(&engine, KEY_Z, ACTION_LOOKAHEAD);
register_keybind(&engine, KEY_R, ACTION_RESTART);
load_sfx(&engine, "snd_jump", PLAYER_JMP_SFX);
load_sfx(&engine, "snd_land", PLAYER_LAND_SFX);

View File

@ -1,5 +1,6 @@
#include "tracy/TracyC.h"
#include "keymaps.h"
#include "scene_impl.h"
#include "game_systems.h"
#include "water_flow.h"
@ -62,9 +63,10 @@ static void level_scene_render_func(Scene_t* scene)
air_pos.x -= 32;
}
}
if (sc_map_size_64v(&scene->ent_manager.entities_map[PLAYER_ENT_TAG]) == 0)
if (data->sm.state == LEVEL_STATE_DEAD)
{
DrawTextEx(*menu_font, "Press R to Try Again", (Vector2){32, data->game_rec.height/2 - 64}, 64, 4, WHITE);
sprintf(buffer, "Press %s to Try Again", data->restart_keyname);
DrawTextEx(*menu_font, buffer, (Vector2){32, data->game_rec.height/2 - 64}, 64, 4, WHITE);
}
// For DEBUG
int gui_x = 5;
@ -73,7 +75,7 @@ static void level_scene_render_func(Scene_t* scene)
DrawRectangle(0, 0, data->game_rec.width, 32, (Color){0,0,0,128});
{
DrawText("Z", 300, 5, 24, RED);
DrawText(data->look_keyname, 300, 5, 24, RED);
Sprite_t* spr = get_sprite(&scene->engine->assets, "eye");
if (data->camera.mode == CAMERA_RANGED_MOVEMENT)
{
@ -518,6 +520,30 @@ 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);
LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data);
{
int restart_key = get_keybind_or_default(scene->engine, ACTION_RESTART, KEY_R);
data->restart_keyname = ExtGetKeyName(restart_key);
sc_map_put_64(&scene->action_map, restart_key, ACTION_RESTART);
}
{
int look_key = get_keybind_or_default(scene->engine, ACTION_LOOKAHEAD, KEY_Z);
data->look_keyname = ExtGetKeyName(look_key);
sc_map_put_64(&scene->action_map, look_key, ACTION_LOOKAHEAD);
}
}
void init_game_scene(LevelScene_t* scene)
{
init_scene(&scene->scene, &level_do_action, ENABLE_ENTITY_MANAGEMENT_SYSTEM | ENABLE_PARTICLE_SYSTEM);
@ -526,6 +552,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, DYNMEM_ENT_TAG, 16);
scene->scene.action_remap_function = &game_action_remap_func;
scene->data.tilemap.tiles = all_tiles;
scene->data.tilemap.render_nodes = all_tile_rendernodes;
init_level_scene_data(
@ -549,6 +576,7 @@ void init_game_scene(LevelScene_t* scene)
scene->data.sm.state_functions[LEVEL_STATE_RUNNING] = NULL;
scene->data.sm.state_functions[LEVEL_STATE_DEAD] = NULL;
scene->data.sm.state_functions[LEVEL_STATE_COMPLETE] = at_level_complete;
scene->data.sm.state = LEVEL_STATE_STARTING;
scene->scene.bg_colour = LIGHTGRAY;
add_scene_layer(
@ -600,15 +628,7 @@ void init_game_scene(LevelScene_t* scene)
// This avoid graphical glitch, not essential
//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);
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);
game_action_remap_func(&scene->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 TEXT_PADDING 3
#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);
scene->data.preview = LoadRenderTexture(LEVEL_PREVIEW_SIZE, LEVEL_PREVIEW_SIZE);
scene->data.update_preview = true;
scene->scene.action_remap_function = level_select_action_remap_func;
add_scene_layer(
&scene->scene, scene->scene.engine->intended_window_size.x,
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_select_render_func);
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_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);
level_select_action_remap_func(&scene->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);
break;
case 1:
change_scene(scene->engine, OPTIONS_SCENE);
change_scene_ext(scene->engine, OPTIONS_SCENE, true);
break;
case 2:
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)
{
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, &menu_scene_render_func);
@ -205,14 +220,7 @@ void init_menu_scene(MenuScene_t* 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);
menu_action_remap_func(&scene->scene);
}
void free_menu_scene(MenuScene_t* scene)

View File

@ -37,6 +37,8 @@ static const char* get_action_name(ActionType_t action) {
return "Return";
case ACTION_LOOKAHEAD:
return "Look";
case ACTION_RESTART:
return "Restart";
default:
return "Undefined";
}
@ -48,55 +50,119 @@ static void options_scene_render_func(Scene_t* scene)
KeyBindInfo_t keybind_info;
char buffer[64];
int y_offset = 16;
int y_offset = 32;
uint16_t line = 0;
Sprite_t* level_board = get_sprite(&scene->engine->assets, "lvl_board");
#define TITLE_FONT_SIZE 40
#define FONT_SIZE 24
#define FONT_COLOUR BLACK
float start_x = scene->engine->intended_window_size.x / 2;
start_x -= level_board->frame_size.x / 2;
Font* menu_font = get_font(&scene->engine->assets, "MenuFont");
BeginTextureMode(scene->layers.render_layers[0].layer_tex);
ClearBackground((Color){0,0,0,0});
draw_sprite(level_board, 0, (Vector2){start_x,0},0, false);
Vector2 title_size = MeasureTextEx(*menu_font, "Options", TITLE_FONT_SIZE, 4);
DrawTextEx(*menu_font, "Options", (Vector2){start_x + title_size.x/2,y_offset}, TITLE_FONT_SIZE, 4, BLACK);
y_offset += TITLE_FONT_SIZE;
start_x += 32;
if (line == data->curr_selection)
{
DrawText(">>", 0, y_offset, 12, WHITE);
DrawTextEx(*menu_font, ">>", (Vector2){start_x,y_offset}, FONT_SIZE, 4, BLACK);
}
float vol = GetMasterVolume();
sprintf(buffer, "Volume : %.1f", vol);
DrawText(buffer, 32, y_offset, 12, WHITE);
y_offset += 12;
DrawTextEx(*menu_font, buffer, (Vector2){start_x+32, y_offset}, FONT_SIZE, 4, BLACK);
y_offset += FONT_SIZE;
line++;
sc_array_foreach(&data->keybinds_info, keybind_info) {
if (line == data->curr_selection)
{
DrawText(">>", 0, y_offset, 12, WHITE);
DrawTextEx(*menu_font, ">>", (Vector2){start_x,y_offset}, FONT_SIZE, 4, BLACK);
}
sprintf(buffer, "%s : %s %d", keybind_info.action_name, keybind_info.key_name, keybind_info.key);
DrawText(buffer, 32, y_offset, 12, WHITE);
y_offset += 12;
if (keybind_info.key == keybind_info.original_key)
{
sprintf(buffer, "%s : %s", keybind_info.action_name, keybind_info.key_name);
DrawTextEx(*menu_font, buffer, (Vector2){start_x+32, y_offset}, FONT_SIZE, 4, BLACK);
}
else
{
sprintf(buffer, "%s : %s => %s", keybind_info.action_name, keybind_info.original_key_name, keybind_info.key_name);
DrawTextEx(*menu_font, buffer, (Vector2){start_x+32, y_offset}, FONT_SIZE, 4, BLUE);
}
y_offset += FONT_SIZE;
line++;
}
y_offset += FONT_SIZE;
if (line == data->curr_selection)
{
DrawText(">>", 0, y_offset, 12, WHITE);
DrawTextEx(*menu_font, ">>", (Vector2){start_x, y_offset}, FONT_SIZE, 4, BLACK);
}
DrawText("OK", 32, y_offset, 12, WHITE);
y_offset += 12;
DrawTextEx(*menu_font, "Apply", (Vector2){start_x+32, y_offset}, FONT_SIZE, 4, BLACK);
y_offset += FONT_SIZE;
line++;
if (line == data->curr_selection)
{
DrawText(">>", 0, y_offset, 12, WHITE);
DrawTextEx(*menu_font, ">>", (Vector2){start_x,y_offset}, FONT_SIZE, 4, BLACK);
}
DrawText("Cancel", 32, y_offset, 12, WHITE);
y_offset += 12;
DrawTextEx(*menu_font, "Return", (Vector2){start_x+32, y_offset}, FONT_SIZE, 4, BLACK);
y_offset += FONT_SIZE;
line++;
if (data->mode == OPTIONS_KEYBIND_MODE)
{
y_offset += FONT_SIZE;
DrawTextEx(*menu_font, "Press a Key...", (Vector2){start_x+32, y_offset}, FONT_SIZE, 4, BLACK);
}
EndTextureMode();
}
static void wait_for_keymap_input(Scene_t* scene)
{
/**
* Due to engine design limitation, I canot remove
* the system when placed into the array
* So, this will have to run as part of the loop
* Even though it is only used in a particular mode
* It's a small performance penalty for unfortunate design
*/
OptionSceneData_t* data = &(CONTAINER_OF(scene, OptionScene_t, scene)->data);
if (data->mode == OPTIONS_NORMAL_MODE)
{
return;
}
if (data->mode == OPTIONS_KEYBIND_READY_MODE)
{
// Keys are processed before system Update
// Need to skip the current frame to get the next key
// This state is to do just that.
data->mode = OPTIONS_KEYBIND_MODE;
return;
}
if (scene->engine->last_input_key == KEY_NULL)
{
return;
}
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->mode = OPTIONS_NORMAL_MODE;
}
static void exec_component_function(Scene_t* scene, uint16_t sel)
{
printf("Sel: %u\n", sel);
OptionSceneData_t* data = &(CONTAINER_OF(scene, OptionScene_t, scene)->data);
if (sel == 0)
{
// Volume option, does nothing
@ -109,16 +175,31 @@ static void exec_component_function(Scene_t* scene, uint16_t sel)
{
// Either OK or Cancel
// Both will return to menu scene
// OK does nothing since the keybinds are already modified
sel -= n_binds;
// TODO: add check to save keybinds
change_scene(scene->engine, MAIN_MENU_SCENE);
if (sel == 0)
{
// 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);
scene->reset_function(scene);
data->curr_selection = sel + n_binds + 1;
}
else
{
// Keybind options
// TODO: This should change the scene mode
// Engine need to record last key released
// Need to reset action map, otherwise will preserve over scene change
scene->action_remap_function(scene);
change_scene(scene->engine, MAIN_MENU_SCENE);
}
}
else
{
// Keybind options, but need to wait one frame
data->mode = OPTIONS_KEYBIND_READY_MODE;
}
}
@ -173,15 +254,56 @@ static void options_do_action(Scene_t* scene, ActionType_t action, bool pressed)
}
}
break;
case OPTIONS_KEYBIND_MODE:
default:
break;
}
}
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),
.original_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)
{
init_scene(&scene->scene, &options_do_action, 0);
scene->scene.bg_colour = BLACK;
scene->scene.reset_function = &reset_options_scene;
scene->scene.action_remap_function = &options_action_remap_func;
add_scene_layer(
&scene->scene, scene->scene.engine->intended_window_size.x,
scene->scene.engine->intended_window_size.y,
@ -201,35 +323,10 @@ void init_options_scene(OptionScene_t* scene)
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, &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);
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);
options_action_remap_func(&scene->scene);
}
void free_options_scene(OptionScene_t* scene)

View File

@ -72,6 +72,8 @@ typedef struct LevelSceneData {
Sprite_t* solid_tile_sprites;
uint8_t selected_solid_tilemap;
LevelPack_t* level_pack;
const char* restart_keyname;
const char* look_keyname;
unsigned int current_level;
CoinCounter_t coins;
bool show_grid;
@ -137,14 +139,17 @@ typedef struct LevelSelectScene {
typedef enum OptionSceneMode {
OPTIONS_NORMAL_MODE,
OPTIONS_KEYBIND_READY_MODE,
OPTIONS_KEYBIND_MODE,
}OptionSceneMode_t;
typedef struct KeyBindInfo {
const char* action_name;
const char* key_name;
const char* original_key_name;
int action;
int key;
int original_key;
}KeyBindInfo_t;
sc_array_def(KeyBindInfo_t, keybinds);

View File

@ -111,6 +111,7 @@ bool load_level_tilemap(LevelScene_t* scene, unsigned int level_num)
scene->data.tilemap.n_tiles = n_tiles;
scene->data.coins.current = 0;
scene->data.coins.total = lvl_map.n_chests;
scene->data.sm.state = LEVEL_STATE_STARTING;
#define N_SOLID_TILESETS 4
static const char* SOLID_TILE_SELECTIONS[N_SOLID_TILESETS] = {