reestructuració
This commit is contained in:
186
source/utils/color.cpp
Normal file
186
source/utils/color.cpp
Normal 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
161
source/utils/color.hpp
Normal 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
274
source/utils/param.cpp
Normal 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
254
source/utils/param.hpp
Normal 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
326
source/utils/utils.cpp
Normal 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
82
source/utils/utils.hpp
Normal 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
|
||||
Reference in New Issue
Block a user