primer commit
This commit is contained in:
57
source/utils/color.cpp
Normal file
57
source/utils/color.cpp
Normal 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
94
source/utils/color.hpp
Normal 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
72
source/utils/defines.hpp
Normal 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
|
||||
38
source/utils/delta_timer.cpp
Normal file
38
source/utils/delta_timer.cpp
Normal 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_;
|
||||
}
|
||||
28
source/utils/delta_timer.hpp
Normal file
28
source/utils/delta_timer.hpp
Normal 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_;
|
||||
};
|
||||
251
source/utils/easing_functions.hpp
Normal file
251
source/utils/easing_functions.hpp
Normal 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
367
source/utils/utils.cpp
Normal 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
81
source/utils/utils.hpp
Normal 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
|
||||
Reference in New Issue
Block a user