337 lines
11 KiB
C
337 lines
11 KiB
C
#include "scene_impl.h"
|
|
#include "assets_tag.h"
|
|
#include "keymaps.h"
|
|
#include <stdio.h>
|
|
|
|
/**
|
|
* A potential performance tweak is to reduce the lookup.
|
|
* The number of configurable actions should be fixed and
|
|
* therefore would be known on scene init.
|
|
*
|
|
* So delegate all lookup on scene init.
|
|
* Then, only call lookup on key changes.
|
|
*/
|
|
|
|
static const char* get_action_name(ActionType_t action) {
|
|
/**
|
|
* This is sort of a hack to get it done.
|
|
* In theory, the engine should provide a way to register a name for an action
|
|
* However, i didn't plan that far and this is the only place that needs it
|
|
* Maybe in the next version of the engine...
|
|
*
|
|
* I could hardcode this to be array, but nah...
|
|
*/
|
|
switch (action) {
|
|
case ACTION_UP:
|
|
return "Up";
|
|
case ACTION_DOWN:
|
|
return "Down";
|
|
case ACTION_LEFT:
|
|
return "Left";
|
|
case ACTION_RIGHT:
|
|
return "Right";
|
|
case ACTION_JUMP:
|
|
case ACTION_CONFIRM:
|
|
return "Confirm/Jump";
|
|
case ACTION_EXIT:
|
|
return "Return";
|
|
case ACTION_LOOKAHEAD:
|
|
return "Look";
|
|
case ACTION_RESTART:
|
|
return "Restart";
|
|
default:
|
|
return "Undefined";
|
|
}
|
|
}
|
|
|
|
static void options_scene_render_func(Scene_t* scene)
|
|
{
|
|
OptionSceneData_t* data = &(CONTAINER_OF(scene, OptionScene_t, scene)->data);
|
|
|
|
KeyBindInfo_t keybind_info;
|
|
char buffer[64];
|
|
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)
|
|
{
|
|
DrawTextEx(*menu_font, ">>", (Vector2){start_x,y_offset}, FONT_SIZE, 4, BLACK);
|
|
}
|
|
float vol = GetMasterVolume();
|
|
sprintf(buffer, "Volume : %.1f", vol);
|
|
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)
|
|
{
|
|
DrawTextEx(*menu_font, ">>", (Vector2){start_x,y_offset}, FONT_SIZE, 4, BLACK);
|
|
}
|
|
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)
|
|
{
|
|
DrawTextEx(*menu_font, ">>", (Vector2){start_x, y_offset}, FONT_SIZE, 4, BLACK);
|
|
}
|
|
DrawTextEx(*menu_font, "Apply", (Vector2){start_x+32, y_offset}, FONT_SIZE, 4, BLACK);
|
|
y_offset += FONT_SIZE;
|
|
line++;
|
|
|
|
if (line == data->curr_selection)
|
|
{
|
|
DrawTextEx(*menu_font, ">>", (Vector2){start_x,y_offset}, FONT_SIZE, 4, BLACK);
|
|
}
|
|
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)
|
|
{
|
|
OptionSceneData_t* data = &(CONTAINER_OF(scene, OptionScene_t, scene)->data);
|
|
if (sel == 0)
|
|
{
|
|
// Volume option, does nothing
|
|
return;
|
|
}
|
|
sel--;
|
|
|
|
uint16_t n_binds = sc_map_size_64(&scene->engine->keybinds);
|
|
if (sel >= n_binds)
|
|
{
|
|
// Either OK or Cancel
|
|
// Both will return to menu scene
|
|
// OK does nothing since the keybinds are already modified
|
|
sel -= n_binds;
|
|
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
|
|
{
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
static void options_do_action(Scene_t* scene, ActionType_t action, bool pressed)
|
|
{
|
|
OptionSceneData_t* data = &(CONTAINER_OF(scene, OptionScene_t, scene)->data);
|
|
|
|
switch(data->mode)
|
|
{
|
|
case OPTIONS_NORMAL_MODE:
|
|
{
|
|
uint16_t new_selection = data->curr_selection;
|
|
if (!pressed)
|
|
{
|
|
switch(action)
|
|
{
|
|
case ACTION_UP:
|
|
if (new_selection > 0)
|
|
{
|
|
data->curr_selection--;
|
|
}
|
|
break;
|
|
case ACTION_DOWN:
|
|
if (new_selection < data->n_selections - 1)
|
|
{
|
|
data->curr_selection++;
|
|
}
|
|
break;
|
|
case ACTION_LEFT:
|
|
// Left and right only works on volume option
|
|
if (new_selection == 0)
|
|
{
|
|
}
|
|
break;
|
|
case ACTION_RIGHT:
|
|
if (new_selection == 0)
|
|
{
|
|
}
|
|
break;
|
|
case ACTION_CONFIRM:
|
|
exec_component_function(scene, new_selection);
|
|
break;
|
|
case ACTION_EXIT:
|
|
if(scene->engine != NULL)
|
|
{
|
|
change_scene(scene->engine, MAIN_MENU_SCENE);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
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,
|
|
(Rectangle){
|
|
0, 0,
|
|
scene->scene.engine->intended_window_size.x,
|
|
scene->scene.engine->intended_window_size.y
|
|
}
|
|
);
|
|
|
|
scene->data.curr_selection = 0;
|
|
scene->data.mode = OPTIONS_NORMAL_MODE;
|
|
|
|
// 3 extra options: Volume, Ok, Cancel
|
|
scene->data.n_selections = sc_map_size_64(&scene->scene.engine->keybinds) + 3;
|
|
|
|
|
|
sc_array_init(&scene->data.keybinds_info);
|
|
|
|
sc_array_add(&scene->scene.systems, &wait_for_keymap_input);
|
|
sc_array_add(&scene->scene.systems, &options_scene_render_func);
|
|
|
|
options_action_remap_func(&scene->scene);
|
|
}
|
|
|
|
void free_options_scene(OptionScene_t* scene)
|
|
{
|
|
sc_array_term(&scene->data.keybinds_info);
|
|
free_scene(&scene->scene);
|
|
}
|