reestructuració

This commit is contained in:
2026-04-14 13:26:22 +02:00
parent 4ac34b8583
commit 4429cd92c1
143 changed files with 0 additions and 0 deletions

186
source/utils/color.cpp Normal file
View File

@@ -0,0 +1,186 @@
#define _USE_MATH_DEFINES
#include "color.hpp"
#include <cctype> // Para isxdigit
#include <cmath> // Para sinf, fmaxf, fminf, M_PI, fmodf, roundf, fmod
#include <cstdint> // Para uint8_t
#include <stdexcept> // Para invalid_argument
#include <string> // Para basic_string, stoi, string
// Método estático para crear Color desde string hexadecimal
auto Color::fromHex(const std::string& hex_str) -> Color {
std::string hex = hex_str;
// Quitar '#' si existe
if (!hex.empty() && hex[0] == '#') {
hex = hex.substr(1);
}
// Verificar longitud válida (6 para RGB o 8 para RGBA)
if (hex.length() != HEX_RGB_LENGTH && hex.length() != HEX_RGBA_LENGTH) {
throw std::invalid_argument("String hexadecimal debe tener 6 o 8 caracteres");
}
// Verificar que todos los caracteres sean hexadecimales válidos
for (char c : hex) {
if (std::isxdigit(c) == 0) {
throw std::invalid_argument("String contiene caracteres no hexadecimales");
}
}
// Convertir cada par de caracteres a valores RGB(A)
Uint8 r = static_cast<Uint8>(std::stoi(hex.substr(0, HEX_COMPONENT_LENGTH), nullptr, HEX_BASE));
Uint8 g = static_cast<Uint8>(std::stoi(hex.substr(HEX_COMPONENT_LENGTH, HEX_COMPONENT_LENGTH), nullptr, HEX_BASE));
Uint8 b = static_cast<Uint8>(std::stoi(hex.substr(HEX_COMPONENT_LENGTH * 2, HEX_COMPONENT_LENGTH), nullptr, HEX_BASE));
Uint8 a = DEFAULT_ALPHA; // Alpha por defecto
// Si tiene 8 caracteres, extraer el alpha
if (hex.length() == HEX_RGBA_LENGTH) {
a = static_cast<Uint8>(std::stoi(hex.substr(HEX_COMPONENT_LENGTH * 3, HEX_COMPONENT_LENGTH), nullptr, HEX_BASE));
}
return Color(r, g, b, a);
}
// Implementaciones de métodos estáticos de Color
constexpr auto Color::RGB_TO_HSV(Color color) -> HSV {
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 = h, .s = s, .v = v};
}
constexpr auto Color::HSV_TO_RGB(HSV hsv) -> Color {
float c = hsv.v * hsv.s;
float x = c * (1 - std::abs(std::fmod(hsv.h / 60.0F, 2) - 1));
float m = hsv.v - c;
float r = 0;
float g = 0;
float 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<uint8_t>(roundf((r + m) * 255)),
static_cast<uint8_t>(roundf((g + m) * 255)),
static_cast<uint8_t>(roundf((b + m) * 255)));
}
// Implementaciones del namespace Colors
namespace Colors {
// Obtiene un color del vector de colores imitando al Coche Fantástico
auto getColorLikeKnightRider(const std::vector<Color>& colors, int counter) -> Color {
int cycle_length = (colors.size() * 2) - 2;
size_t n = counter % cycle_length;
size_t index;
if (n < colors.size()) {
index = n; // Avanza: 0,1,2,3
} else {
index = (2 * (colors.size() - 1)) - n; // Retrocede: 2,1
}
return colors[index];
}
auto generateMirroredCycle(Color base, ColorCycleStyle style) -> Cycle {
Cycle result{};
HSV base_hsv = Color::RGB_TO_HSV(base);
for (size_t i = 0; i < CYCLE_SIZE; ++i) {
float t = static_cast<float>(i) / (CYCLE_SIZE - 1); // 0 → 1
float hue_shift = 0.0F;
float sat_shift = 0.0F;
float val_shift = 0.0F;
switch (style) {
case ColorCycleStyle::SUBTLE_PULSE:
// Solo brillo suave
val_shift = 0.07F * sinf(t * M_PI);
break;
case ColorCycleStyle::HUE_WAVE:
// Oscilación leve de tono
hue_shift = 15.0F * (t - 0.5F) * 2.0F;
val_shift = 0.05F * sinf(t * M_PI);
break;
case ColorCycleStyle::VIBRANT:
// Cambios fuertes en tono y brillo
hue_shift = 35.0F * sinf(t * M_PI);
val_shift = 0.2F * sinf(t * M_PI);
sat_shift = -0.2F * sinf(t * M_PI);
break;
case ColorCycleStyle::DARKEN_GLOW:
// Se oscurece al centro
val_shift = -0.15F * sinf(t * M_PI);
break;
case ColorCycleStyle::LIGHT_FLASH:
// Se ilumina al centro
val_shift = 0.25F * sinf(t * M_PI);
break;
}
HSV adjusted = {
.h = fmodf(base_hsv.h + hue_shift + 360.0F, 360.0F),
.s = fminf(1.0F, fmaxf(0.0F, base_hsv.s + sat_shift)),
.v = fminf(1.0F, fmaxf(0.0F, base_hsv.v + val_shift))};
Color c = Color::HSV_TO_RGB(adjusted);
result[i] = c;
result[(2 * CYCLE_SIZE) - 1 - i] = c; // espejo
}
return result;
}
} // namespace Colors

161
source/utils/color.hpp Normal file
View File

@@ -0,0 +1,161 @@
// IWYU pragma: no_include <bits/std_abs.h>
#pragma once
#include <SDL3/SDL.h> // Para Uint8
#include <algorithm> // Para max, min
#include <array> // Para array
#include <cstdlib> // Para size_t, abs
#include <string> // Para string
#include <vector> // Para vector
// --- Estructura HSV: define un color en formato HSV ---
struct HSV {
float h; // Matiz (Hue)
float s; // Saturación (Saturation)
float v; // Valor (Value)
};
// --- Estructura Color: define un color RGBA ---
struct Color {
private:
static constexpr int DEFAULT_LIGHTEN_AMOUNT = 50;
static constexpr int DEFAULT_DARKEN_AMOUNT = 50;
static constexpr int DEFAULT_APPROACH_STEP = 1;
static constexpr size_t HEX_RGB_LENGTH = 6;
static constexpr size_t HEX_RGBA_LENGTH = 8;
static constexpr int HEX_BASE = 16;
static constexpr size_t HEX_COMPONENT_LENGTH = 2;
public:
static constexpr Uint8 MAX_COLOR_VALUE = 255;
static constexpr Uint8 MIN_COLOR_VALUE = 0;
static constexpr Uint8 DEFAULT_ALPHA = 255;
static constexpr Uint8 MAX_ALPHA_VALUE = 255;
static constexpr Uint8 MIN_ALPHA_VALUE = 0;
Uint8 r, g, b, a;
constexpr Color()
: r(MIN_COLOR_VALUE),
g(MIN_COLOR_VALUE),
b(MIN_COLOR_VALUE),
a(DEFAULT_ALPHA) {}
explicit constexpr Color(Uint8 red, Uint8 green, Uint8 blue, Uint8 alpha = DEFAULT_ALPHA)
: r(red),
g(green),
b(blue),
a(alpha) {}
[[nodiscard]] constexpr auto INVERSE() const -> Color {
return Color(MAX_COLOR_VALUE - r, MAX_COLOR_VALUE - g, MAX_COLOR_VALUE - b, a);
}
[[nodiscard]] constexpr auto LIGHTEN(int amount = DEFAULT_LIGHTEN_AMOUNT) const -> Color {
return Color(
std::min(static_cast<int>(MAX_COLOR_VALUE), r + amount),
std::min(static_cast<int>(MAX_COLOR_VALUE), g + amount),
std::min(static_cast<int>(MAX_COLOR_VALUE), b + amount),
a);
}
[[nodiscard]] constexpr auto DARKEN(int amount = DEFAULT_DARKEN_AMOUNT) const -> Color {
return Color(
std::max(static_cast<int>(MIN_COLOR_VALUE), r - amount),
std::max(static_cast<int>(MIN_COLOR_VALUE), g - amount),
std::max(static_cast<int>(MIN_COLOR_VALUE), b - amount),
a);
}
// Método estático para crear Color desde string hexadecimal
static auto fromHex(const std::string& hex_str) -> Color;
// Conversiones de formato de color
[[nodiscard]] constexpr static auto RGB_TO_HSV(Color color) -> HSV;
[[nodiscard]] constexpr static auto HSV_TO_RGB(HSV hsv) -> Color;
[[nodiscard]] constexpr auto IS_EQUAL_TO(const Color& other) const -> bool {
return r == other.r && g == other.g && b == other.b && a == other.a;
}
[[nodiscard]] constexpr auto APPROACH_TO(const Color& target, int step = DEFAULT_APPROACH_STEP) const -> Color {
auto approach_component = [step](Uint8 current, Uint8 target_val) -> Uint8 {
if (std::abs(current - target_val) <= step) {
return target_val;
}
return (current < target_val) ? current + step : current - step;
};
Uint8 new_r = approach_component(r, target.r);
Uint8 new_g = approach_component(g, target.g);
Uint8 new_b = approach_component(b, target.b);
Uint8 new_a = approach_component(a, target.a);
return Color(new_r, new_g, new_b, new_a);
}
// Interpolación lineal hacia otro color (t=0.0: this, t=1.0: target)
[[nodiscard]] constexpr auto LERP(const Color& target, float t) const -> Color {
// Asegurar que t esté en el rango [0.0, 1.0]
t = std::clamp(t, 0.0F, 1.0F);
// Interpolación lineal para cada componente
auto lerp_component = [t](Uint8 start, Uint8 end) -> Uint8 {
return static_cast<Uint8>(start + ((end - start) * t));
};
return Color(
lerp_component(r, target.r),
lerp_component(g, target.g),
lerp_component(b, target.b),
lerp_component(a, target.a));
}
// Sobrecarga para aceptar componentes RGBA directamente
[[nodiscard]] constexpr auto LERP(Uint8 red, Uint8 green, Uint8 blue, Uint8 alpha, float t) const -> Color {
return LERP(Color(red, green, blue, alpha), t);
}
// Convierte el color a un entero de 32 bits en formato RGBA
[[nodiscard]] constexpr auto TO_UINT32() const -> Uint32 {
return (static_cast<Uint32>(r) << 24) |
(static_cast<Uint32>(g) << 16) |
(static_cast<Uint32>(b) << 8) |
static_cast<Uint32>(a);
}
};
// --- Enum ColorCycleStyle: define estilos de ciclo de color ---
enum class ColorCycleStyle {
SUBTLE_PULSE, // Variación leve en brillo (por defecto)
HUE_WAVE, // Variación suave en tono (sin verde)
VIBRANT, // Cambios agresivos en tono y brillo
DARKEN_GLOW, // Oscurece hacia el centro y regresa
LIGHT_FLASH // Ilumina hacia el centro y regresa
};
// --- Namespace Colors: constantes y utilidades de color ---
namespace Colors {
// --- Constantes ---
constexpr size_t CYCLE_SIZE = 6; // Mitad del ciclo espejado
// --- Alias ---
using Cycle = std::array<Color, 2 * CYCLE_SIZE>;
// --- Colores predefinidos ---
constexpr Color NO_COLOR_MOD = Color(0XFF, 0XFF, 0XFF);
constexpr Color SHADOW_TEXT = Color(0X43, 0X43, 0X4F);
constexpr Color TITLE_SHADOW_TEXT = Color(0x14, 0x87, 0xc4);
constexpr Color ORANGE_TEXT = Color(0XFF, 0X7A, 0X00);
constexpr Color FLASH = Color(0XFF, 0XFF, 0XFF);
constexpr Color BLUE_SKY = Color(0X02, 0X88, 0XD1);
constexpr Color PINK_SKY = Color(0XFF, 0X6B, 0X97);
constexpr Color GREEN_SKY = Color(0X00, 0X79, 0X6B);
// --- Funciones ---
auto getColorLikeKnightRider(const std::vector<Color>& colors, int counter) -> Color;
auto generateMirroredCycle(Color base, ColorCycleStyle style = ColorCycleStyle::SUBTLE_PULSE) -> Cycle;
} // namespace Colors

274
source/utils/param.cpp Normal file
View File

@@ -0,0 +1,274 @@
#include "param.hpp"
#include <SDL3/SDL.h> // Para SDL_LogWarn, SDL_LogCategory, SDL_LogError
#include <fstream> // Para basic_istream, basic_ifstream, ifstream, istringstream
#include <functional> // Para function
#include <iostream> // Para std::cout
#include <sstream> // Para basic_istringstream
#include <stdexcept> // Para runtime_error
#include <string> // Para string, basic_string, stoi, stof, hash, allocator, operator==, char_traits, operator+, operator>>, getline
#include <unordered_map> // Para unordered_map, operator==, _Node_iterator_base
#include <utility> // Para pair
#include "color.hpp" // Para Color
#include "ui/notifier.hpp" // Para Notifier
#include "utils.hpp" // Para Zone, stringToBool, getFileName
// Variable global - ahora se inicializa automáticamente con valores por defecto
Param param;
// Declaraciones de funciones privadas
namespace {
auto setParams(const std::string& var, const std::string& value) -> bool;
}
// Implementación del método privado de Param
void Param::precalculateZones() {
// playArea - cálculos basados en el rectángulo actual
game.play_area.center_x = game.play_area.rect.w / 2;
game.play_area.first_quarter_x = game.play_area.rect.w / 4;
game.play_area.third_quarter_x = game.play_area.rect.w / 4 * 3;
game.play_area.center_y = game.play_area.rect.h / 2;
game.play_area.first_quarter_y = game.play_area.rect.h / 4;
game.play_area.third_quarter_y = game.play_area.rect.h / 4 * 3;
// gameArea - cálculos basados en width y height actuales
game.game_area.rect = {.x = 0, .y = 0, .w = game.width, .h = game.height};
game.game_area.center_x = game.game_area.rect.w / 2;
game.game_area.first_quarter_x = game.game_area.rect.w / 4;
game.game_area.third_quarter_x = game.game_area.rect.w / 4 * 3;
game.game_area.center_y = game.game_area.rect.h / 2;
game.game_area.first_quarter_y = game.game_area.rect.h / 4;
game.game_area.third_quarter_y = game.game_area.rect.h / 4 * 3;
}
// Carga los parámetros desde un archivo
void loadParamsFromFile(const std::string& file_path) {
// Los parámetros ya están inicializados con valores por defecto
// Solo necesitamos abrir el archivo y sobrescribir los valores que aparezcan
std::ifstream file(file_path);
if (!file.is_open()) {
std::cout << "Error: No se pudo abrir el archivo " << file_path << '\n';
throw std::runtime_error("No se pudo abrir el archivo: " + file_path);
}
std::string line;
std::string param_name;
std::string param_value;
while (std::getline(file, line)) {
// Elimina comentarios
auto comment_pos = line.find('#');
if (comment_pos != std::string::npos) {
line.resize(comment_pos);
}
// Usa un stream para separar palabras
std::istringstream iss(line);
if (iss >> param_name >> param_value) {
if (!setParams(param_name, param_value)) {
std::cout << "WARNING: Parámetro desconocido: " << param_name << '\n';
}
}
}
file.close();
// Recalcula las zonas después de cargar todos los parámetros
param.precalculateZones();
}
// Implementación local de setParams
namespace {
auto setParams(const std::string& var, const std::string& value) -> bool {
// Mapas estáticos para diferentes tipos de parámetros
static const std::unordered_map<std::string, std::function<void(const std::string&)>> INT_PARAMS = {
{"game.width", [](const std::string& v) -> void { param.game.width = std::stoi(v); }},
{"game.height", [](const std::string& v) -> void { param.game.height = std::stoi(v); }},
{"game.play_area.rect.x", [](const std::string& v) -> void { param.game.play_area.rect.x = std::stoi(v); }},
{"game.play_area.rect.y", [](const std::string& v) -> void { param.game.play_area.rect.y = std::stoi(v); }},
{"game.play_area.rect.w", [](const std::string& v) -> void { param.game.play_area.rect.w = std::stoi(v); }},
{"game.play_area.rect.h", [](const std::string& v) -> void { param.game.play_area.rect.h = std::stoi(v); }},
{"game.name_entry_idle_time", [](const std::string& v) -> void { param.game.name_entry_idle_time = std::stoi(v); }},
{"game.name_entry_total_time", [](const std::string& v) -> void { param.game.name_entry_total_time = std::stoi(v); }},
{"fade.num_squares_width", [](const std::string& v) -> void { param.fade.num_squares_width = std::stoi(v); }},
{"fade.num_squares_height", [](const std::string& v) -> void { param.fade.num_squares_height = std::stoi(v); }},
{"fade.random_squares_duration_ms", [](const std::string& v) -> void { param.fade.random_squares_duration_ms = std::stoi(v); }},
{"fade.post_duration_ms", [](const std::string& v) -> void { param.fade.post_duration_ms = std::stoi(v); }},
{"fade.venetian_size", [](const std::string& v) -> void { param.fade.venetian_size = std::stoi(v); }},
{"scoreboard.rect.x", [](const std::string& v) -> void { param.scoreboard.rect.x = std::stoi(v); }},
{"scoreboard.rect.y", [](const std::string& v) -> void { param.scoreboard.rect.y = std::stoi(v); }},
{"scoreboard.rect.w", [](const std::string& v) -> void { param.scoreboard.rect.w = std::stoi(v); }},
{"scoreboard.rect.h", [](const std::string& v) -> void { param.scoreboard.rect.h = std::stoi(v); }},
{"scoreboard.skip_countdown_value", [](const std::string& v) -> void { param.scoreboard.skip_countdown_value = std::stoi(v); }},
{"title.press_start_position", [](const std::string& v) -> void { param.title.press_start_position = std::stoi(v); }},
{"title.arcade_edition_position", [](const std::string& v) -> void { param.title.arcade_edition_position = std::stoi(v); }},
{"title.title_c_c_position", [](const std::string& v) -> void { param.title.title_c_c_position = std::stoi(v); }},
{"intro.text_distance_from_bottom", [](const std::string& v) -> void { param.intro.text_distance_from_bottom = std::stoi(v); }}};
static const std::unordered_map<std::string, std::function<void(const std::string&)>> COLOR_PARAMS = {
{"fade.color", [](const std::string& v) -> void { param.fade.color = Color::fromHex(v); }},
{"scoreboard.separator_color", [](const std::string& v) -> void { param.scoreboard.separator_color = Color::fromHex(v); }},
{"scoreboard.easy_color", [](const std::string& v) -> void { param.scoreboard.easy_color = Color::fromHex(v); }},
{"scoreboard.normal_color", [](const std::string& v) -> void { param.scoreboard.normal_color = Color::fromHex(v); }},
{"scoreboard.hard_color", [](const std::string& v) -> void { param.scoreboard.hard_color = Color::fromHex(v); }},
{"scoreboard.text_color1", [](const std::string& v) -> void { param.scoreboard.text_color1 = Color::fromHex(v); }},
{"scoreboard.text_color2", [](const std::string& v) -> void { param.scoreboard.text_color2 = Color::fromHex(v); }},
{"title.bg_color", [](const std::string& v) -> void { param.title.bg_color = Color::fromHex(v); }},
{"background.attenuate_color", [](const std::string& v) -> void { param.background.attenuate_color = Color::fromHex(v); }},
{"notification.color", [](const std::string& v) -> void { param.notification.color = Color::fromHex(v); }},
{"service_menu.title_color", [](const std::string& v) -> void { param.service_menu.title_color = Color::fromHex(v); }},
{"service_menu.text_color", [](const std::string& v) -> void { param.service_menu.text_color = Color::fromHex(v); }},
{"service_menu.selected_color", [](const std::string& v) -> void { param.service_menu.selected_color = Color::fromHex(v); }},
{"service_menu.bg_color", [](const std::string& v) -> void { param.service_menu.bg_color = Color::fromHex(v); }},
{"service_menu.window_message.bg_color", [](const std::string& v) -> void { param.service_menu.window_message.bg_color = Color::fromHex(v); }},
{"service_menu.window_message.border_color", [](const std::string& v) -> void { param.service_menu.window_message.border_color = Color::fromHex(v); }},
{"service_menu.window_message.title_color", [](const std::string& v) -> void { param.service_menu.window_message.title_color = Color::fromHex(v); }},
{"service_menu.window_message.text_color", [](const std::string& v) -> void { param.service_menu.window_message.text_color = Color::fromHex(v); }},
{"intro.bg_color", [](const std::string& v) -> void { param.intro.bg_color = Color::fromHex(v); }},
{"intro.card_color", [](const std::string& v) -> void { param.intro.card_color = Color::fromHex(v); }},
{"intro.shadow_color", [](const std::string& v) -> void { param.intro.shadow_color = Color::fromHex(v); }},
{"debug.color", [](const std::string& v) -> void { param.debug.color = Color::fromHex(v); }},
{"resource.color", [](const std::string& v) -> void { param.resource.color = Color::fromHex(v); }},
{"game.item_text_outline_color", [](const std::string& v) -> void { param.game.item_text_outline_color = Color::fromHex(v); }},
{"player.default_shirt[0].darkest", [](const std::string& v) -> void { param.player.default_shirt[0].darkest = Color::fromHex(v); }},
{"player.default_shirt[0].dark", [](const std::string& v) -> void { param.player.default_shirt[0].dark = Color::fromHex(v); }},
{"player.default_shirt[0].base", [](const std::string& v) -> void { param.player.default_shirt[0].base = Color::fromHex(v); }},
{"player.default_shirt[0].light", [](const std::string& v) -> void { param.player.default_shirt[0].light = Color::fromHex(v); }},
{"player.default_shirt[1].darkest", [](const std::string& v) -> void { param.player.default_shirt[1].darkest = Color::fromHex(v); }},
{"player.default_shirt[1].dark", [](const std::string& v) -> void { param.player.default_shirt[1].dark = Color::fromHex(v); }},
{"player.default_shirt[1].base", [](const std::string& v) -> void { param.player.default_shirt[1].base = Color::fromHex(v); }},
{"player.default_shirt[1].light", [](const std::string& v) -> void { param.player.default_shirt[1].light = Color::fromHex(v); }},
{"player.one_coffee_shirt[0].darkest", [](const std::string& v) -> void { param.player.one_coffee_shirt[0].darkest = Color::fromHex(v); }},
{"player.one_coffee_shirt[0].dark", [](const std::string& v) -> void { param.player.one_coffee_shirt[0].dark = Color::fromHex(v); }},
{"player.one_coffee_shirt[0].base", [](const std::string& v) -> void { param.player.one_coffee_shirt[0].base = Color::fromHex(v); }},
{"player.one_coffee_shirt[0].light", [](const std::string& v) -> void { param.player.one_coffee_shirt[0].light = Color::fromHex(v); }},
{"player.one_coffee_shirt[1].darkest", [](const std::string& v) -> void { param.player.one_coffee_shirt[1].darkest = Color::fromHex(v); }},
{"player.one_coffee_shirt[1].dark", [](const std::string& v) -> void { param.player.one_coffee_shirt[1].dark = Color::fromHex(v); }},
{"player.one_coffee_shirt[1].base", [](const std::string& v) -> void { param.player.one_coffee_shirt[1].base = Color::fromHex(v); }},
{"player.one_coffee_shirt[1].light", [](const std::string& v) -> void { param.player.one_coffee_shirt[1].light = Color::fromHex(v); }},
{"player.two_coffee_shirt[0].darkest", [](const std::string& v) -> void { param.player.two_coffee_shirt[0].darkest = Color::fromHex(v); }},
{"player.two_coffee_shirt[0].dark", [](const std::string& v) -> void { param.player.two_coffee_shirt[0].dark = Color::fromHex(v); }},
{"player.two_coffee_shirt[0].base", [](const std::string& v) -> void { param.player.two_coffee_shirt[0].base = Color::fromHex(v); }},
{"player.two_coffee_shirt[0].light", [](const std::string& v) -> void { param.player.two_coffee_shirt[0].light = Color::fromHex(v); }},
{"player.two_coffee_shirt[1].darkest", [](const std::string& v) -> void { param.player.two_coffee_shirt[1].darkest = Color::fromHex(v); }},
{"player.two_coffee_shirt[1].dark", [](const std::string& v) -> void { param.player.two_coffee_shirt[1].dark = Color::fromHex(v); }},
{"player.two_coffee_shirt[1].base", [](const std::string& v) -> void { param.player.two_coffee_shirt[1].base = Color::fromHex(v); }},
{"player.two_coffee_shirt[1].light", [](const std::string& v) -> void { param.player.two_coffee_shirt[1].light = Color::fromHex(v); }},
{"player.outline_color[0]", [](const std::string& v) -> void { param.player.outline_color[0] = Color::fromHex(v); }},
{"player.outline_color[1]", [](const std::string& v) -> void { param.player.outline_color[1] = Color::fromHex(v); }}};
static const std::unordered_map<std::string, std::function<void(const std::string&)>> BOOL_PARAMS = {
{"scoreboard.separator_autocolor", [](const std::string& v) -> void { param.scoreboard.separator_autocolor = stringToBool(v); }},
{"scoreboard.text_autocolor", [](const std::string& v) -> void { param.scoreboard.text_autocolor = stringToBool(v); }},
{"balloon.bouncing_sound", [](const std::string& v) -> void { param.balloon.bouncing_sound = stringToBool(v); }},
{"notification.sound", [](const std::string& v) -> void { param.notification.sound = stringToBool(v); }},
{"service_menu.drop_shadow", [](const std::string& v) -> void { param.service_menu.drop_shadow = stringToBool(v); }}};
static const std::unordered_map<std::string, std::function<void(const std::string&)>> FLOAT_PARAMS = {
{"balloon.settings[0].vel", [](const std::string& v) -> void { param.balloon.settings.at(0).vel = std::stof(v); }},
{"balloon.settings[0].grav", [](const std::string& v) -> void { param.balloon.settings.at(0).grav = std::stof(v); }},
{"balloon.settings[1].vel", [](const std::string& v) -> void { param.balloon.settings.at(1).vel = std::stof(v); }},
{"balloon.settings[1].grav", [](const std::string& v) -> void { param.balloon.settings.at(1).grav = std::stof(v); }},
{"balloon.settings[2].vel", [](const std::string& v) -> void { param.balloon.settings.at(2).vel = std::stof(v); }},
{"balloon.settings[2].grav", [](const std::string& v) -> void { param.balloon.settings.at(2).grav = std::stof(v); }},
{"balloon.settings[3].vel", [](const std::string& v) -> void { param.balloon.settings.at(3).vel = std::stof(v); }},
{"balloon.settings[3].grav", [](const std::string& v) -> void { param.balloon.settings.at(3).grav = std::stof(v); }},
{"tabe.min_spawn_time", [](const std::string& v) -> void { param.tabe.min_spawn_time = std::stof(v); }},
{"tabe.max_spawn_time", [](const std::string& v) -> void { param.tabe.max_spawn_time = std::stof(v); }},
{"title.title_duration", [](const std::string& v) -> void { param.title.title_duration = std::stof(v); }},
{"service_menu.window_message.padding", [](const std::string& v) -> void { param.service_menu.window_message.padding = std::stof(v); }},
{"service_menu.window_message.line_spacing", [](const std::string& v) -> void { param.service_menu.window_message.line_spacing = std::stof(v); }},
{"service_menu.window_message.title_separator_spacing", [](const std::string& v) -> void { param.service_menu.window_message.title_separator_spacing = std::stof(v); }},
{"service_menu.window_message.min_width", [](const std::string& v) -> void { param.service_menu.window_message.min_width = std::stof(v); }},
{"service_menu.window_message.min_height", [](const std::string& v) -> void { param.service_menu.window_message.min_height = std::stof(v); }},
{"service_menu.window_message.max_width_ratio", [](const std::string& v) -> void { param.service_menu.window_message.max_width_ratio = std::stof(v); }},
{"service_menu.window_message.max_height_ratio", [](const std::string& v) -> void { param.service_menu.window_message.max_height_ratio = std::stof(v); }},
{"service_menu.window_message.text_safety_margin", [](const std::string& v) -> void { param.service_menu.window_message.text_safety_margin = std::stof(v); }},
{"service_menu.window_message.animation_duration", [](const std::string& v) -> void { param.service_menu.window_message.animation_duration = std::stof(v); }}};
static const std::unordered_map<std::string, std::function<void(const std::string&)>> INT_PARAMS_EXTRA = {};
// Colores válidos para globos
static const std::unordered_map<std::string, bool> VALID_BALLOON_COLORS = {
{"blue", true},
{"orange", true},
{"red", true},
{"green", true}};
auto validate_balloon_color = [](const std::string& color) -> bool {
return VALID_BALLOON_COLORS.contains(color);
};
static const std::unordered_map<std::string, std::function<void(const std::string&)>> STRING_PARAMS = {
{"balloon.color[0]", [validate_balloon_color](const std::string& v) -> void {
if (!validate_balloon_color(v)) {
std::cout << "Color de globo inválido '" << v << "'. Usando 'blue' por defecto." << '\n';
param.balloon.color.at(0) = "blue";
} else {
param.balloon.color.at(0) = v;
}
}},
{"balloon.color[1]", [validate_balloon_color](const std::string& v) -> void {
if (!validate_balloon_color(v)) {
std::cout << "Color de globo inválido '" << v << "'. Usando 'orange' por defecto." << '\n';
param.balloon.color.at(1) = "orange";
} else {
param.balloon.color.at(1) = v;
}
}},
{"balloon.color[2]", [validate_balloon_color](const std::string& v) -> void {
if (!validate_balloon_color(v)) {
std::cout << "Color de globo inválido '" << v << "'. Usando 'red' por defecto." << '\n';
param.balloon.color.at(2) = "red";
} else {
param.balloon.color.at(2) = v;
}
}},
{"balloon.color[3]", [validate_balloon_color](const std::string& v) -> void {
if (!validate_balloon_color(v)) {
std::cout << "Color de globo inválido '" << v << "'. Usando 'green' por defecto." << '\n';
param.balloon.color.at(3) = "green";
} else {
param.balloon.color.at(3) = v;
}
}}};
// Lambda para intentar cada mapa de parámetros
auto try_map = [&](const auto& param_map) -> bool {
auto it = param_map.find(var);
if (it != param_map.end()) {
it->second(value);
return true;
}
return false;
};
// Intentar con todos los mapas
if (try_map(INT_PARAMS) || try_map(COLOR_PARAMS) || try_map(BOOL_PARAMS) ||
try_map(FLOAT_PARAMS) || try_map(STRING_PARAMS)) {
return true;
}
// Casos especiales que necesitan lógica personalizada
if (var == "notification.pos_h") {
if (value == "LEFT") {
param.notification.pos_h = Notifier::Position::LEFT;
} else if (value == "MIDDLE") {
param.notification.pos_h = Notifier::Position::MIDDLE;
} else {
param.notification.pos_h = Notifier::Position::RIGHT;
}
return true;
}
if (var == "notification.pos_v") {
param.notification.pos_v = value == "TOP" ? Notifier::Position::TOP : Notifier::Position::BOTTOM;
return true;
}
return false; // Parámetro no encontrado
}
} // namespace

254
source/utils/param.hpp Normal file
View File

@@ -0,0 +1,254 @@
#pragma once
#include <SDL3/SDL.h> // Para Uint32, SDL_FRect
#include <array> // Para array
#include <string> // Para basic_string, string
#include "color.hpp" // Para Color, Zone
#include "defaults.hpp" // Para los valores por defecto
#include "ui/notifier.hpp" // Para Notifier::Position
#include "utils.hpp"
// --- Parámetros del juego ---
struct ParamGame {
float width = Defaults::Game::WIDTH;
float height = Defaults::Game::HEIGHT;
Zone play_area{}; // Se inicializa en el constructor de Param
Zone game_area{}; // Se inicializa en el constructor de Param
int name_entry_idle_time = Defaults::Game::NAME_ENTRY_IDLE_TIME;
int name_entry_total_time = Defaults::Game::NAME_ENTRY_TOTAL_TIME;
Color item_text_outline_color;
};
// --- Parámetros del fade ---
struct ParamFade {
Color color = Color::fromHex(Defaults::Fade::COLOR);
float num_squares_width = Defaults::Fade::NUM_SQUARES_WIDTH;
float num_squares_height = Defaults::Fade::NUM_SQUARES_HEIGHT;
int random_squares_duration_ms = Defaults::Fade::RANDOM_SQUARES_DURATION_MS;
int post_duration_ms = Defaults::Fade::POST_DURATION_MS;
float venetian_size = Defaults::Fade::VENETIAN_SIZE;
};
// --- Parámetros de la pantalla de título ---
struct ParamTitle {
int press_start_position = Defaults::Title::PRESS_START_POSITION;
float title_duration = Defaults::Title::DURATION_S;
int arcade_edition_position = Defaults::Title::ARCADE_EDITION_POSITION;
int title_c_c_position = Defaults::Title::TITLE_C_C_POSITION;
Color bg_color = Color::fromHex(Defaults::Title::BG_COLOR);
};
// --- Parámetros del fondo ---
struct ParamBackground {
Color attenuate_color = Color::fromHex(Defaults::Background::ATTENUATE_COLOR);
};
// --- Parámetros de los globos ---
struct ParamBalloon {
struct Settings {
float grav;
float vel;
// Constructor por defecto
constexpr Settings(float grav_val = 0.0F, float vel_val = 0.0F)
: grav(grav_val),
vel(vel_val) {}
};
std::array<Settings, 4> settings = {{Settings(Defaults::Balloon::SETTINGS[0].grav, Defaults::Balloon::SETTINGS[0].vel),
Settings(Defaults::Balloon::SETTINGS[1].grav, Defaults::Balloon::SETTINGS[1].vel),
Settings(Defaults::Balloon::SETTINGS[2].grav, Defaults::Balloon::SETTINGS[2].vel),
Settings(Defaults::Balloon::SETTINGS[3].grav, Defaults::Balloon::SETTINGS[3].vel)}};
std::array<std::string, 4> color = {{Defaults::Balloon::COLORS[0],
Defaults::Balloon::COLORS[1],
Defaults::Balloon::COLORS[2],
Defaults::Balloon::COLORS[3]}};
bool bouncing_sound = Defaults::Balloon::BOUNCING_SOUND;
};
// --- Parámetros de las notificaciones ---
struct ParamNotification {
Notifier::Position pos_h = Defaults::Notification::POS_H;
Notifier::Position pos_v = Defaults::Notification::POS_V;
bool sound = Defaults::Notification::SOUND;
Color color = Color::fromHex(Defaults::Notification::COLOR);
};
// --- Parámetros del marcador ---
struct ParamScoreboard {
SDL_FRect rect = {
.x = Defaults::Scoreboard::RECT_X,
.y = Defaults::Scoreboard::RECT_Y,
.w = Defaults::Scoreboard::RECT_W,
.h = Defaults::Scoreboard::RECT_H};
bool separator_autocolor = Defaults::Scoreboard::SEPARATOR_AUTOCOLOR;
Color separator_color = Color::fromHex(Defaults::Scoreboard::SEPARATOR_COLOR);
Color easy_color = Color::fromHex(Defaults::Scoreboard::EASY_COLOR);
Color normal_color = Color::fromHex(Defaults::Scoreboard::NORMAL_COLOR);
Color hard_color = Color::fromHex(Defaults::Scoreboard::HARD_COLOR);
bool text_autocolor = Defaults::Scoreboard::TEXT_AUTOCOLOR;
Color text_color1 = Color::fromHex(Defaults::Scoreboard::TEXT_COLOR1);
Color text_color2 = Color::fromHex(Defaults::Scoreboard::TEXT_COLOR2);
int skip_countdown_value = Defaults::Scoreboard::SKIP_COUNTDOWN_VALUE;
};
// --- Parámetros del menú de servicio ---
struct ParamServiceMenu {
Color title_color = Color::fromHex(Defaults::ServiceMenu::TITLE_COLOR);
Color text_color = Color::fromHex(Defaults::ServiceMenu::TEXT_COLOR);
Color selected_color = Color::fromHex(Defaults::ServiceMenu::SELECTED_COLOR);
Color bg_color = Color::fromHex(Defaults::ServiceMenu::BG_COLOR);
bool drop_shadow = Defaults::ServiceMenu::DROP_SHADOW;
// Configuración para ventanas de mensaje
struct WindowMessage {
Color bg_color = Color::fromHex(Defaults::ServiceMenu::WindowMessage::BG_COLOR);
Color border_color = Color::fromHex(Defaults::ServiceMenu::WindowMessage::BORDER_COLOR);
Color title_color = Color::fromHex(Defaults::ServiceMenu::WindowMessage::TITLE_COLOR);
Color text_color = Color::fromHex(Defaults::ServiceMenu::WindowMessage::TEXT_COLOR);
float padding = Defaults::ServiceMenu::WindowMessage::PADDING;
float line_spacing = Defaults::ServiceMenu::WindowMessage::LINE_SPACING;
float title_separator_spacing = Defaults::ServiceMenu::WindowMessage::TITLE_SEPARATOR_SPACING;
float min_width = Defaults::ServiceMenu::WindowMessage::MIN_WIDTH;
float min_height = Defaults::ServiceMenu::WindowMessage::MIN_HEIGHT;
float max_width_ratio = Defaults::ServiceMenu::WindowMessage::MAX_WIDTH_RATIO;
float max_height_ratio = Defaults::ServiceMenu::WindowMessage::MAX_HEIGHT_RATIO;
float text_safety_margin = Defaults::ServiceMenu::WindowMessage::TEXT_SAFETY_MARGIN;
float animation_duration = Defaults::ServiceMenu::WindowMessage::ANIMATION_DURATION;
} window_message;
};
// --- Parámetros de la intro ---
struct ParamIntro {
Color bg_color = Color::fromHex(Defaults::Intro::BG_COLOR);
Color card_color = Color::fromHex(Defaults::Intro::CARD_COLOR);
Color shadow_color = Color::fromHex(Defaults::Intro::SHADOW_COLOR);
int text_distance_from_bottom = Defaults::Intro::TEXT_DISTANCE_FROM_BOTTOM;
};
// --- Parámetros para Debug ---
struct ParamDebug {
Color color = Color::fromHex(Defaults::Debug::COLOR);
};
// --- Parámetros para Resource ---
struct ParamResource {
Color color = Color::fromHex(Defaults::Resource::COLOR);
};
// --- Parámetros para Tabe ---
struct ParamTabe {
float min_spawn_time = Defaults::Tabe::MIN_SPAWN_TIME;
float max_spawn_time = Defaults::Tabe::MAX_SPAWN_TIME;
};
// --- Parámetros del Jugador ---
struct ParamPlayer {
// Configuración de camisetas del jugador
struct Shirt {
Color darkest; // Tono más oscuro - bordes y contornos
Color dark; // Tono oscuro - sombras
Color base; // Tono principal - color base de la camiseta
Color light; // Tono claro - zonas iluminadas/highlights
// Constructor por defecto
Shirt() = default;
// Constructor con tonalidades específicas
Shirt(const Color& darkest_tone, const Color& dark_tone, const Color& base_tone, const Color& light_tone)
: darkest(darkest_tone),
dark(dark_tone),
base(base_tone),
light(light_tone) {}
};
// Inicialización con valores por defecto
const Shirt default_player0_shirt = Shirt(
Color::fromHex(Defaults::Player::DefaultShirt::PLAYER0_DARKEST),
Color::fromHex(Defaults::Player::DefaultShirt::PLAYER0_DARK),
Color::fromHex(Defaults::Player::DefaultShirt::PLAYER0_BASE),
Color::fromHex(Defaults::Player::DefaultShirt::PLAYER0_LIGHT));
const Shirt default_player1_shirt = Shirt(
Color::fromHex(Defaults::Player::DefaultShirt::PLAYER1_DARKEST),
Color::fromHex(Defaults::Player::DefaultShirt::PLAYER1_DARK),
Color::fromHex(Defaults::Player::DefaultShirt::PLAYER1_BASE),
Color::fromHex(Defaults::Player::DefaultShirt::PLAYER1_LIGHT));
std::array<Shirt, 2> default_shirt = {default_player0_shirt, default_player1_shirt};
const Shirt one_coffee_player0_shirt = Shirt(
Color::fromHex(Defaults::Player::OneCoffeeShirt::PLAYER0_DARKEST),
Color::fromHex(Defaults::Player::OneCoffeeShirt::PLAYER0_DARK),
Color::fromHex(Defaults::Player::OneCoffeeShirt::PLAYER0_BASE),
Color::fromHex(Defaults::Player::OneCoffeeShirt::PLAYER0_LIGHT));
const Shirt one_coffee_player1_shirt = Shirt(
Color::fromHex(Defaults::Player::OneCoffeeShirt::PLAYER1_DARKEST),
Color::fromHex(Defaults::Player::OneCoffeeShirt::PLAYER1_DARK),
Color::fromHex(Defaults::Player::OneCoffeeShirt::PLAYER1_BASE),
Color::fromHex(Defaults::Player::OneCoffeeShirt::PLAYER1_LIGHT));
std::array<Shirt, 2> one_coffee_shirt = {one_coffee_player0_shirt, one_coffee_player1_shirt};
const Shirt two_coffee_player0_shirt = Shirt(
Color::fromHex(Defaults::Player::TwoCoffeeShirt::PLAYER0_DARKEST),
Color::fromHex(Defaults::Player::TwoCoffeeShirt::PLAYER0_DARK),
Color::fromHex(Defaults::Player::TwoCoffeeShirt::PLAYER0_BASE),
Color::fromHex(Defaults::Player::TwoCoffeeShirt::PLAYER0_LIGHT));
const Shirt two_coffee_player1_shirt = Shirt(
Color::fromHex(Defaults::Player::TwoCoffeeShirt::PLAYER1_DARKEST),
Color::fromHex(Defaults::Player::TwoCoffeeShirt::PLAYER1_DARK),
Color::fromHex(Defaults::Player::TwoCoffeeShirt::PLAYER1_BASE),
Color::fromHex(Defaults::Player::TwoCoffeeShirt::PLAYER1_LIGHT));
std::array<Shirt, 2> two_coffee_shirt = {two_coffee_player0_shirt, two_coffee_player1_shirt};
const Color outline_player0_color = Color::fromHex(Defaults::Player::OutlineColor::PLAYER0);
const Color outline_player1_color = Color::fromHex(Defaults::Player::OutlineColor::PLAYER1);
std::array<Color, 2> outline_color = {outline_player0_color, outline_player1_color};
};
// --- Estructura Param: almacena todos los parámetros del juego ---
struct Param {
ParamGame game;
ParamFade fade;
ParamScoreboard scoreboard;
ParamTitle title;
ParamBackground background;
ParamBalloon balloon;
ParamNotification notification;
ParamServiceMenu service_menu;
ParamIntro intro;
ParamDebug debug;
ParamResource resource;
ParamTabe tabe;
ParamPlayer player;
// Constructor que inicializa las zonas que dependen de otros valores
Param() {
// Inicializar play_area usando los valores por defecto
game.play_area.rect = {
.x = Defaults::Game::PLAY_AREA_X,
.y = Defaults::Game::PLAY_AREA_Y,
.w = Defaults::Game::PLAY_AREA_W,
.h = Defaults::Game::PLAY_AREA_H};
// Las zonas calculadas se inicializarán en precalculateZones()
precalculateZones();
}
// Función pública para recalcular zonas (necesaria después de cambiar parámetros)
void precalculateZones();
};
// --- Variables ---
extern Param param; // Variable global con los parámetros del juego
// --- Funciones ---
void loadParamsFromFile(const std::string& file_path); // Carga parámetros desde archivo

326
source/utils/utils.cpp Normal file
View File

@@ -0,0 +1,326 @@
// NOLINTNEXTLINE(bugprone-reserved-identifier) -- requerido por <cmath> para exponer M_PI en MSVC
#define _USE_MATH_DEFINES
#include "utils.hpp"
#include <SDL3/SDL.h> // Para SDL_RenderPoint, SDL_FRect, SDL_FPoint, SDL_Renderer
#include <algorithm> // Para clamp, __transform_fn, transform
#include <cctype> // Para tolower, isspace
#include <cmath> // Para pow, sin, M_PI, cos, sqrt
#include <compare> // Para operator<
#include <filesystem> // Para path
#include <ranges> // Para __find_if_not_fn, find_if_not, reverse_view, __find_fn, find, ref_view
#include <string> // Para basic_string, string, allocator, char_traits, operator==, operator+
#include "lang.hpp" // Para getText
// Calcula el cuadrado de la distancia entre dos puntos
auto distanceSquared(int x1, int y1, int x2, int y2) -> double {
const int DELTA_X = x2 - x1;
const int DELTA_Y = y2 - y1;
return (DELTA_X * DELTA_X) + (DELTA_Y * DELTA_Y);
}
// Obtiene el punto de colisión entre dos circulos
auto getCollisionPoint(const Circle& a, const Circle& b) -> SDL_FPoint {
float dx = b.x - a.x;
float dy = b.y - a.y;
float dist = std::sqrt((dx * dx) + (dy * dy));
// Normaliza el vector
float nx = dx / dist;
float ny = dy / dist;
// Punto en el borde del círculo A hacia B
SDL_FPoint contact;
contact.x = a.x + (nx * a.r);
contact.y = a.y + (ny * a.r);
return contact;
}
// Detector de colisiones entre dos circulos
auto checkCollision(const Circle& a, const Circle& b) -> bool {
// Calcula el radio total al cuadrado
int total_radius_squared = (a.r + b.r) * (a.r + b.r);
// Comprueba si la distancia entre los centros de los círculos es inferior a la suma de sus radios
return distanceSquared(a.x, a.y, b.x, b.y) < total_radius_squared;
}
// Detector de colisiones entre un circulo y un rectangulo
auto checkCollision(const Circle& a, const SDL_FRect& b) -> bool {
// Encuentra el punto más cercano en el rectángulo
float c_x = std::clamp(static_cast<float>(a.x), b.x, b.x + b.w);
float c_y = std::clamp(static_cast<float>(a.y), b.y, b.y + b.h);
// Si el punto más cercano está dentro del círculo
return distanceSquared(static_cast<float>(a.x), static_cast<float>(a.y), c_x, c_y) < static_cast<float>(a.r) * a.r;
}
// Detector de colisiones entre dos rectangulos
auto checkCollision(const SDL_FRect& a, const SDL_FRect& b) -> bool {
const int LEFT_A = a.x;
const int RIGHT_A = a.x + a.w;
const int TOP_A = a.y;
const int BOTTOM_A = a.y + a.h;
const int LEFT_B = b.x;
const int RIGHT_B = b.x + b.w;
const int TOP_B = b.y;
const int BOTTOM_B = b.y + b.h;
if (BOTTOM_A <= TOP_B) {
return false;
}
if (TOP_A >= BOTTOM_B) {
return false;
}
if (RIGHT_A <= LEFT_B) {
return false;
}
if (LEFT_A >= RIGHT_B) {
return false;
}
return true;
}
// Detector de colisiones entre un punto y un rectangulo
auto checkCollision(const SDL_FPoint& p, const SDL_FRect& r) -> bool {
if (p.x < r.x || p.x > r.x + r.w) {
return false;
}
if (p.y < r.y || p.y > r.y + r.h) {
return false;
}
return true;
}
// Convierte una cadena en un valor booleano
auto stringToBool(const std::string& str) -> bool {
std::string s = trim(toLower(str));
return (s == "true" || s == "1" || s == "yes" || s == "on");
}
// Convierte un valor booleano en una cadena
auto boolToString(bool value) -> std::string {
return value ? "true" : "false";
}
// Convierte un valor booleano en una cadena "on" o "off"
auto boolToOnOff(bool value) -> std::string {
return value ? Lang::getText("[NOTIFICATIONS] 06") : Lang::getText("[NOTIFICATIONS] 07");
}
// Convierte una cadena a minusculas
auto toLower(const std::string& str) -> std::string {
std::string result = str;
std::ranges::transform(result, result.begin(), [](unsigned char c) -> int { return std::tolower(c); });
return result;
}
// Dibuja un circulo
void drawCircle(SDL_Renderer* renderer, int32_t center_x, int32_t center_y, int32_t radius) {
const int32_t DIAMETER = (radius * 2);
int32_t x = (radius - 1);
int32_t y = 0;
int32_t tx = 1;
int32_t ty = 1;
int32_t error = (tx - DIAMETER);
while (x >= y) {
// Each of the following renders an octant of the circle
SDL_RenderPoint(renderer, center_x + x, center_y - y);
SDL_RenderPoint(renderer, center_x + x, center_y + y);
SDL_RenderPoint(renderer, center_x - x, center_y - y);
SDL_RenderPoint(renderer, center_x - x, center_y + y);
SDL_RenderPoint(renderer, center_x + y, center_y - x);
SDL_RenderPoint(renderer, center_x + y, center_y + x);
SDL_RenderPoint(renderer, center_x - y, center_y - x);
SDL_RenderPoint(renderer, center_x - y, center_y + x);
if (error <= 0) {
++y;
error += ty;
ty += 2;
}
if (error > 0) {
--x;
tx += 2;
error += (tx - DIAMETER);
}
}
}
// Quita los espacioes en un string
auto trim(const std::string& str) -> std::string {
auto start = std::ranges::find_if_not(str, ::isspace);
auto end = std::ranges::find_if_not(std::ranges::reverse_view(str), ::isspace).base();
return (start < end ? std::string(start, end) : std::string());
}
// Función de suavizado
auto easeOutQuint(double time) -> double {
return 1 - std::pow(1 - time, 5);
}
// Función de suavizado
auto easeInQuint(double time) -> double {
return pow(time, 5);
}
// Función de suavizado
auto easeInOutQuint(double time) -> double {
return time < 0.5 ? 16 * pow(time, 5) : 1 - (pow((-2 * time) + 2, 5) / 2);
}
// Función de suavizado
auto easeInQuad(double time) -> double {
return time * time;
}
// Función de suavizado
auto easeOutQuad(double time) -> double {
return 1 - ((1 - time) * (1 - time));
}
// Función de suavizado
auto easeInOutSine(double time) -> double {
return -0.5 * (std::cos(M_PI * time) - 1);
}
// Función de suavizado
auto easeInOut(double time) -> double {
return time < 0.5 ? 2 * time * time : -1 + ((4 - (2 * time)) * time);
}
// Función de suavizado (easeInOutExpo)
auto easeInOutExpo(double time) -> double {
if (time == 0) {
return 0;
}
if (time == 1) {
return 1;
}
if (time < 0.5) {
return pow(2, (20 * time) - 10) / 2;
}
return (2 - pow(2, (-20 * time) + 10)) / 2;
}
// Función de suavizado (easeInElastic)
auto easeInElastic(double time) -> double {
if (time == 0) {
return 0;
}
if (time == 1) {
return 1;
}
const double C4 = (2 * M_PI) / 3;
return -pow(2, (10 * time) - 10) * sin(((time * 10) - 10.75) * C4);
}
// Función de suavizado
auto easeOutBounce(double time) -> double {
if (time < 1 / 2.75) {
return 7.5625 * time * time;
}
if (time < 2 / 2.75) {
time -= 1.5 / 2.75;
return (7.5625 * time * time) + 0.75;
}
if (time < 2.5 / 2.75) {
time -= 2.25 / 2.75;
return (7.5625 * time * time) + 0.9375;
}
time -= 2.625 / 2.75;
return (7.5625 * time * time) + 0.984375;
}
// Función de suavizado (easeOutElastic)
auto easeOutElastic(double time) -> double {
if (time == 0) {
return 0;
}
if (time == 1) {
return 1;
}
const double C4 = (2 * M_PI) / 3; // Constante para controlar la elasticidad
return (pow(2, -10 * time) * sin(((time * 10) - 0.75) * C4)) + 1;
}
// Ease Out Expo - Muy suave al final (más dramático)
auto easeOutExpo(double time) -> double {
return time == 1.0F ? 1.0F : 1.0F - pow(2.0F, -10.0F * time);
}
// Ease In Expo - Arranque muy gradual
auto easeInExpo(double time) -> double {
return time == 0.0F ? 0.0F : pow(2.0F, 10.0F * (time - 1.0F));
}
// Ease Out Back - Con un pequeño "rebote"
auto easeOutBack(double time) -> double {
const double C1 = 1.70158F;
const double C3 = C1 + 1.0F;
return 1.0F + (C3 * pow(time - 1.0F, 3.0F)) + (C1 * pow(time - 1.0F, 2.0F));
}
// Ease Out Cubic - Desaceleración suave al final
auto easeOutCubic(double time) -> double {
return 1.0F - pow(1.0F - time, 3.0F);
}
// Ease In Cubic - Aceleración gradual
auto easeInCubic(double time) -> double {
return time * time * time;
}
// Comprueba si una vector contiene una cadena
auto stringInVector(const std::vector<std::string>& vec, const std::string& str) -> bool {
return std::ranges::find(vec, str) != vec.end();
}
// Obtiene el nombre de un fichero a partir de una ruta completa
auto getFileName(const std::string& path) -> std::string {
return std::filesystem::path(path).filename().string();
}
// Obtiene la ruta eliminando el nombre del fichero
auto getPath(const std::string& full_path) -> std::string {
std::filesystem::path path(full_path);
return path.parent_path().string();
}
// Trunca un string y le añade puntos suspensivos
auto truncateWithEllipsis(const std::string& input, size_t length) -> std::string {
if (input.size() <= length) {
return input;
}
if (length <= 3) {
std::string result(length, '.');
return result;
}
return input.substr(0, length) + "...";
}
// Añade espacios entre las letras de un string
auto spaceBetweenLetters(const std::string& input) -> std::string {
std::string result;
for (size_t i = 0; i < input.size(); ++i) {
result += input[i];
if (i != input.size() - 1) {
result += ' ';
}
}
return result;
}

82
source/utils/utils.hpp Normal file
View File

@@ -0,0 +1,82 @@
// IWYU pragma: no_include <bits/std_abs.h>
#pragma once
#include <SDL3/SDL.h> // Para Uint8, SDL_FRect, SDL_FPoint, SDL_Renderer
#include <cstddef> // Para size_t
#include <cstdint> // Para int32_t
#include <string> // Para string
#include <vector> // Para vector
// --- Constantes ---
constexpr int BLOCK = 8;
// --- Estructuras ---
struct Circle {
int x, y, r; // Coordenadas y radio
Circle()
: x(0),
y(0),
r(0) {}
Circle(int x_coord, int y_coord, int radius)
: x(x_coord),
y(y_coord),
r(radius) {}
};
struct Zone {
SDL_FRect rect; // Rectangulo que define la zona
float center_x; // Anclaje al 50% del eje X
float first_quarter_x; // Anclaje al 25% del eje X
float third_quarter_x; // Anclaje al 75% del eje X
float center_y; // Anclaje al 50% del eje Y
float first_quarter_y; // Anclaje al 25% del eje Y
float third_quarter_y; // Anclaje al 75% del eje Y
};
// --- Funciones ---
// Colisiones y geometría
auto distanceSquared(int x1, int y1, int x2, int y2) -> double;
auto getCollisionPoint(const Circle& a, const Circle& b) -> SDL_FPoint;
auto checkCollision(const Circle& a, const Circle& b) -> bool;
auto checkCollision(const Circle& a, const SDL_FRect& b) -> bool;
auto checkCollision(const SDL_FRect& a, const SDL_FRect& b) -> bool;
auto checkCollision(const SDL_FPoint& p, const SDL_FRect& r) -> bool;
// Conversión y manipulación de cadenas
auto stringToBool(const std::string& str) -> bool;
auto boolToString(bool value) -> std::string;
auto boolToOnOff(bool value) -> std::string;
auto toLower(const std::string& str) -> std::string;
auto trim(const std::string& str) -> std::string;
auto spaceBetweenLetters(const std::string& input) -> std::string;
// Dibujo
void drawCircle(SDL_Renderer* renderer, int32_t center_x, int32_t center_y, int32_t radius);
// Funciones de suavizado (easing)
auto easeOutQuint(double time) -> double;
auto easeInQuint(double time) -> double;
auto easeInOutQuint(double time) -> double;
auto easeInQuad(double time) -> double;
auto easeOutQuad(double time) -> double;
auto easeInOutSine(double time) -> double;
auto easeInOut(double time) -> double;
auto easeInOutExpo(double time) -> double;
auto easeOutBounce(double time) -> double;
auto easeOutElastic(double time) -> double;
auto easeInElastic(double time) -> double;
auto easeOutExpo(double time) -> double;
auto easeInExpo(double time) -> double;
auto easeOutBack(double time) -> double;
auto easeOutCubic(double time) -> double;
auto easeInCubic(double time) -> double;
// Utilidades varias
auto stringInVector(const std::vector<std::string>& vec, const std::string& str) -> bool; // Comprueba si un vector contiene una cadena
auto truncateWithEllipsis(const std::string& input, size_t length) -> std::string; // Trunca un string y le añade puntos suspensivos
// Ficheros y rutas
auto getFileName(const std::string& path) -> std::string; // Obtiene el nombre de un fichero a partir de una ruta
auto getPath(const std::string& full_path) -> std::string; // Obtiene la ruta eliminando el nombre del fichero