diff --git a/source/director.cpp b/source/director.cpp index f3916c7..8090f1f 100644 --- a/source/director.cpp +++ b/source/director.cpp @@ -180,6 +180,7 @@ void Director::bindInputs() Input::get()->bindKey(InputAction::SERVICE, SDL_SCANCODE_0); Input::get()->bindKey(InputAction::EXIT, SDL_SCANCODE_ESCAPE); Input::get()->bindKey(InputAction::PAUSE, SDL_SCANCODE_P); + Input::get()->bindKey(InputAction::BACK, SDL_SCANCODE_BACKSPACE); Input::get()->bindKey(InputAction::WINDOW_DEC_SIZE, SDL_SCANCODE_F1); Input::get()->bindKey(InputAction::WINDOW_INC_SIZE, SDL_SCANCODE_F2); diff --git a/source/global_inputs.cpp b/source/global_inputs.cpp index b3d195d..a2abd9f 100644 --- a/source/global_inputs.cpp +++ b/source/global_inputs.cpp @@ -211,6 +211,34 @@ namespace globalInputs ServiceMenu::get()->setSelectorDown(); return; } + + // Derecha + if (Input::get()->checkInput(InputAction::RIGHT, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD)) + { + ServiceMenu::get()->setSelectorRight(); + return; + } + + // Izquierda + if (Input::get()->checkInput(InputAction::LEFT, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD)) + { + ServiceMenu::get()->setSelectorLeft(); + return; + } + + // Aceptar + if (Input::get()->checkInput(InputAction::START, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD)) + { + ServiceMenu::get()->acceptSelection(); + return; + } + + // Atras + if (Input::get()->checkInput(InputAction::BACK, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD)) + { + ServiceMenu::get()->moveBack(); + return; + } } // Mandos diff --git a/source/input.h b/source/input.h index 5fbaf38..babe142 100644 --- a/source/input.h +++ b/source/input.h @@ -32,6 +32,7 @@ enum class InputAction : int START, // Inputs de control + BACK, EXIT, PAUSE, SERVICE, diff --git a/source/screen.cpp b/source/screen.cpp index 956f412..be5ecf0 100644 --- a/source/screen.cpp +++ b/source/screen.cpp @@ -411,4 +411,13 @@ void Screen::getSingletons() { serviceMenu_ = ServiceMenu::get(); notifier_ = Notifier::get(); +} + +// Aplica los valores de las opciones +void Screen::applySettings() +{ + SDL_SetRenderVSync(renderer_, options.video.v_sync ? 1 : SDL_RENDERER_VSYNC_DISABLED); + SDL_SetRenderLogicalPresentation(Screen::get()->getRenderer(), param.game.width, param.game.height, options.video.integer_scale ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE : SDL_LOGICAL_PRESENTATION_LETTERBOX); + adjustWindowSize(); + setFullscreenMode(); } \ No newline at end of file diff --git a/source/screen.h b/source/screen.h index 958b605..650a7b4 100644 --- a/source/screen.h +++ b/source/screen.h @@ -40,6 +40,7 @@ public: void setWindowZoom(int size); // Cambia el tamaño de la ventana bool decWindowZoom(); // Reduce el tamaño de la ventana bool incWindowZoom(); // Aumenta el tamaño de la ventana + void applySettings(); // Aplica los valores de las opciones // --- Efectos visuales --- void shake() { shake_effect_.enable(src_rect_, dst_rect_); } // Agita la pantalla diff --git a/source/service_menu.cpp b/source/service_menu.cpp index 8c7ca9d..418c4c8 100644 --- a/source/service_menu.cpp +++ b/source/service_menu.cpp @@ -5,7 +5,7 @@ #include #include "text.h" #include "resource.h" -#include "options.h +#include "options.h" // Singleton ServiceMenu *ServiceMenu::instance_ = nullptr; @@ -21,16 +21,20 @@ ServiceMenu *ServiceMenu::get() { return ServiceMenu::instance_; } // Constructor ServiceMenu::ServiceMenu() - : elementText_(Resource::get()->getText("04b_25_flat")), - titleText_(Resource::get()->getText("04b_25_flat_2x")) + : element_text_(Resource::get()->getText("04b_25_flat")), + title_text_(Resource::get()->getText("04b_25_flat_2x")), + current_settings_group_(SettingsGroup::MAIN), + previous_settings_group_(current_settings_group_) { setAnchors(); initializeOptions(); + updateMenu(current_settings_group_); } void ServiceMenu::toggle() { enabled_ = !enabled_; + reset(); } void ServiceMenu::render() @@ -39,40 +43,6 @@ void ServiceMenu::render() { int y = rect_.y; constexpr int H_PADDING = 20; - constexpr std::array MAIN_LIST = { - "VIDEO", - "AUDIO", - "GAME", - "SYSTEM"}; - - constexpr std::array VIDEO_LIST = { - "VIDEO MODE", - "WINDOW SIZE", - "SHADERS", - "VSYNC", - "INTEGER SCALE"}; - - constexpr std::array AUDIO_LIST = { - "AUDIO", - "MAIN VOLUME", - "MUSIC VOLUME", - "SFX VOLUME"}; - - constexpr std::array GAME_LIST = { - "AUTOFIRE", - "LANG"}; - - constexpr std::array SYSTEM_LIST = { - "RESET", - "EXIT", - "SHUTDOWN"}; - - constexpr std::array OPTIONS_LIST = { - "FULLSCREEN", - "3", - "OFF", - "ON", - "ON"}; // SOMBRA SDL_FRect shadowRect = {rect_.x + 5, rect_.y + 5, rect_.w, rect_.h}; @@ -80,28 +50,28 @@ void ServiceMenu::render() SDL_RenderFillRect(Screen::get()->getRenderer(), &shadowRect); // FONDO - SDL_SetRenderDrawColor(Screen::get()->getRenderer(), bgColor_.r, bgColor_.g, bgColor_.b, 255); + SDL_SetRenderDrawColor(Screen::get()->getRenderer(), bg_color_.r, bg_color_.g, bg_color_.b, 255); SDL_RenderFillRect(Screen::get()->getRenderer(), &rect_); // BORDE - SDL_SetRenderDrawColor(Screen::get()->getRenderer(), titleColor_.r, titleColor_.g, titleColor_.b, 255); + SDL_SetRenderDrawColor(Screen::get()->getRenderer(), title_color_.r, title_color_.g, title_color_.b, 255); SDL_RenderRect(Screen::get()->getRenderer(), &rect_); // SERVICE MENU - y += lineHeight_; - titleText_->writeDX(TEXT_COLOR | TEXT_CENTER, param.game.game_area.center_x, y, "SERVICE MENU", -4, titleColor_); + y += line_height_; + title_text_->writeDX(TEXT_COLOR | TEXT_CENTER, param.game.game_area.center_x, y, "SERVICE MENU", -4, title_color_); // LINEA - y += lineHeight_ * 2; - SDL_SetRenderDrawColor(Screen::get()->getRenderer(), titleColor_.r, titleColor_.g, titleColor_.b, 255); + y += line_height_ * 2; + SDL_SetRenderDrawColor(Screen::get()->getRenderer(), title_color_.r, title_color_.g, title_color_.b, 255); SDL_RenderLine(Screen::get()->getRenderer(), rect_.x + H_PADDING, y, rect_.x + rect_.w - H_PADDING, y); // LIST - for (size_t i = 0; i < VIDEO_LIST.size(); ++i) + for (size_t i = 0; i < option_pairs_.size(); ++i) { - y += lineHeight_; - elementText_->writeColored(rect_.x + H_PADDING, y, VIDEO_LIST.at(i), i == selected_ ? selectedColor_ : textColor_, -2); - elementText_->writeColored(rect_.x + H_PADDING + 100, y, "" + std::string(OPTIONS_LIST.at(i)), i == selected_ ? selectedColor_ : textColor_, -2); + y += line_height_; + element_text_->writeColored(rect_.x + H_PADDING, y, option_pairs_.at(i).first, i == selected_ ? selected_color_ : text_color_, -2); + element_text_->writeColored(rect_.x + H_PADDING + 100, y, "" + std::string(option_pairs_.at(i).second), i == selected_ ? selected_color_ : text_color_, -2); } } } @@ -111,7 +81,7 @@ void ServiceMenu::update() if (enabled_) { updateCounter(); - selectedColor_ = getSelectedColor(); + selected_color_ = getSelectedColor(); } } @@ -119,7 +89,7 @@ void ServiceMenu::setAnchors() { width_ = 220; height_ = 200; - lineHeight_ = elementText_->getCharacterSize() + 5; + line_height_ = element_text_->getCharacterSize() + 5; rect_ = { (param.game.width - width_) / 2, (param.game.height - height_) / 2, @@ -168,44 +138,137 @@ void ServiceMenu::setSelectorUp() } else { - selected_ = 4; + selected_ = option_pairs_.size() - 1; } } void ServiceMenu::setSelectorDown() { - selected_ = (selected_ + 1) % 5; + selected_ = (selected_ + 1) % option_pairs_.size(); +} + +void ServiceMenu::setSelectorRight() +{ + display_options_.at(selected_).adjustValue(true); + option_pairs_ = getOptionPairs(current_settings_group_); + applySettings(current_settings_group_); +} +void ServiceMenu::setSelectorLeft() +{ + display_options_.at(selected_).adjustValue(false); + option_pairs_ = getOptionPairs(current_settings_group_); + applySettings(current_settings_group_); } void ServiceMenu::acceptSelection() { + if (display_options_.at(selected_).type == ValueType::FOLDER) + { + previous_settings_group_ = current_settings_group_; + current_settings_group_ = display_options_.at(selected_).target_group; + updateMenu(current_settings_group_); + selected_ = 0; + } } -void ServiceMenu::cancelSelection() +void ServiceMenu::moveBack() { + if (current_settings_group_ == SettingsGroup::MAIN) + { + enabled_ = false; + return; + } + else + { + selected_ = 0; + current_settings_group_ = previous_settings_group_; + updateMenu(current_settings_group_); + } } void ServiceMenu::initializeOptions() { // Video options_.emplace_back("FULLSCREEN", SettingsGroup::VIDEO, OptionBehavior::ADJUST, &options.video.fullscreen, ValueType::BOOL); - options_.emplace_back("WINDOW SIZE", SettingsGroup::VIDEO, OptionBehavior::ADJUST, &options.window.size, ValueType::INT); + options_.emplace_back("WINDOW SIZE", SettingsGroup::VIDEO, OptionBehavior::ADJUST, &options.window.size, ValueType::INT, 1, options.window.max_size, 1); options_.emplace_back("SHADERS", SettingsGroup::VIDEO, OptionBehavior::ADJUST, &options.video.shaders, ValueType::BOOL); options_.emplace_back("VSYNC", SettingsGroup::VIDEO, OptionBehavior::ADJUST, &options.video.v_sync, ValueType::BOOL); options_.emplace_back("INTEGER SCALE", SettingsGroup::VIDEO, OptionBehavior::ADJUST, &options.video.integer_scale, ValueType::BOOL); // Audio options_.emplace_back("AUDIO", SettingsGroup::AUDIO, OptionBehavior::ADJUST, &options.audio.enabled, ValueType::BOOL); - options_.emplace_back("MAIN VOLUME", SettingsGroup::AUDIO, OptionBehavior::ADJUST, &options.audio.volume, ValueType::INT); - options_.emplace_back("MUSIC VOLUME", SettingsGroup::AUDIO, OptionBehavior::ADJUST, &options.audio.music.volume, ValueType::INT); - options_.emplace_back("SFX VOLUME", SettingsGroup::AUDIO, OptionBehavior::ADJUST, &options.audio.sound.volume, ValueType::INT); + options_.emplace_back("MAIN VOLUME", SettingsGroup::AUDIO, OptionBehavior::ADJUST, &options.audio.volume, ValueType::INT, 0, 100, 5); + options_.emplace_back("MUSIC VOLUME", SettingsGroup::AUDIO, OptionBehavior::ADJUST, &options.audio.music.volume, ValueType::INT, 0, 100, 5); + options_.emplace_back("SFX VOLUME", SettingsGroup::AUDIO, OptionBehavior::ADJUST, &options.audio.sound.volume, ValueType::INT, 0, 100, 5); // Game options_.emplace_back("AUTOFIRE", SettingsGroup::GAME, OptionBehavior::ADJUST, &options.game.autofire, ValueType::BOOL); - //options_.emplace_back("LANG", SettingsGroup::GAME, OptionBehavior::ADJUST, &options.game.language, ValueType::BOOL); + // options_.emplace_back("LANG", SettingsGroup::GAME, OptionBehavior::ADJUST, &options.game.language, ValueType::BOOL); // System options_.emplace_back("RESET", SettingsGroup::SYSTEM, OptionBehavior::SELECT, nullptr, ValueType::NONE); options_.emplace_back("QUIT", SettingsGroup::SYSTEM, OptionBehavior::SELECT, nullptr, ValueType::NONE); options_.emplace_back("SHUTDOWN", SettingsGroup::SYSTEM, OptionBehavior::SELECT, nullptr, ValueType::NONE); + + // Menu principal + options_.emplace_back("VIDEO", SettingsGroup::MAIN, OptionBehavior::SELECT, SettingsGroup::VIDEO); + options_.emplace_back("AUDIO", SettingsGroup::MAIN, OptionBehavior::SELECT, SettingsGroup::AUDIO); + options_.emplace_back("GAME", SettingsGroup::MAIN, OptionBehavior::SELECT, SettingsGroup::GAME); + options_.emplace_back("SYSTEM", SettingsGroup::MAIN, OptionBehavior::SELECT, SettingsGroup::SYSTEM); +} + +ServiceMenu::OptionPairs ServiceMenu::getOptionPairs(ServiceMenu::SettingsGroup group) const +{ + OptionPairs optionPairs; + + for (const auto &option : options_) + { + if (option.group == group) + { + optionPairs.emplace_back(option.caption, option.getValueAsString()); + } + } + + return optionPairs; +} + +std::vector ServiceMenu::getOptionsByGroup(SettingsGroup group) const +{ + std::vector filteredOptions; + + for (const auto &option : options_) + { + if (option.group == group) + { + filteredOptions.push_back(option); + } + } + + return filteredOptions; +} + +void ServiceMenu::applySettings(ServiceMenu::SettingsGroup group) +{ + switch (group) + { + case SettingsGroup::VIDEO: + Screen::get()->applySettings(); + break; + + default: + break; + } +} + +void ServiceMenu::updateMenu(SettingsGroup group) +{ + option_pairs_ = getOptionPairs(group); + display_options_ = getOptionsByGroup(group); +} + +void ServiceMenu::reset() +{ + selected_ = 0; + previous_settings_group_ = current_settings_group_ = SettingsGroup::MAIN; + updateMenu(current_settings_group_); } \ No newline at end of file diff --git a/source/service_menu.h b/source/service_menu.h index 72ca410..dd67757 100644 --- a/source/service_menu.h +++ b/source/service_menu.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -24,19 +25,24 @@ public: // --- Métodos de control --- void setSelectorUp(); void setSelectorDown(); + void setSelectorRight(); + void setSelectorLeft(); void acceptSelection(); - void cancelSelection(); + void moveBack(); // --- Getters --- bool isEnabled() const { return enabled_; } private: + using OptionPairs = std::vector>; + enum class SettingsGroup { - VIDEO, // Configuraciones relacionadas con la calidad y resolución de imagen - AUDIO, // Opciones de sonido y volumen - GAME, // Ajustes de jugabilidad y mecánicas - SYSTEM // Preferencias generales y configuraciones del sistema + VIDEO, // Configuraciones relacionadas con la calidad y resolución de imagen + AUDIO, // Opciones de sonido y volumen + GAME, // Ajustes de jugabilidad y mecánicas + SYSTEM, // Preferencias generales y configuraciones del sistema + MAIN // Raíz }; enum class OptionBehavior @@ -49,6 +55,7 @@ private: { BOOL, INT, + FOLDER, NONE }; @@ -57,48 +64,98 @@ private: std::string caption; // Texto visible en el menú SettingsGroup group; // Categoría de la opción OptionBehavior behavior; // Cómo se interactúa con la opción - void *linkedVariable; // Puntero a la variable que controla la opción - ValueType type; // Tipo de la variable (bool o int) + void *linked_variable; // Puntero a la variable que controla la opción + ValueType type; // Tipo de la variable (bool, int, folder, none) - // Constructor para inicializar los valores fácilmente + int min_value; // Valor mínimo (solo aplicable si type == INT) + int max_value; // Valor máximo (solo aplicable si type == INT) + int step_value; // Incremento al modificar la opción (solo aplicable si type == INT) + + SettingsGroup target_group; // Grupo al que hace referencia la opción si es de tipo FOLDER + + // Constructor para opciones de tipo BOOL, NONE o FOLDER OptionEntry(std::string cap, SettingsGroup grp, OptionBehavior beh, void *var, ValueType t) - : caption(cap), group(grp), behavior(beh), linkedVariable(var), type(t) {} + : caption(cap), group(grp), behavior(beh), linked_variable(var), type(t), + min_value(0), max_value(0), step_value(0), target_group(SettingsGroup::SYSTEM) {} + + // Constructor para opciones de tipo INT con valores mínimos, máximos e incremento + OptionEntry(std::string cap, SettingsGroup grp, OptionBehavior beh, void *var, ValueType t, int min, int max, int step) + : caption(cap), group(grp), behavior(beh), linked_variable(var), type(t), + min_value(min), max_value(max), step_value(step), target_group(SettingsGroup::SYSTEM) {} + + // Constructor para opciones de tipo FOLDER que referencian otro grupo + OptionEntry(std::string cap, SettingsGroup grp, OptionBehavior beh, SettingsGroup tgtGrp) + : caption(cap), group(grp), behavior(beh), linked_variable(nullptr), type(ValueType::FOLDER), + min_value(0), max_value(0), step_value(0), target_group(tgtGrp) {} + + // Método para modificar el valor + void adjustValue(bool increase) + { + if (linked_variable) + { + if (type == ValueType::INT) + { + int &value = *(static_cast(linked_variable)); + int newValue = increase ? value + step_value : value - step_value; + + // Asegurar que el nuevo valor se mantenga dentro de los límites + value = std::clamp(newValue, min_value, max_value); + } + else if (type == ValueType::BOOL) + { + bool &value = *(static_cast(linked_variable)); + value = !value; // Invierte el valor booleano + } + } + } // Método para obtener el valor como string std::string getValueAsString() const { - if (type == ValueType::BOOL) - return (*(static_cast(linkedVariable))) ? "ON" : "OFF"; - else if (type == ValueType::INT) - return std::to_string(*(static_cast(linkedVariable))); - - return "Unknown"; + switch (type) + { + case ValueType::BOOL: + return (*(static_cast(linked_variable))) ? "ON" : "OFF"; + case ValueType::INT: + return std::to_string(*(static_cast(linked_variable))); + default: + return ""; + } } }; // -- Variables internas --- - bool enabled_ = false; // Indica si el menú de servicio está activo - SDL_FRect rect_; // Rectangulo para definir el area del menú de servicio - std::shared_ptr elementText_; // Objeto para escribir texto; - std::shared_ptr titleText_; // Objeto para escribir texto; - size_t selected_ = 2; // Elemento del menú seleccionado - Uint32 counter_ = 0; // Contador interno - std::vector options_; // Listado con todas las opciones del menú de servicio + bool enabled_ = false; // Indica si el menú de servicio está activo + SDL_FRect rect_; // Rectangulo para definir el area del menú de servicio + std::shared_ptr element_text_; // Objeto para escribir texto; + std::shared_ptr title_text_; // Objeto para escribir texto; + size_t selected_ = 0; // Elemento del menú seleccionado + Uint32 counter_ = 0; // Contador interno + std::vector options_; // Listado con todas las opciones del menú de servicio + std::vector display_options_; // Listado con todas las opciones del menú de servicio que se estan mostrando + OptionPairs option_pairs_; // Listado con las opciones de menu actuales (filtradas por grupo) + SettingsGroup current_settings_group_; // Grupo actual + SettingsGroup previous_settings_group_; // Grupo anterior // -- Aspecto -- - Color bgColor_ = SERV_MENU_BG_COLOR; // Color de fondo - Color titleColor_ = SERV_MENU_TITLE_COLOR; // Color del título del menu - Color textColor_ = SERV_MENU_TEXT_COLOR; // Color para el texto de los elementos - Color selectedColor_ = SERV_MENU_SELECTED_COLOR; // Color para el elemento seleccionado - int width_; // Ancho del menú - int height_; // Alto del menu - int lineHeight_; // Espacio entre elementos del menu + Color bg_color_ = SERV_MENU_BG_COLOR; // Color de fondo + Color title_color_ = SERV_MENU_TITLE_COLOR; // Color del título del menu + Color text_color_ = SERV_MENU_TEXT_COLOR; // Color para el texto de los elementos + Color selected_color_ = SERV_MENU_SELECTED_COLOR; // Color para el elemento seleccionado + int width_; // Ancho del menú + int height_; // Alto del menu + int line_height_; // Espacio entre elementos del menu // -- Métodos internos --- void setAnchors(); // Establece el valor de las variables de anclaje void updateCounter(); // Actualiza el contador interno Color getSelectedColor(); // Devuelve el color del elemento seleccionado void initializeOptions(); // Crea todas las opciones del menú de servicio + OptionPairs getOptionPairs(SettingsGroup group) const; + std::vector getOptionsByGroup(SettingsGroup group) const; + void applySettings(SettingsGroup group); + void updateMenu(SettingsGroup group); + void reset(); // --- Patrón Singleton --- ServiceMenu(); // Constructor privado