diff --git a/source/ui/menu_renderer.cpp b/source/ui/menu_renderer.cpp index 1610f67..8b9ca64 100644 --- a/source/ui/menu_renderer.cpp +++ b/source/ui/menu_renderer.cpp @@ -4,13 +4,33 @@ #include "screen.h" // Para param #include -MenuRenderer::MenuRenderer(std::shared_ptr element_text, std::shared_ptr title_text) - : element_text_(std::move(element_text)), title_text_(std::move(title_text)) {} +MenuRenderer::MenuRenderer(const ServiceMenu *menu_state, std::shared_ptr element_text, std::shared_ptr title_text) + : element_text_(std::move(element_text)), title_text_(std::move(title_text)) { init(menu_state); } + +void MenuRenderer::init(const ServiceMenu *menu_state) +{ + aspect_ = menu_state->getAspect(); + switch (aspect_) + { + case ServiceMenu::Aspect::SHADOW: + bg_color_ = SERV_MENU_BG_COLOR_SHADOW; + bg_alpha_ = 255; + break; + + case ServiceMenu::Aspect::ALPHA: + bg_color_ = SERV_MENU_BG_COLOR_ALPHA; + bg_alpha_ = 240; + break; + + default: + break; + } +} void MenuRenderer::render(const ServiceMenu *menu_state) { // Dibuja la sombra - if (menu_state->getAspect() == ServiceMenu::Aspect::ASPECT1) + if (aspect_ == ServiceMenu::Aspect::SHADOW) { SDL_FRect shadowRect = {rect_.x + 5, rect_.y + 5, rect_.w, rect_.h}; SDL_SetRenderDrawColor(Screen::get()->getRenderer(), 0, 0, 0, 64); @@ -18,13 +38,14 @@ void MenuRenderer::render(const ServiceMenu *menu_state) } // Dibuja el fondo - const Uint8 ALPHA = menu_state->getAspect() == ServiceMenu::Aspect::ASPECT1 ? 255 : 255; - SDL_SetRenderDrawColor(Screen::get()->getRenderer(), bg_color_.r, bg_color_.g, bg_color_.b, ALPHA); + SDL_SetRenderDrawColor(Screen::get()->getRenderer(), bg_color_.r, bg_color_.g, bg_color_.b, bg_alpha_); SDL_RenderFillRect(Screen::get()->getRenderer(), &rect_); // Dibuja el borde - SDL_SetRenderDrawColor(Screen::get()->getRenderer(), title_color_.r, title_color_.g, title_color_.b, 255); + const Color BORDER_COLOR = title_color_.darken(); + SDL_SetRenderDrawColor(Screen::get()->getRenderer(), BORDER_COLOR.r, BORDER_COLOR.g, BORDER_COLOR.b, 255); SDL_RenderRect(Screen::get()->getRenderer(), &rect_); + SDL_RenderRect(Screen::get()->getRenderer(), &border_rect_); // Dibuja el título float y = rect_.y + title_padding_; @@ -32,7 +53,7 @@ void MenuRenderer::render(const ServiceMenu *menu_state) // Dibuja la línea separadora y = rect_.y + upper_height_; - SDL_SetRenderDrawColor(Screen::get()->getRenderer(), title_color_.r, title_color_.g, title_color_.b, 255); + SDL_SetRenderDrawColor(Screen::get()->getRenderer(), BORDER_COLOR.r, BORDER_COLOR.g, BORDER_COLOR.b, 255); SDL_RenderLine(Screen::get()->getRenderer(), rect_.x + ServiceMenu::OPTIONS_HORIZONTAL_PADDING_, y, rect_.x + rect_.w - ServiceMenu::OPTIONS_HORIZONTAL_PADDING_, y); // Dibuja las opciones @@ -113,7 +134,7 @@ SDL_FRect MenuRenderer::calculateNewRect(const ServiceMenu *menu_state) const auto &display_options = menu_state->getDisplayOptions(); lower_height_ = ((display_options.size() > 0 ? display_options.size() - 1 : 0) * (options_height_ + options_padding_)) + options_height_ + (lower_padding_ * 2); height_ = upper_height_ + lower_height_; - + return {(param.game.width - width_) / 2.0f, (param.game.height - height_) / 2.0f, (float)width_, (float)height_}; } @@ -130,7 +151,7 @@ void MenuRenderer::resize(const ServiceMenu *menu_state) } else { - rect_ = new_rect; + rect_ = setRect(new_rect); resizing_ = false; } options_y_ = new_rect.y + upper_height_ + lower_padding_; @@ -138,8 +159,7 @@ void MenuRenderer::resize(const ServiceMenu *menu_state) void MenuRenderer::setSize(const ServiceMenu *menu_state) { - SDL_FRect new_rect = calculateNewRect(menu_state); - rect_ = new_rect; + rect_ = setRect(calculateNewRect(menu_state)); resizing_ = false; options_y_ = rect_.y + upper_height_ + lower_padding_; } @@ -152,15 +172,17 @@ void MenuRenderer::updateResizeAnimation() float t = static_cast(resize_anim_step_) / resize_anim_steps_; if (t >= 1.0f) { - rect_ = rect_anim_to_; + rect_ = setRect(rect_anim_to_); resizing_ = false; return; } float ease = 1 - (1 - t) * (1 - t); - rect_.x = rect_anim_from_.x + (rect_anim_to_.x - rect_anim_from_.x) * ease; - rect_.y = rect_anim_from_.y + (rect_anim_to_.y - rect_anim_from_.y) * ease; - rect_.w = rect_anim_from_.w + (rect_anim_to_.w - rect_anim_from_.w) * ease; - rect_.h = rect_anim_from_.h + (rect_anim_to_.h - rect_anim_from_.h) * ease; + SDL_FRect rect = + {rect_anim_from_.x + (rect_anim_to_.x - rect_anim_from_.x) * ease, + rect_anim_from_.y + (rect_anim_to_.y - rect_anim_from_.y) * ease, + rect_anim_from_.w + (rect_anim_to_.w - rect_anim_from_.w) * ease, + rect_anim_from_.h + (rect_anim_to_.h - rect_anim_from_.h) * ease}; + rect_ = setRect(rect); } void MenuRenderer::precalculateMenuWidths(const std::vector> &all_options, const ServiceMenu *menu_state) @@ -209,10 +231,12 @@ void MenuRenderer::updateColorCounter() Color MenuRenderer::getAnimatedSelectedColor() { - static const std::array colors = { - Color(0xFF, 0xFB, 0x8A), Color(0xFF, 0xE4, 0x5D), Color(0xFF, 0xD1, 0x3C), - Color(0xFF, 0xBF, 0x23), Color(0xFF, 0xAA, 0x12), Color(0xE6, 0x9A, 0x08), - Color(0xE6, 0x9A, 0x08), Color(0xFF, 0xAA, 0x12), Color(0xFF, 0xBF, 0x23), - Color(0xFF, 0xD1, 0x3C), Color(0xFF, 0xE4, 0x5D), Color(0xFF, 0xFB, 0x8A)}; - return colors.at(color_counter_ % colors.size()); + static auto colorCycle = generateMirroredCycle(SERV_MENU_SELECTED_COLOR, ColorCycleStyle::HueWave); + return colorCycle.at(color_counter_ % colorCycle.size()); } + +SDL_FRect MenuRenderer::setRect(SDL_FRect rect) +{ + border_rect_ = {rect.x - 1, rect.y + 1, rect.w + 2, rect.h - 2}; + return rect; +} \ No newline at end of file diff --git a/source/ui/menu_renderer.h b/source/ui/menu_renderer.h index b0adfd7..bc92118 100644 --- a/source/ui/menu_renderer.h +++ b/source/ui/menu_renderer.h @@ -14,7 +14,7 @@ class MenuOption; class MenuRenderer { public: - MenuRenderer(std::shared_ptr element_text, std::shared_ptr title_text); + MenuRenderer(const ServiceMenu *menu_state, std::shared_ptr element_text, std::shared_ptr title_text); // Métodos principales de la vista void render(const ServiceMenu *menu_state); @@ -33,8 +33,11 @@ private: std::shared_ptr title_text_; // --- Variables de estado de la vista (layout y animación) --- + ServiceMenu::Aspect aspect_ = ServiceMenu::Aspect::ALPHA; SDL_FRect rect_{}; - Color bg_color_ = SERV_MENU_BG_COLOR; + SDL_FRect border_rect_{}; + Color bg_color_ = SERV_MENU_BG_COLOR_ALPHA; + Uint8 bg_alpha_ = 240; Color title_color_ = SERV_MENU_TITLE_COLOR; Color text_color_ = SERV_MENU_TEXT_COLOR; Color selected_color_ = SERV_MENU_SELECTED_COLOR; @@ -61,6 +64,7 @@ private: int group_menu_widths_[5]{}; // --- Métodos privados de la vista --- + void init(const ServiceMenu *menu_state); void setAnchors(const ServiceMenu *menu_state); SDL_FRect calculateNewRect(const ServiceMenu *menu_state); void resize(const ServiceMenu *menu_state); @@ -70,4 +74,5 @@ private: int getMenuWidthForGroup(ServiceMenu::SettingsGroup group) const; Color getAnimatedSelectedColor(); void updateColorCounter(); + SDL_FRect setRect(SDL_FRect rect); }; diff --git a/source/ui/service_menu.cpp b/source/ui/service_menu.cpp index 28d5000..e9fab10 100644 --- a/source/ui/service_menu.cpp +++ b/source/ui/service_menu.cpp @@ -1,17 +1,17 @@ #include "ui/service_menu.h" -#include "menu_renderer.h" +#include "menu_renderer.h" #include "menu_option.h" #include "resource.h" #include "audio.h" #include "section.h" -#include "screen.h" +#include "screen.h" #include "asset.h" // Singleton -ServiceMenu* ServiceMenu::instance_ = nullptr; +ServiceMenu *ServiceMenu::instance_ = nullptr; void ServiceMenu::init() { ServiceMenu::instance_ = new ServiceMenu(); } void ServiceMenu::destroy() { delete ServiceMenu::instance_; } -ServiceMenu* ServiceMenu::get() { return ServiceMenu::instance_; } +ServiceMenu *ServiceMenu::get() { return ServiceMenu::instance_; } // Constructor ServiceMenu::ServiceMenu() @@ -21,23 +21,27 @@ ServiceMenu::ServiceMenu() auto element_text = Resource::get()->getText("04b_25_flat"); auto title_text = Resource::get()->getText("04b_25_flat_2x"); - renderer_ = std::make_unique(element_text, title_text); + renderer_ = std::make_unique(this, element_text, title_text); restart_message_ui_ = std::make_unique(element_text, Lang::getText("[SERVICE_MENU] NEED_RESTART_MESSAGE"), SERV_MENU_TITLE_COLOR); - + reset(); } -void ServiceMenu::toggle() { +void ServiceMenu::toggle() +{ enabled_ = !enabled_; - if (!enabled_) { + if (!enabled_) + { reset(); } } -void ServiceMenu::render() { - if (!enabled_) return; +void ServiceMenu::render() +{ + if (!enabled_) + return; renderer_->render(this); - + // El mensaje de reinicio se dibuja por encima, así que lo gestionamos aquí const float MSG_X = param.game.game_area.center_x; const float MSG_Y = renderer_->getRect().y + 39.0f; @@ -45,20 +49,24 @@ void ServiceMenu::render() { restart_message_ui_->render(); } -void ServiceMenu::update() { - if (!enabled_) return; +void ServiceMenu::update() +{ + if (!enabled_) + return; renderer_->update(this); bool now_pending = Options::pending_changes.has_pending_changes; - if (now_pending != last_pending_changes_) { + if (now_pending != last_pending_changes_) + { now_pending ? restart_message_ui_->show() : restart_message_ui_->hide(); last_pending_changes_ = now_pending; } restart_message_ui_->update(); } -void ServiceMenu::reset() { +void ServiceMenu::reset() +{ selected_ = 0; main_menu_selected_ = 0; current_settings_group_ = SettingsGroup::MAIN; @@ -69,45 +77,60 @@ void ServiceMenu::reset() { } // --- Lógica de Navegación --- -void ServiceMenu::setSelectorUp() { - if (display_options_.empty()) return; +void ServiceMenu::setSelectorUp() +{ + if (display_options_.empty()) + return; selected_ = (selected_ > 0) ? selected_ - 1 : display_options_.size() - 1; playMenuSound(); } -void ServiceMenu::setSelectorDown() { - if (display_options_.empty()) return; +void ServiceMenu::setSelectorDown() +{ + if (display_options_.empty()) + return; selected_ = (selected_ + 1) % display_options_.size(); playMenuSound(); } -void ServiceMenu::adjustOption(bool adjust_up) { - if (display_options_.empty()) return; - auto& selected_option = display_options_.at(selected_); - if (selected_option->getBehavior() == MenuOption::Behavior::ADJUST) { +void ServiceMenu::adjustOption(bool adjust_up) +{ + if (display_options_.empty()) + return; + auto &selected_option = display_options_.at(selected_); + if (selected_option->getBehavior() == MenuOption::Behavior::ADJUST) + { selected_option->adjustValue(adjust_up); applySettings(); playMenuSound(); } } -void ServiceMenu::selectOption() { - if (display_options_.empty()) return; - if (current_settings_group_ == SettingsGroup::MAIN) main_menu_selected_ = selected_; - auto& selected_option = display_options_.at(selected_); - if (auto folder = dynamic_cast(selected_option)) { +void ServiceMenu::selectOption() +{ + if (display_options_.empty()) + return; + if (current_settings_group_ == SettingsGroup::MAIN) + main_menu_selected_ = selected_; + auto &selected_option = display_options_.at(selected_); + if (auto folder = dynamic_cast(selected_option)) + { previous_settings_group_ = current_settings_group_; current_settings_group_ = folder->getTargetGroup(); selected_ = 0; updateMenu(); - } else if (selected_option->getBehavior() == MenuOption::Behavior::SELECT) { + } + else if (selected_option->getBehavior() == MenuOption::Behavior::SELECT) + { selected_option->executeAction(); } playMenuSound(); } -void ServiceMenu::moveBack() { - if (current_settings_group_ == SettingsGroup::MAIN) { +void ServiceMenu::moveBack() +{ + if (current_settings_group_ == SettingsGroup::MAIN) + { enabled_ = false; return; } @@ -119,21 +142,25 @@ void ServiceMenu::moveBack() { // --- Lógica Interna --- -void ServiceMenu::updateMenu() { +void ServiceMenu::updateMenu() +{ title_ = settingsGroupToString(current_settings_group_); AdjustListValues(); // Actualiza las opciones visibles display_options_.clear(); - for (auto& option : options_) { - if (option->getGroup() == current_settings_group_ && !option->isHidden()) { + for (auto &option : options_) + { + if (option->getGroup() == current_settings_group_ && !option->isHidden()) + { display_options_.push_back(option.get()); } } - + // Actualiza los pares de strings para el renderer option_pairs_.clear(); - for (const auto& option : display_options_) { + for (const auto &option : display_options_) + { option_pairs_.emplace_back(option->getCaption(), option->getValueAsString()); } @@ -141,12 +168,18 @@ void ServiceMenu::updateMenu() { renderer_->onLayoutChanged(this); } -void ServiceMenu::applySettings() { - if (current_settings_group_ == SettingsGroup::VIDEO) Screen::get()->applySettings(); - if (current_settings_group_ == SettingsGroup::AUDIO) Audio::get()->applySettings(); - if (current_settings_group_ == SettingsGroup::SETTINGS) { - for(auto& option : options_){ - if(option->getCaption() == Lang::getText("[SERVICE_MENU] SHUTDOWN]")){ +void ServiceMenu::applySettings() +{ + if (current_settings_group_ == SettingsGroup::VIDEO) + Screen::get()->applySettings(); + if (current_settings_group_ == SettingsGroup::AUDIO) + Audio::get()->applySettings(); + if (current_settings_group_ == SettingsGroup::SETTINGS) + { + for (auto &option : options_) + { + if (option->getCaption() == Lang::getText("[SERVICE_MENU] SHUTDOWN]")) + { option->setHidden(!Options::settings.shutdown_enabled); updateMenu(); // El menú debe refrescarse si algo se oculta break; @@ -155,28 +188,34 @@ void ServiceMenu::applySettings() { } // Refresca los pares de strings por si un valor cambió option_pairs_.clear(); - for (const auto& option : display_options_) { + for (const auto &option : display_options_) + { option_pairs_.emplace_back(option->getCaption(), option->getValueAsString()); } } // --- Getters y otros --- -ServiceMenu::GroupAlignment ServiceMenu::getCurrentGroupAlignment() const { - switch (current_settings_group_) { - case SettingsGroup::VIDEO: - case SettingsGroup::AUDIO: - case SettingsGroup::SETTINGS: - return GroupAlignment::LEFT; - default: - return GroupAlignment::CENTERED; +ServiceMenu::GroupAlignment ServiceMenu::getCurrentGroupAlignment() const +{ + switch (current_settings_group_) + { + case SettingsGroup::VIDEO: + case SettingsGroup::AUDIO: + case SettingsGroup::SETTINGS: + return GroupAlignment::LEFT; + default: + return GroupAlignment::CENTERED; } } -size_t ServiceMenu::countOptionsInGroup(SettingsGroup group) const { +size_t ServiceMenu::countOptionsInGroup(SettingsGroup group) const +{ size_t count = 0; - for (const auto& option : options_) { - if (option->getGroup() == group && !option->isHidden()) { + for (const auto &option : options_) + { + if (option->getGroup() == group && !option->isHidden()) + { count++; } } @@ -225,7 +264,7 @@ void ServiceMenu::initializeOptions() options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] SETTINGS"), SettingsGroup::MAIN, SettingsGroup::SETTINGS)); options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] SYSTEM"), SettingsGroup::MAIN, SettingsGroup::SYSTEM)); - //precalculateMenuWidths(); + // precalculateMenuWidths(); } // Sincroniza los valores de las opciones tipo lista @@ -240,7 +279,6 @@ void ServiceMenu::AdjustListValues() } } - // Reproduce el sonido de navegación del menú void ServiceMenu::playMenuSound() { Audio::get()->playSound("clock.wav"); } @@ -263,4 +301,3 @@ std::string ServiceMenu::settingsGroupToString(SettingsGroup group) const return Lang::getText("[SERVICE_MENU] TITLE"); } } - diff --git a/source/ui/service_menu.h b/source/ui/service_menu.h index ca073e9..8cc5a17 100644 --- a/source/ui/service_menu.h +++ b/source/ui/service_menu.h @@ -14,7 +14,7 @@ class MenuRenderer; // <-- Nuevo class ServiceMenu { public: - enum class Aspect { ASPECT1, ASPECT2 }; + enum class Aspect { SHADOW, ALPHA }; enum class SettingsGroup { VIDEO, AUDIO, SETTINGS, SYSTEM, MAIN }; enum class GroupAlignment { CENTERED, LEFT }; @@ -28,8 +28,8 @@ public: static ServiceMenu* get(); void toggle(); - void render(); // Ahora solo delega - void update(); // Ahora delega la parte visual + void render(); + void update(); void reset(); // --- Lógica de navegación --- @@ -60,7 +60,7 @@ private: SettingsGroup current_settings_group_; SettingsGroup previous_settings_group_; - Aspect aspect_ = Aspect::ASPECT1; + Aspect aspect_ = Aspect::ALPHA; std::string title_; size_t selected_ = 0; size_t main_menu_selected_ = 0; diff --git a/source/utils.cpp b/source/utils.cpp index be30f09..a8a8dd2 100644 --- a/source/utils.cpp +++ b/source/utils.cpp @@ -15,29 +15,6 @@ // Variables Overrides overrides = Overrides(); -// Colores -const Color BG_COLOR = Color(0X27, 0X27, 0X36); -const Color NO_COLOR = Color(0XFF, 0XFF, 0XFF); -const Color SHADOW_TEXT_COLOR = Color(0X43, 0X43, 0X4F); -const Color SEPARATOR_COLOR = Color(0X0D, 0X1A, 0X2B); -const Color SCOREBOARD_EASY_COLOR = Color(0X4B, 0X69, 0X2F); -const Color SCOREBOARD_NORMAL_COLOR = Color(0X2E, 0X3F, 0X47); -const Color SCOREBOARD_HARD_COLOR = Color(0X76, 0X42, 0X8A); -const Color FLASH_COLOR = Color(0XFF, 0XFF, 0XFF); -const Color FADE_COLOR = Color(0X27, 0X27, 0X36); -const Color ORANGE_COLOR = Color(0XFF, 0X7A, 0X00); -const Color ORANGE_SOFT_COLOR = Color(0XFF, 0XA0, 0X33); -const Color ORANGE_SHADOW_COLOR = ORANGE_SOFT_COLOR.darken(100); -const Color GREEN_COLOR = Color(0X5B, 0XEC, 0X95); -const Color BLUE_SKY_COLOR = Color(0X02, 0X88, 0XD1); -const Color PINK_SKY_COLOR = Color(0XFF, 0X6B, 0X97); -const Color GREEN_SKY_COLOR = Color(0X00, 0X79, 0X6B); -const Color SERV_MENU_TITLE_COLOR = Color(0XFF, 0XFF, 0XFF); -const Color SERV_MENU_TEXT_COLOR = Color(0XFF, 0XFF, 0XFF); -const Color SERV_MENU_SELECTED_COLOR = Color(0XFF, 0XFF, 0X00);; -const Color SERV_MENU_BG_COLOR = Color(0x1E, 0x1E, 0x1E); -const Color DEBUG_COLOR = Color(0xFF, 0xFF, 0x00); - // Obtiene un color del vector de colores imitando al Coche Fantástico Color getColorLikeKnightRider(const std::vector &colors, int counter_) { @@ -313,18 +290,18 @@ void printWithDots(const std::string &text1, const std::string &text2, const std // Calcula el ancho del campo para text2 restando la longitud de text1 y text3 size_t field_width = TOTAL_WIDTH > (text1.size() + text3.size()) - ? TOTAL_WIDTH - text1.size() - text3.size() - : 0; + ? TOTAL_WIDTH - text1.size() - text3.size() + : 0; // Prepara el bloque a imprimir a partir de text2 std::string field_text; if (text2.size() < field_width) - { + { // Si text2 es más corto, lo rellenamos a la derecha con puntos field_text = text2 + std::string(field_width - text2.size(), '.'); } else - { + { // Si es demasiado largo, lo cortamos field_text = text2.substr(0, field_width); } @@ -414,4 +391,167 @@ std::string getPath(const std::string &full_path) { std::filesystem::path path(full_path); return path.parent_path().string(); -} \ No newline at end of file +} + +constexpr HSV rgbToHsv(Color color) +{ + float r = color.r / 255.0f; + float g = color.g / 255.0f; + float b = color.b / 255.0f; + + float max = fmaxf(fmaxf(r, g), b); + float min = fminf(fminf(r, g), b); + float delta = max - min; + + float h = 0.0f; + if (delta > 0.00001f) + { + if (max == r) + h = fmodf((g - b) / delta, 6.0f); + else if (max == g) + h = ((b - r) / delta) + 2.0f; + else + h = ((r - g) / delta) + 4.0f; + h *= 60.0f; + if (h < 0.0f) + h += 360.0f; + } + + float s = (max <= 0.0f) ? 0.0f : delta / max; + float v = max; + + return {h, s, v}; +} + +constexpr Color hsvToRgb(HSV hsv) +{ + float c = hsv.v * hsv.s; + float x = c * (1 - fabsf(fmodf(hsv.h / 60.0f, 2) - 1)); + float m = hsv.v - c; + + float r = 0, g = 0, b = 0; + + if (hsv.h < 60) + { + r = c; + g = x; + b = 0; + } + else if (hsv.h < 120) + { + r = x; + g = c; + b = 0; + } + else if (hsv.h < 180) + { + r = 0; + g = c; + b = x; + } + else if (hsv.h < 240) + { + r = 0; + g = x; + b = c; + } + else if (hsv.h < 300) + { + r = x; + g = 0; + b = c; + } + else + { + r = c; + g = 0; + b = x; + } + + return Color( + static_cast(roundf((r + m) * 255)), + static_cast(roundf((g + m) * 255)), + static_cast(roundf((b + m) * 255))); +} + +/*ColorCycle generateMirroredCycle(Color base) +{ + ColorCycle result{}; + HSV baseHSV = rgbToHsv(base); + + for (size_t i = 0; i < COLOR_CYCLE_SIZE; ++i) + { + float t = static_cast(i) / (COLOR_CYCLE_SIZE - 1); + float hueShift = 25.0f * t; // reducido de 45 a 25 + float satShift = 0.0f; // sin cambio de saturación + float valShift = 0.07f * sinf(t * 3.1415926f); // brillo más suave + + HSV adjusted = { + fmodf(baseHSV.h + hueShift, 360.0f), + fminf(1.0f, fmaxf(0.0f, baseHSV.s + satShift)), + fminf(1.0f, fmaxf(0.0f, baseHSV.v + valShift))}; + + Color c = hsvToRgb(adjusted); + result[i] = c; + result[2 * COLOR_CYCLE_SIZE - 1 - i] = c; // espejo + } + + return result; +}*/ + +ColorCycle generateMirroredCycle(Color base, ColorCycleStyle style) +{ + ColorCycle result{}; + HSV baseHSV = rgbToHsv(base); + + for (size_t i = 0; i < COLOR_CYCLE_SIZE; ++i) + { + float t = static_cast(i) / (COLOR_CYCLE_SIZE - 1); // 0 → 1 + float hueShift = 0.0f; + float satShift = 0.0f; + float valShift = 0.0f; + + switch (style) + { + case ColorCycleStyle::SubtlePulse: + // Solo brillo suave + valShift = 0.07f * sinf(t * M_PI); + break; + + case ColorCycleStyle::HueWave: + // Oscilación leve de tono + hueShift = 15.0f * (t - 0.5f) * 2.0f; + valShift = 0.05f * sinf(t * M_PI); + break; + + case ColorCycleStyle::Vibrant: + // Cambios fuertes en tono y brillo + hueShift = 35.0f * sinf(t * M_PI); + valShift = 0.2f * sinf(t * M_PI); + satShift = -0.2f * sinf(t * M_PI); + break; + + case ColorCycleStyle::DarkenGlow: + // Se oscurece al centro + valShift = -0.15f * sinf(t * M_PI); + break; + + case ColorCycleStyle::LightFlash: + // Se ilumina al centro + valShift = 0.25f * sinf(t * M_PI); + break; + } + + HSV adjusted = { + fmodf(baseHSV.h + hueShift + 360.0f, 360.0f), + fminf(1.0f, fmaxf(0.0f, baseHSV.s + satShift)), + fminf(1.0f, fmaxf(0.0f, baseHSV.v + valShift)) + }; + + Color c = hsvToRgb(adjusted); + result[i] = c; + result[2 * COLOR_CYCLE_SIZE - 1 - i] = c; // espejo + } + + return result; +} diff --git a/source/utils.h b/source/utils.h index 2986acf..f91950f 100644 --- a/source/utils.h +++ b/source/utils.h @@ -7,10 +7,12 @@ #include // Para max, min #include // Para string #include // Para vector +#include // Para array // --- Constantes --- constexpr int BLOCK = 8; constexpr int TOTAL_DEMO_DATA = 2000; +constexpr size_t COLOR_CYCLE_SIZE = 6; // Mitad del ciclo espejado // --- Estructuras y tipos --- struct Overrides @@ -33,7 +35,7 @@ struct Circle : x(xCoord), y(yCoord), r(radius) {} }; -// Estructura para definir un color +// Estructura para definir un color RGB struct Color { Uint8 r, g, b; @@ -41,14 +43,14 @@ struct Color explicit constexpr Color(Uint8 red, Uint8 green, Uint8 blue) : r(red), g(green), b(blue) {} constexpr Color getInverse() const { return Color(255 - r, 255 - g, 255 - b); } - Color lighten(int amount = 50) const + constexpr Color lighten(int amount = 50) const { return Color( std::min(255, r + amount), std::min(255, g + amount), std::min(255, b + amount)); } - Color darken(int amount = 50) const + constexpr Color darken(int amount = 50) const { return Color( std::max(0, r - amount), @@ -57,6 +59,23 @@ struct Color } }; +// Estructura para definir un color HSV +struct HSV +{ + float h, s, v; +}; + +// Estructura para definir el ciclo de color +enum class ColorCycleStyle +{ + SubtlePulse, // Variación leve en brillo (por defecto) + HueWave, // Variación suave en tono (sin verde) + Vibrant, // Cambios agresivos en tono y brillo + DarkenGlow, // Oscurece hacia el centro y regresa + LightFlash // Ilumina hacia el centro y regresa +}; + + // Posiciones de las notificaciones enum class NotifyPosition { @@ -108,33 +127,41 @@ struct Zone float third_quarter_y; // Anclaje al 75% del eje Y }; +// --- Alias --- +using ColorCycle = std::array; + // --- Funciones utilitarias --- // Colores -extern const Color BG_COLOR; -extern const Color NO_COLOR; -extern const Color SHADOW_TEXT_COLOR; -extern const Color SEPARATOR_COLOR; -extern const Color SCOREBOARD_EASY_COLOR; -extern const Color SCOREBOARD_NORMAL_COLOR; -extern const Color SCOREBOARD_HARD_COLOR; -extern const Color FLASH_COLOR; -extern const Color FADE_COLOR; -extern const Color ORANGE_COLOR; -extern const Color ORANGE_SOFT_COLOR; -extern const Color ORANGE_SHADOW_COLOR; -extern const Color GREEN_COLOR; -extern const Color BLUE_SKY_COLOR; -extern const Color PINK_SKY_COLOR; -extern const Color GREEN_SKY_COLOR; -extern const Color SERV_MENU_TITLE_COLOR; -extern const Color SERV_MENU_TEXT_COLOR; -extern const Color SERV_MENU_SELECTED_COLOR; -extern const Color SERV_MENU_BG_COLOR; -extern const Color DEBUG_COLOR; +constexpr Color BG_COLOR = Color(0X27, 0X27, 0X36); +constexpr Color NO_COLOR = Color(0XFF, 0XFF, 0XFF); +constexpr Color SHADOW_TEXT_COLOR = Color(0X43, 0X43, 0X4F); +constexpr Color SEPARATOR_COLOR = Color(0X0D, 0X1A, 0X2B); +constexpr Color SCOREBOARD_EASY_COLOR = Color(0X4B, 0X69, 0X2F); +constexpr Color SCOREBOARD_NORMAL_COLOR = Color(0X2E, 0X3F, 0X47); +constexpr Color SCOREBOARD_HARD_COLOR = Color(0X76, 0X42, 0X8A); +constexpr Color FLASH_COLOR = Color(0XFF, 0XFF, 0XFF); +constexpr Color FADE_COLOR = Color(0X27, 0X27, 0X36); +constexpr Color ORANGE_COLOR = Color(0XFF, 0X7A, 0X00); +constexpr Color ORANGE_SOFT_COLOR = Color(0XFF, 0XA0, 0X33); +constexpr Color ORANGE_SHADOW_COLOR = ORANGE_SOFT_COLOR.darken(100); +constexpr Color GREEN_COLOR = Color(0X5B, 0XEC, 0X95); +constexpr Color BLUE_SKY_COLOR = Color(0X02, 0X88, 0XD1); +constexpr Color PINK_SKY_COLOR = Color(0XFF, 0X6B, 0X97); +constexpr Color GREEN_SKY_COLOR = Color(0X00, 0X79, 0X6B); +constexpr Color SERV_MENU_TITLE_COLOR = Color(0x99, 0xFF, 0x62); +constexpr Color SERV_MENU_TEXT_COLOR = Color(255, 255, 255); +constexpr Color SERV_MENU_SELECTED_COLOR = Color(255, 170, 18).lighten(); +//constexpr Color SERV_MENU_SELECTED_COLOR = SERV_MENU_TITLE_COLOR.darken(); +constexpr Color SERV_MENU_BG_COLOR_SHADOW = Color(0x1E, 0x1E, 0x1E); +constexpr Color SERV_MENU_BG_COLOR_ALPHA = Color(0x00, 0x00, 0x00); +constexpr Color DEBUG_COLOR = Color(0xFF, 0xFF, 0x00); // Colores y gráficos Color getColorLikeKnightRider(const std::vector &colors, int counter_); +constexpr HSV rgbToHsv(Color color); +constexpr Color hsvToRgb(HSV hsv); +ColorCycle generateMirroredCycle(Color base, ColorCycleStyle style = ColorCycleStyle::SubtlePulse); // Colisiones y geometría double distanceSquared(int x1, int y1, int x2, int y2);