HATPC/scenes/options_scene.c

324 lines
9.9 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";
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 = 16;
uint16_t line = 0;
BeginTextureMode(scene->layers.render_layers[0].layer_tex);
ClearBackground((Color){0,0,0,0});
if (line == data->curr_selection)
{
DrawText(">>", 0, y_offset, 12, WHITE);
}
float vol = GetMasterVolume();
sprintf(buffer, "Volume : %.1f", vol);
DrawText(buffer, 32, y_offset, 12, WHITE);
y_offset += 12;
line++;
sc_array_foreach(&data->keybinds_info, keybind_info) {
if (line == data->curr_selection)
{
DrawText(">>", 0, y_offset, 12, WHITE);
}
sprintf(buffer, "%s : %s", keybind_info.action_name, keybind_info.key_name);
DrawText(buffer, 32, y_offset, 12, WHITE);
y_offset += 12;
line++;
}
if (line == data->curr_selection)
{
DrawText(">>", 0, y_offset, 12, WHITE);
}
DrawText("OK", 32, y_offset, 12, WHITE);
y_offset += 12;
line++;
if (line == data->curr_selection)
{
DrawText(">>", 0, y_offset, 12, WHITE);
}
DrawText("Cancel", 32, y_offset, 12, WHITE);
y_offset += 12;
line++;
if (data->mode == OPTIONS_KEYBIND_MODE)
{
y_offset += 36;
DrawText("Press a Key...", 32, y_offset, 12, WHITE);
}
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;
}
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_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);
printf("Sel: %u\n", sel);
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)
{
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);
}
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),
.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);
}