primer commit

This commit is contained in:
2025-11-23 11:44:31 +01:00
commit 6ada29eaf8
613 changed files with 484459 additions and 0 deletions

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

@@ -0,0 +1,57 @@
/**
* @file color.cpp
* @brief Implementación de la gestión de colores de la paleta Amstrad CPC
*/
#include "color.hpp"
#include <unordered_map>
auto Color::fromString(const std::string& name) -> Uint8 {
// Mapa de nombres de colores a índices de paleta
// Incluye nombres oficiales del CPC y aliases para compatibilidad
static const std::unordered_map<std::string, Uint8> COLOR_MAP = {
// Transparente
{"transparent", index(Cpc::TRANSPARENT)},
// Colores oficiales Amstrad CPC
{"black", index(Cpc::BLACK)},
{"blue", index(Cpc::BLUE)},
{"bright_blue", index(Cpc::BRIGHT_BLUE)},
{"red", index(Cpc::RED)},
{"magenta", index(Cpc::MAGENTA)},
{"mauve", index(Cpc::MAUVE)},
{"bright_red", index(Cpc::BRIGHT_RED)},
{"purple", index(Cpc::PURPLE)},
{"bright_magenta", index(Cpc::BRIGHT_MAGENTA)},
{"green", index(Cpc::GREEN)},
{"cyan", index(Cpc::CYAN)},
{"sky_blue", index(Cpc::SKY_BLUE)},
{"yellow", index(Cpc::YELLOW)},
{"white", index(Cpc::WHITE)},
{"pastel_blue", index(Cpc::PASTEL_BLUE)},
{"orange", index(Cpc::ORANGE)},
{"pink", index(Cpc::PINK)},
{"pastel_magenta", index(Cpc::PASTEL_MAGENTA)},
{"bright_green", index(Cpc::BRIGHT_GREEN)},
{"sea_green", index(Cpc::SEA_GREEN)},
{"bright_cyan", index(Cpc::BRIGHT_CYAN)},
{"lime", index(Cpc::LIME)},
{"pastel_green", index(Cpc::PASTEL_GREEN)},
{"pastel_cyan", index(Cpc::PASTEL_CYAN)},
{"bright_yellow", index(Cpc::BRIGHT_YELLOW)},
{"pastel_yellow", index(Cpc::PASTEL_YELLOW)},
{"bright_white", index(Cpc::BRIGHT_WHITE)},
// Aliases para compatibilidad con archivos YAML existentes (Spectrum)
{"bright_black", index(Cpc::BLACK)}, // No existe en CPC, mapea a negro
};
auto it = COLOR_MAP.find(name);
if (it != COLOR_MAP.end()) {
return it->second;
}
// Si no se encuentra, devuelve negro por defecto
return index(Cpc::BLACK);
}

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

@@ -0,0 +1,94 @@
/**
* @file color.hpp
* @brief Gestión de colores de la paleta Amstrad CPC
*/
#ifndef COLOR_HPP
#define COLOR_HPP
#include <SDL2/SDL_stdinc.h>
#include <string>
/**
* @class Color
* @brief Clase para gestionar los colores de la paleta Amstrad CPC
*
* La paleta del Amstrad CPC tiene 27 colores únicos organizados en un sistema
* de 3 niveles de intensidad (0, 128, 255) para cada componente RGB.
*/
class Color {
public:
/**
* @enum Cpc
* @brief Índices de los colores de la paleta Amstrad CPC
*
* Los nombres corresponden a los colores oficiales documentados por Amstrad.
* El índice 0 está reservado para transparencia.
*/
enum class Cpc : Uint8 {
// Transparente (índice 0)
TRANSPARENT = 0,
// Negros y azules (R=0)
BLACK = 1, // 0, 0, 0
BLUE = 2, // 0, 0, 128
BRIGHT_BLUE = 3, // 0, 0, 255
// Rojos y magentas (G=0)
RED = 4, // 128, 0, 0
MAGENTA = 5, // 128, 0, 128
MAUVE = 6, // 128, 0, 255
BRIGHT_RED = 7, // 255, 0, 0
PURPLE = 8, // 255, 0, 128
BRIGHT_MAGENTA = 9, // 255, 0, 255
// Verdes y cianes (R=0, G>0)
GREEN = 10, // 0, 128, 0
CYAN = 11, // 0, 128, 128
SKY_BLUE = 12, // 0, 128, 255
// Amarillos y blancos medios (G=128)
YELLOW = 13, // 128, 128, 0
WHITE = 14, // 128, 128, 128
PASTEL_BLUE = 15, // 128, 128, 255
// Naranjas y rosas (R=255, G=128)
ORANGE = 16, // 255, 128, 0
PINK = 17, // 255, 128, 128
PASTEL_MAGENTA = 18, // 255, 128, 255
// Verdes brillantes (G=255)
BRIGHT_GREEN = 19, // 0, 255, 0
SEA_GREEN = 20, // 0, 255, 128
BRIGHT_CYAN = 21, // 0, 255, 255
// Limas y pasteles verdes (G=255)
LIME = 22, // 128, 255, 0
PASTEL_GREEN = 23, // 128, 255, 128
PASTEL_CYAN = 24, // 128, 255, 255
// Amarillos brillantes y blancos (R=255, G=255)
BRIGHT_YELLOW = 25, // 255, 255, 0
PASTEL_YELLOW = 26, // 255, 255, 128
BRIGHT_WHITE = 27 // 255, 255, 255
};
/**
* @brief Obtiene el índice de paleta de un color CPC
* @param color Color del enum Cpc
* @return Índice de paleta (Uint8)
*/
static constexpr auto index(Cpc color) -> Uint8 {
return static_cast<Uint8>(color);
}
/**
* @brief Convierte un nombre de color (string) a índice de paleta
* @param name Nombre del color en minúsculas (ej: "cyan", "bright_blue")
* @return Índice de paleta, o 1 (BLACK) si no se encuentra
*/
static auto fromString(const std::string& name) -> Uint8;
};
#endif // COLOR_HPP

72
source/utils/defines.hpp Normal file
View File

@@ -0,0 +1,72 @@
#pragma once
#include <SDL3/SDL.h>
// Tamaño de bloque
namespace Tile {
constexpr int SIZE = 8;
constexpr int HALF_SIZE = SIZE / 2;
} // namespace Tile
namespace GameCanvas {
constexpr int WIDTH = 320;
constexpr int HEIGHT = 240;
constexpr int CENTER_X = WIDTH / 2;
constexpr int FIRST_QUARTER_X = WIDTH / 4;
constexpr int THIRD_QUARTER_X = (WIDTH / 4) * 3;
constexpr int CENTER_Y = HEIGHT / 2;
constexpr int FIRST_QUARTER_Y = HEIGHT / 4;
constexpr int THIRD_QUARTER_Y = (HEIGHT / 4) * 3;
} // namespace GameCanvas
namespace PlayArea {
// Origen (esquina superior izquierda)
constexpr int X = 0;
constexpr int Y = 0;
// Dimensiones en tiles
constexpr int TILE_COLS = 40; // Ancho del mapa en tiles
constexpr int TILE_ROWS = 24; // Alto del mapa en tiles
constexpr int TILE_COUNT = TILE_COLS * TILE_ROWS; // 960 tiles totales
// Dimensiones en pixels
constexpr int WIDTH = TILE_COLS * Tile::SIZE; // 320
constexpr int HEIGHT = TILE_ROWS * Tile::SIZE; // 192
// Bordes (derivados, útiles para colisiones)
constexpr int LEFT = X;
constexpr int TOP = Y;
constexpr int RIGHT = X + WIDTH; // 320
constexpr int BOTTOM = Y + HEIGHT; // 192
// Puntos de referencia
constexpr int CENTER_X = X + (WIDTH / 2); // 160
constexpr int CENTER_Y = Y + (HEIGHT / 2); // 96
constexpr int QUARTER_X = WIDTH / 4;
constexpr int QUARTER_Y = HEIGHT / 4;
} // namespace PlayArea
namespace ScoreboardArea {
// Origen (justo debajo de PlayArea)
constexpr int X = 0;
constexpr int Y = PlayArea::BOTTOM; // 192
// Dimensiones
constexpr int WIDTH = GameCanvas::WIDTH; // 320
constexpr int HEIGHT = (6 * Tile::SIZE); // 48
// Bordes
constexpr int LEFT = X;
constexpr int TOP = Y;
constexpr int RIGHT = X + WIDTH;
constexpr int BOTTOM = Y + HEIGHT; // 240
} // namespace ScoreboardArea
namespace Collision {
constexpr int NONE = -1;
} // namespace Collision
namespace Flip {
constexpr SDL_FlipMode LEFT = SDL_FLIP_HORIZONTAL;
constexpr SDL_FlipMode RIGHT = SDL_FLIP_NONE;
} // namespace Flip

View File

@@ -0,0 +1,38 @@
#include "utils/delta_timer.hpp"
DeltaTimer::DeltaTimer() noexcept
: last_counter_(SDL_GetPerformanceCounter()),
perf_freq_(static_cast<double>(SDL_GetPerformanceFrequency())),
time_scale_(1.0F) {
}
auto DeltaTimer::tick() noexcept -> float {
const Uint64 NOW = SDL_GetPerformanceCounter();
const Uint64 DIFF = (NOW > last_counter_) ? (NOW - last_counter_) : 0;
last_counter_ = NOW;
const double SECONDS = static_cast<double>(DIFF) / perf_freq_;
return static_cast<float>(SECONDS * static_cast<double>(time_scale_));
}
auto DeltaTimer::peek() const noexcept -> float {
const Uint64 NOW = SDL_GetPerformanceCounter();
const Uint64 DIFF = (NOW > last_counter_) ? (NOW - last_counter_) : 0;
const double SECONDS = static_cast<double>(DIFF) / perf_freq_;
return static_cast<float>(SECONDS * static_cast<double>(time_scale_));
}
void DeltaTimer::reset(Uint64 counter) noexcept {
if (counter == 0) {
last_counter_ = SDL_GetPerformanceCounter();
} else {
last_counter_ = counter;
}
}
void DeltaTimer::setTimeScale(float scale) noexcept {
time_scale_ = std::max(scale, 0.0F);
}
auto DeltaTimer::getTimeScale() const noexcept -> float {
return time_scale_;
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include <SDL3/SDL.h>
#include <algorithm>
class DeltaTimer {
public:
DeltaTimer() noexcept;
// Calcula delta en segundos y actualiza el contador interno
auto tick() noexcept -> float;
// Devuelve el delta estimado desde el último tick sin actualizar el contador
[[nodiscard]] auto peek() const noexcept -> float;
// Reinicia el contador al valor actual o al valor pasado (en performance counter ticks)
void reset(Uint64 counter = 0) noexcept;
// Escala el tiempo retornado por tick/peek, por defecto 1.0f
void setTimeScale(float scale) noexcept;
[[nodiscard]] auto getTimeScale() const noexcept -> float;
private:
Uint64 last_counter_;
double perf_freq_;
float time_scale_;
};

View File

@@ -0,0 +1,251 @@
/**
* @file easing_functions.hpp
* @brief Colección de funciones de suavizado (easing) para animaciones
*
* Todas las funciones toman un parámetro t (0.0 a 1.0) que representa
* el progreso de la animación y retornan el valor suavizado.
*
* Convenciones:
* - In: Aceleración (slow -> fast)
* - Out: Desaceleración (fast -> slow)
* - InOut: Aceleración + Desaceleración (slow -> fast -> slow)
*/
#pragma once
#include <cmath>
#include <numbers>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
namespace Easing {
// LINEAR
inline auto linear(float t) -> float {
return t;
}
// QUAD (Cuadrática: t^2)
inline auto quadIn(float t) -> float {
return t * t;
}
inline auto quadOut(float t) -> float {
return t * (2.0F - t);
}
inline auto quadInOut(float t) -> float {
if (t < 0.5F) {
return 2.0F * t * t;
}
return -1.0F + ((4.0F - 2.0F * t) * t);
}
// CUBIC (Cúbica: t^3)
inline auto cubicIn(float t) -> float {
return t * t * t;
}
inline auto cubicOut(float t) -> float {
const float F = t - 1.0F;
return (F * F * F) + 1.0F;
}
inline auto cubicInOut(float t) -> float {
if (t < 0.5F) {
return 4.0F * t * t * t;
}
const float F = ((2.0F * t) - 2.0F);
return (0.5F * F * F * F) + 1.0F;
}
// QUART (Cuártica: t^4)
inline auto quartIn(float t) -> float {
return t * t * t * t;
}
inline auto quartOut(float t) -> float {
const float F = t - 1.0F;
return 1.0F - (F * F * F * F);
}
inline auto quartInOut(float t) -> float {
if (t < 0.5F) {
return 8.0F * t * t * t * t;
}
const float F = t - 1.0F;
return 1.0F - (8.0F * F * F * F * F);
}
// QUINT (Quíntica: t^5)
inline auto quintIn(float t) -> float {
return t * t * t * t * t;
}
inline auto quintOut(float t) -> float {
const float F = t - 1.0F;
return (F * F * F * F * F) + 1.0F;
}
inline auto quintInOut(float t) -> float {
if (t < 0.5F) {
return 16.0F * t * t * t * t * t;
}
const float F = ((2.0F * t) - 2.0F);
return (0.5F * F * F * F * F * F) + 1.0F;
}
// SINE (Sinusoidal)
inline auto sineIn(float t) -> float {
return 1.0F - std::cos(t * std::numbers::pi_v<float> * 0.5F);
}
inline auto sineOut(float t) -> float {
return std::sin(t * std::numbers::pi_v<float> * 0.5F);
}
inline auto sineInOut(float t) -> float {
return 0.5F * (1.0F - std::cos(std::numbers::pi_v<float> * t));
}
// EXPO (Exponencial)
inline auto expoIn(float t) -> float {
if (t == 0.0F) {
return 0.0F;
}
return std::pow(2.0F, 10.0F * (t - 1.0F));
}
inline auto expoOut(float t) -> float {
if (t == 1.0F) {
return 1.0F;
}
return 1.0F - std::pow(2.0F, -10.0F * t);
}
inline auto expoInOut(float t) -> float {
if (t == 0.0F || t == 1.0F) {
return t;
}
if (t < 0.5F) {
return 0.5F * std::pow(2.0F, (20.0F * t) - 10.0F);
}
return 0.5F * (2.0F - std::pow(2.0F, (-20.0F * t) + 10.0F));
}
// CIRC (Circular)
inline auto circIn(float t) -> float {
return 1.0F - std::sqrt(1.0F - (t * t));
}
inline auto circOut(float t) -> float {
const float F = t - 1.0F;
return std::sqrt(1.0F - (F * F));
}
inline auto circInOut(float t) -> float {
if (t < 0.5F) {
return 0.5F * (1.0F - std::sqrt(1.0F - (4.0F * t * t)));
}
const float F = (2.0F * t) - 2.0F;
return 0.5F * (std::sqrt(1.0F - (F * F)) + 1.0F);
}
// BACK (Overshoot - retrocede antes de avanzar)
inline auto backIn(float t, float overshoot = 1.70158F) -> float {
return t * t * ((overshoot + 1.0F) * t - overshoot);
}
inline auto backOut(float t, float overshoot = 1.70158F) -> float {
const float F = t - 1.0F;
return (F * F * ((overshoot + 1.0F) * F + overshoot)) + 1.0F;
}
inline auto backInOut(float t, float overshoot = 1.70158F) -> float {
const float S = overshoot * 1.525F;
if (t < 0.5F) {
const float F = 2.0F * t;
return 0.5F * (F * F * ((S + 1.0F) * F - S));
}
const float F = (2.0F * t) - 2.0F;
return 0.5F * (F * F * ((S + 1.0F) * F + S) + 2.0F);
}
// ELASTIC (Oscilación elástica - efecto de resorte)
inline auto elasticIn(float t, float amplitude = 1.0F, float period = 0.3F) -> float {
if (t == 0.0F || t == 1.0F) {
return t;
}
const float S = period / (2.0F * std::numbers::pi_v<float>)*std::asin(1.0F / amplitude);
const float F = t - 1.0F;
return -(amplitude * std::pow(2.0F, 10.0F * F) *
std::sin((F - S) * (2.0F * std::numbers::pi_v<float>) / period));
}
inline auto elasticOut(float t, float amplitude = 1.0F, float period = 0.3F) -> float {
if (t == 0.0F || t == 1.0F) {
return t;
}
const float S = period / (2.0F * std::numbers::pi_v<float>)*std::asin(1.0F / amplitude);
return (amplitude * std::pow(2.0F, -10.0F * t) *
std::sin((t - S) * (2.0F * std::numbers::pi_v<float>) / period)) +
1.0F;
}
inline auto elasticInOut(float t, float amplitude = 1.0F, float period = 0.3F) -> float {
if (t == 0.0F || t == 1.0F) {
return t;
}
const float S = period / (2.0F * std::numbers::pi_v<float>)*std::asin(1.0F / amplitude);
if (t < 0.5F) {
const float F = (2.0F * t) - 1.0F;
return -0.5F * (amplitude * std::pow(2.0F, 10.0F * F) * std::sin((F - S) * (2.0F * std::numbers::pi_v<float>) / period));
}
const float F = (2.0F * t) - 1.0F;
return (0.5F * amplitude * std::pow(2.0F, -10.0F * F) *
std::sin((F - S) * (2.0F * std::numbers::pi_v<float>) / period)) +
1.0F;
}
// BOUNCE (Rebote - simula física de rebote)
inline auto bounceOut(float t) -> float {
const float N1 = 7.5625F;
const float D1 = 2.75F;
if (t < 1.0F / D1) {
return N1 * t * t;
}
if (t < 2.0F / D1) {
const float F = t - (1.5F / D1);
return (N1 * F * F) + 0.75F;
}
if (t < 2.5F / D1) {
const float F = t - (2.25F / D1);
return (N1 * F * F) + 0.9375F;
}
const float F = t - (2.625F / D1);
return (N1 * F * F) + 0.984375F;
}
inline auto bounceIn(float t) -> float {
return 1.0F - bounceOut(1.0F - t);
}
inline auto bounceInOut(float t) -> float {
if (t < 0.5F) {
return 0.5F * bounceIn(2.0F * t);
}
return (0.5F * bounceOut((2.0F * t) - 1.0F)) + 0.5F;
}
} // namespace Easing

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

@@ -0,0 +1,367 @@
#include "utils/utils.hpp"
#include <algorithm> // Para find, transform
#include <cctype> // Para tolower
#include <cmath> // Para round, abs
#include <cstdlib> // Para abs
#include <exception> // Para exception
#include <filesystem> // Para path
#include <iostream> // Para basic_ostream, cout, basic_ios, ios, endl
#include <string> // Para basic_string, string, char_traits, allocator
#include <unordered_map> // Para unordered_map, operator==, _Node_const_iter...
#include <utility> // Para pair
#include "core/resources/resource_cache.hpp" // Para Resource
#include "utils/color.hpp" // Para Color
// 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);
}
// 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;
total_radius_squared = total_radius_squared * total_radius_squared;
// Si la distancia entre el centro de los circulos 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& rect) -> bool {
SDL_Rect b = toSDLRect(rect);
// Closest point on collision box
int c_x;
int c_y;
// Find closest x offset
if (a.x < b.x) {
c_x = b.x;
} else if (a.x > b.x + b.w) {
c_x = b.x + b.w;
} else {
c_x = a.x;
}
// Find closest y offset
if (a.y < b.y) {
c_y = b.y;
} else if (a.y > b.y + b.h) {
c_y = b.y + b.h;
} else {
c_y = a.y;
}
// If the closest point is inside the circle_t
if (distanceSquared(a.x, a.y, c_x, c_y) < a.r * a.r) {
// This box and the circle_t have collided
return true;
}
// If the shapes have not collided
return false;
}
// Detector de colisiones entre dos rectangulos
auto checkCollision(const SDL_FRect& rect_a, const SDL_FRect& rect_b) -> bool {
SDL_Rect a = toSDLRect(rect_a);
SDL_Rect b = toSDLRect(rect_b);
// Calcula las caras del rectangulo a
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;
// Calcula las caras del rectangulo b
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;
// Si cualquiera de las caras de a está fuera de b
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;
}
// Si ninguna de las caras está fuera de b
return true;
}
// Detector de colisiones entre un punto y un rectangulo
auto checkCollision(const SDL_FPoint& point, const SDL_FRect& rect) -> bool {
SDL_Rect r = toSDLRect(rect);
SDL_Point p = toSDLPoint(point);
// Comprueba si el punto está a la izquierda del rectangulo
if (p.x < r.x) {
return false;
}
// Comprueba si el punto está a la derecha del rectangulo
if (p.x > r.x + r.w) {
return false;
}
// Comprueba si el punto está por encima del rectangulo
if (p.y < r.y) {
return false;
}
// Comprueba si el punto está por debajo del rectangulo
if (p.y > r.y + r.h) {
return false;
}
// Si no está fuera, es que está dentro
return true;
}
// Detector de colisiones entre una linea horizontal y un rectangulo
auto checkCollision(const LineHorizontal& l, const SDL_FRect& rect) -> bool {
SDL_Rect r = toSDLRect(rect);
// Comprueba si la linea esta por encima del rectangulo
if (l.y < r.y) {
return false;
}
// Comprueba si la linea esta por debajo del rectangulo
if (l.y >= r.y + r.h) {
return false;
}
// Comprueba si el inicio de la linea esta a la derecha del rectangulo
if (l.x1 >= r.x + r.w) {
return false;
}
// Comprueba si el final de la linea esta a la izquierda del rectangulo
if (l.x2 < r.x) {
return false;
}
// Si ha llegado hasta aquí, hay colisión
return true;
}
// Detector de colisiones entre una linea vertical y un rectangulo
auto checkCollision(const LineVertical& l, const SDL_FRect& rect) -> bool {
SDL_Rect r = toSDLRect(rect);
// Comprueba si la linea esta por la izquierda del rectangulo
if (l.x < r.x) {
return false;
}
// Comprueba si la linea esta por la derecha del rectangulo
if (l.x >= r.x + r.w) {
return false;
}
// Comprueba si el inicio de la linea esta debajo del rectangulo
if (l.y1 >= r.y + r.h) {
return false;
}
// Comprueba si el final de la linea esta encima del rectangulo
if (l.y2 < r.y) {
return false;
}
// Si ha llegado hasta aquí, hay colisión
return true;
}
// Detector de colisiones entre una linea horizontal y un punto
auto checkCollision(const LineHorizontal& l, const SDL_FPoint& point) -> bool {
SDL_Point p = toSDLPoint(point);
// Comprueba si el punto esta sobre la linea
if (p.y > l.y) {
return false;
}
// Comprueba si el punto esta bajo la linea
if (p.y < l.y) {
return false;
}
// Comprueba si el punto esta a la izquierda de la linea
if (p.x < l.x1) {
return false;
}
// Comprueba si el punto esta a la derecha de la linea
if (p.x > l.x2) {
return false;
}
// Si ha llegado aquí, hay colisión
return true;
}
// Detector de colisiones entre dos lineas
auto checkCollision(const Line& l1, const Line& l2) -> SDL_Point {
const float X1 = l1.x1;
const float Y1 = l1.y1;
const float X2 = l1.x2;
const float Y2 = l1.y2;
const float X3 = l2.x1;
const float Y3 = l2.y1;
const float X4 = l2.x2;
const float Y4 = l2.y2;
// calculate the direction of the lines
float u_a = ((X4 - X3) * (Y1 - Y3) - (Y4 - Y3) * (X1 - X3)) / ((Y4 - Y3) * (X2 - X1) - (X4 - X3) * (Y2 - Y1));
float u_b = ((X2 - X1) * (Y1 - Y3) - (Y2 - Y1) * (X1 - X3)) / ((Y4 - Y3) * (X2 - X1) - (X4 - X3) * (Y2 - Y1));
// if uA and uB are between 0-1, lines are colliding
if (u_a >= 0 && u_a <= 1 && u_b >= 0 && u_b <= 1) {
// Calcula la intersección
const float X = X1 + (u_a * (X2 - X1));
const float Y = Y1 + (u_a * (Y2 - Y1));
return {static_cast<int>(std::round(X)), static_cast<int>(std::round(Y))};
}
return {-1, -1};
}
// Convierte SDL_FRect a SDL_Rect
auto toSDLRect(const SDL_FRect& frect) -> SDL_Rect {
SDL_Rect rect = {
.x = static_cast<int>(frect.x),
.y = static_cast<int>(frect.y),
.w = static_cast<int>(frect.w),
.h = static_cast<int>(frect.h)};
return rect;
}
// Convierte SDL_FPoint a SDL_Point
auto toSDLPoint(const SDL_FPoint& fpoint) -> SDL_Point {
SDL_Point point = {
.x = static_cast<int>(fpoint.x),
.y = static_cast<int>(fpoint.y)};
return point;
}
// Convierte una cadena a un indice de la paleta
auto stringToColor(const std::string& str) -> Uint8 {
return Color::fromString(str);
}
// Convierte una cadena a un entero de forma segura
auto safeStoi(const std::string& value, int default_value) -> int {
try {
return std::stoi(value);
} catch (const std::exception&) {
return default_value;
}
}
// Convierte una cadena a un booleano
auto stringToBool(const std::string& str) -> bool {
std::string lower_str = str;
std::ranges::transform(lower_str, lower_str.begin(), ::tolower);
return (lower_str == "true" || lower_str == "1" || lower_str == "yes" || lower_str == "on");
}
// Convierte un booleano a una cadena
auto boolToString(bool value) -> std::string {
return value ? "1" : "0";
}
// Compara dos colores
auto colorAreEqual(ColorRGB color1, ColorRGB color2) -> bool {
const bool R = color1.r == color2.r;
const bool G = color1.g == color2.g;
const bool B = color1.b == color2.b;
return (R && G && B);
}
// Función para convertir un string a minúsculas
auto toLower(const std::string& str) -> std::string {
std::string lower_str = str;
std::ranges::transform(lower_str, lower_str.begin(), ::tolower);
return lower_str;
}
// Función para convertir un string a mayúsculas
auto toUpper(const std::string& str) -> std::string {
std::string upper_str = str;
std::ranges::transform(upper_str, upper_str.begin(), ::toupper);
return upper_str;
}
// 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();
}
// Imprime por pantalla una linea de texto de tamaño fijo rellena con puntos
void printWithDots(const std::string& text1, const std::string& text2, const std::string& text3) {
std::cout.setf(std::ios::left, std::ios::adjustfield);
std::cout << text1;
std::cout.width(50 - text1.length() - text3.length());
std::cout.fill('.');
std::cout << text2;
std::cout << text3 << '\n';
}
// 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();
}
// Rellena una textura de un color
void fillTextureWithColor(SDL_Renderer* renderer, SDL_Texture* texture, Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
// Guardar el render target actual
SDL_Texture* previous_target = SDL_GetRenderTarget(renderer);
// Establecer la textura como el render target
SDL_SetRenderTarget(renderer, texture);
// Establecer el color deseado
SDL_SetRenderDrawColor(renderer, r, g, b, a);
// Pintar toda el área
SDL_RenderClear(renderer);
// Restaurar el render target previo
SDL_SetRenderTarget(renderer, previous_target);
}
// 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;
}

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

@@ -0,0 +1,81 @@
#pragma once
#include <SDL3/SDL.h>
#include <string> // Para string
#include <vector> // Para vector
// Estructura para definir un circulo
struct Circle {
int x{0};
int y{0};
int r{0};
};
// Estructura para definir una linea horizontal
struct LineHorizontal {
int x1{0}, x2{0}, y{0};
};
// Estructura para definir una linea vertical
struct LineVertical {
int x{0}, y1{0}, y2{0};
};
// Estructura para definir una linea
struct Line {
int x1{0}, y1{0}, x2{0}, y2{0};
};
// Estructura para definir un color RGB
struct ColorRGB {
Uint8 r{0};
Uint8 g{0};
Uint8 b{0};
// Constructor
ColorRGB(Uint8 red, Uint8 green, Uint8 blue)
: r(red),
g(green),
b(blue) {}
};
// COLISIONES Y GEOMETRÍA
auto distanceSquared(int x1, int y1, int x2, int y2) -> double; // Distancia² entre dos puntos
auto checkCollision(const Circle& a, const Circle& b) -> bool; // Colisión círculo-círculo
auto checkCollision(const Circle& a, const SDL_FRect& rect) -> bool; // Colisión círculo-rectángulo
auto checkCollision(const SDL_FRect& a, const SDL_FRect& b) -> bool; // Colisión rectángulo-rectángulo
auto checkCollision(const SDL_FPoint& p, const SDL_FRect& r) -> bool; // Colisión punto-rectángulo
auto checkCollision(const LineHorizontal& l, const SDL_FRect& r) -> bool; // Colisión línea horizontal-rectángulo
auto checkCollision(const LineVertical& l, const SDL_FRect& r) -> bool; // Colisión línea vertical-rectángulo
auto checkCollision(const LineHorizontal& l, const SDL_FPoint& p) -> bool; // Colisión línea horizontal-punto
auto checkCollision(const Line& l1, const Line& l2) -> SDL_Point; // Colisión línea-línea (intersección)
// CONVERSIONES DE TIPOS SDL
auto toSDLRect(const SDL_FRect& frect) -> SDL_Rect; // Convierte SDL_FRect a SDL_Rect
auto toSDLPoint(const SDL_FPoint& fpoint) -> SDL_Point; // Convierte SDL_FPoint a SDL_Point
// CONVERSIONES DE STRING
auto stringToColor(const std::string& str) -> Uint8; // String a índice de paleta
auto safeStoi(const std::string& value, int default_value = 0) -> int; // String a int seguro (sin excepciones)
auto stringToBool(const std::string& str) -> bool; // String a bool (true/1/yes/on)
auto boolToString(bool value) -> std::string; // Bool a string (1/0)
auto toLower(const std::string& str) -> std::string; // String a minúsculas
auto toUpper(const std::string& str) -> std::string; // String a mayúsculas
// OPERACIONES CON STRINGS
auto stringInVector(const std::vector<std::string>& vec, const std::string& str) -> bool; // Busca string en vector
auto spaceBetweenLetters(const std::string& input) -> std::string; // Añade espacios entre letras
// OPERACIONES CON COLORES
auto colorAreEqual(ColorRGB color1, ColorRGB color2) -> bool; // Compara dos colores RGB
// OPERACIONES CON FICHEROS
auto getFileName(const std::string& path) -> std::string; // Extrae nombre de fichero de ruta
auto getPath(const std::string& full_path) -> std::string; // Extrae directorio de ruta completa
// RENDERIZADO
void fillTextureWithColor(SDL_Renderer* renderer, SDL_Texture* texture, Uint8 r, Uint8 g, Uint8 b, Uint8 a); // Rellena textura
// OUTPUT Y UTILIDADES DE CONSOLA
void printWithDots(const std::string& text1, const std::string& text2, const std::string& text3); // Imprime línea con puntos