Compare commits
6 Commits
merdes-nov
...
bcdd48d622
| Author | SHA1 | Date | |
|---|---|---|---|
| bcdd48d622 | |||
| 6985569573 | |||
| 5db43e674d | |||
| 34baa3c97d | |||
| bddb790fe2 | |||
| 6fae12ba02 |
@@ -64,6 +64,7 @@ set(APP_SOURCES
|
|||||||
source/balloon_manager.cpp
|
source/balloon_manager.cpp
|
||||||
source/balloon.cpp
|
source/balloon.cpp
|
||||||
source/bullet.cpp
|
source/bullet.cpp
|
||||||
|
source/bullet_manager.cpp
|
||||||
source/enter_name.cpp
|
source/enter_name.cpp
|
||||||
source/explosions.cpp
|
source/explosions.cpp
|
||||||
source/game_logo.cpp
|
source/game_logo.cpp
|
||||||
|
|||||||
109
source/bullet_manager.cpp
Normal file
109
source/bullet_manager.cpp
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#include "bullet_manager.h"
|
||||||
|
|
||||||
|
#include <algorithm> // Para remove_if
|
||||||
|
|
||||||
|
#include "bullet.h" // Para Bullet
|
||||||
|
#include "param.h" // Para param
|
||||||
|
#include "player.h" // Para Player
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
BulletManager::BulletManager()
|
||||||
|
: play_area_(param.game.play_area.rect) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actualiza el estado de todas las balas
|
||||||
|
void BulletManager::update(float deltaTime) {
|
||||||
|
for (auto& bullet : bullets_) {
|
||||||
|
if (bullet->isEnabled()) {
|
||||||
|
processBulletUpdate(bullet, deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Renderiza todas las balas activas
|
||||||
|
void BulletManager::render() {
|
||||||
|
for (auto& bullet : bullets_) {
|
||||||
|
if (bullet->isEnabled()) {
|
||||||
|
bullet->render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crea una nueva bala
|
||||||
|
void BulletManager::createBullet(int x, int y, Bullet::Type type, Bullet::Color color, int owner) {
|
||||||
|
bullets_.emplace_back(std::make_shared<Bullet>(x, y, type, color, owner));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Libera balas que ya no están habilitadas
|
||||||
|
void BulletManager::freeBullets() {
|
||||||
|
if (!bullets_.empty()) {
|
||||||
|
// Elimina las balas deshabilitadas del vector
|
||||||
|
bullets_.erase(
|
||||||
|
std::remove_if(bullets_.begin(), bullets_.end(),
|
||||||
|
[](const std::shared_ptr<Bullet>& bullet) {
|
||||||
|
return !bullet->isEnabled();
|
||||||
|
}),
|
||||||
|
bullets_.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Elimina todas las balas
|
||||||
|
void BulletManager::clearAllBullets() {
|
||||||
|
bullets_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifica colisiones de todas las balas
|
||||||
|
void BulletManager::checkCollisions() {
|
||||||
|
for (auto& bullet : bullets_) {
|
||||||
|
if (!bullet->isEnabled()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifica colisión con Tabe
|
||||||
|
if (tabe_collision_callback_ && tabe_collision_callback_(bullet)) {
|
||||||
|
break; // Sale del bucle si hubo colisión
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifica colisión con globos
|
||||||
|
if (balloon_collision_callback_ && balloon_collision_callback_(bullet)) {
|
||||||
|
break; // Sale del bucle si hubo colisión
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Establece el callback para colisión con Tabe
|
||||||
|
void BulletManager::setTabeCollisionCallback(CollisionCallback callback) {
|
||||||
|
tabe_collision_callback_ = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Establece el callback para colisión con globos
|
||||||
|
void BulletManager::setBalloonCollisionCallback(CollisionCallback callback) {
|
||||||
|
balloon_collision_callback_ = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Establece el callback para balas fuera de límites
|
||||||
|
void BulletManager::setOutOfBoundsCallback(OutOfBoundsCallback callback) {
|
||||||
|
out_of_bounds_callback_ = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Métodos privados ---
|
||||||
|
|
||||||
|
// Procesa la actualización individual de una bala
|
||||||
|
void BulletManager::processBulletUpdate(const std::shared_ptr<Bullet>& bullet, float deltaTime) {
|
||||||
|
auto status = bullet->update(deltaTime);
|
||||||
|
|
||||||
|
// Si la bala salió de los límites, llama al callback
|
||||||
|
if (status == Bullet::MoveStatus::OUT && out_of_bounds_callback_) {
|
||||||
|
out_of_bounds_callback_(bullet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifica si la bala está fuera de los límites del área de juego
|
||||||
|
auto BulletManager::isBulletOutOfBounds(const std::shared_ptr<Bullet>& bullet) -> bool {
|
||||||
|
auto collider = bullet->getCollider();
|
||||||
|
|
||||||
|
return (collider.x < play_area_.x ||
|
||||||
|
collider.x > play_area_.x + play_area_.w ||
|
||||||
|
collider.y < play_area_.y ||
|
||||||
|
collider.y > play_area_.y + play_area_.h);
|
||||||
|
}
|
||||||
79
source/bullet_manager.h
Normal file
79
source/bullet_manager.h
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h> // Para SDL_FRect
|
||||||
|
|
||||||
|
#include <functional> // Para function
|
||||||
|
#include <memory> // Para shared_ptr, unique_ptr
|
||||||
|
#include <vector> // Para vector
|
||||||
|
|
||||||
|
#include "bullet.h" // Para Bullet
|
||||||
|
#include "utils.h" // Para Circle
|
||||||
|
|
||||||
|
// --- Types ---
|
||||||
|
using Bullets = std::vector<std::shared_ptr<Bullet>>;
|
||||||
|
|
||||||
|
// --- Forward declarations ---
|
||||||
|
class Player;
|
||||||
|
|
||||||
|
// --- Clase BulletManager: gestiona todas las balas del juego ---
|
||||||
|
//
|
||||||
|
// Esta clase se encarga de la gestión completa de las balas del juego,
|
||||||
|
// incluyendo su creación, actualización, renderizado y colisiones.
|
||||||
|
//
|
||||||
|
// Funcionalidades principales:
|
||||||
|
// • Gestión del ciclo de vida: creación, actualización y destrucción de balas
|
||||||
|
// • Renderizado: dibuja todas las balas activas en pantalla
|
||||||
|
// • Detección de colisiones: mediante sistema de callbacks
|
||||||
|
// • Limpieza automática: elimina balas deshabilitadas del contenedor
|
||||||
|
// • Configuración flexible: permite ajustar parámetros de las balas
|
||||||
|
//
|
||||||
|
// La clase utiliza un sistema de callbacks para manejar las colisiones,
|
||||||
|
// permitiendo que la lógica específica del juego permanezca en Game.
|
||||||
|
class BulletManager {
|
||||||
|
public:
|
||||||
|
// --- Types para callbacks ---
|
||||||
|
using CollisionCallback = std::function<bool(const std::shared_ptr<Bullet>&)>;
|
||||||
|
using OutOfBoundsCallback = std::function<void(const std::shared_ptr<Bullet>&)>;
|
||||||
|
|
||||||
|
// --- Constructor y destructor ---
|
||||||
|
BulletManager();
|
||||||
|
~BulletManager() = default;
|
||||||
|
|
||||||
|
// --- Métodos principales ---
|
||||||
|
void update(float deltaTime); // Actualiza el estado de las balas (time-based)
|
||||||
|
void render(); // Renderiza las balas en pantalla
|
||||||
|
|
||||||
|
// --- Gestión de balas ---
|
||||||
|
void createBullet(int x, int y, Bullet::Type type, Bullet::Color color, int owner); // Crea una nueva bala
|
||||||
|
void freeBullets(); // Libera balas que ya no sirven
|
||||||
|
void clearAllBullets(); // Elimina todas las balas
|
||||||
|
|
||||||
|
// --- Detección de colisiones ---
|
||||||
|
void checkCollisions(); // Verifica colisiones de todas las balas
|
||||||
|
void setTabeCollisionCallback(CollisionCallback callback); // Establece callback para colisión con Tabe
|
||||||
|
void setBalloonCollisionCallback(CollisionCallback callback); // Establece callback para colisión con globos
|
||||||
|
void setOutOfBoundsCallback(OutOfBoundsCallback callback); // Establece callback para balas fuera de límites
|
||||||
|
|
||||||
|
// --- Configuración ---
|
||||||
|
void setPlayArea(SDL_FRect play_area) { play_area_ = play_area; }; // Define el área de juego
|
||||||
|
|
||||||
|
// --- Getters ---
|
||||||
|
auto getBullets() -> Bullets& { return bullets_; } // Obtiene referencia al vector de balas
|
||||||
|
[[nodiscard]] auto getNumBullets() const -> int { return bullets_.size(); } // Obtiene el número de balas activas
|
||||||
|
|
||||||
|
private:
|
||||||
|
// --- Objetos y punteros ---
|
||||||
|
Bullets bullets_; // Vector con las balas activas
|
||||||
|
|
||||||
|
// --- Variables de configuración ---
|
||||||
|
SDL_FRect play_area_; // Área de juego para límites
|
||||||
|
|
||||||
|
// --- Callbacks para colisiones ---
|
||||||
|
CollisionCallback tabe_collision_callback_; // Callback para colisión con Tabe
|
||||||
|
CollisionCallback balloon_collision_callback_; // Callback para colisión con globos
|
||||||
|
OutOfBoundsCallback out_of_bounds_callback_; // Callback para balas fuera de límites
|
||||||
|
|
||||||
|
// --- Métodos internos ---
|
||||||
|
void processBulletUpdate(const std::shared_ptr<Bullet>& bullet, float deltaTime); // Procesa actualización individual
|
||||||
|
auto isBulletOutOfBounds(const std::shared_ptr<Bullet>& bullet) -> bool; // Verifica si la bala está fuera de límites
|
||||||
|
};
|
||||||
@@ -95,6 +95,29 @@ struct Color {
|
|||||||
return Color(new_r, new_g, new_b, new_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
|
// Convierte el color a un entero de 32 bits en formato RGBA
|
||||||
[[nodiscard]] constexpr auto TO_UINT32() const -> Uint32 {
|
[[nodiscard]] constexpr auto TO_UINT32() const -> Uint32 {
|
||||||
return (static_cast<Uint32>(r) << 24) |
|
return (static_cast<Uint32>(r) << 24) |
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ Director::Director(int argc, std::span<char *> argv) {
|
|||||||
Section::name = Section::Name::GAME;
|
Section::name = Section::Name::GAME;
|
||||||
Section::options = Section::Options::GAME_PLAY_1P;
|
Section::options = Section::Options::GAME_PLAY_1P;
|
||||||
#elif _DEBUG
|
#elif _DEBUG
|
||||||
Section::name = Section::Name::TITLE;
|
Section::name = Section::Name::GAME;
|
||||||
Section::options = Section::Options::GAME_PLAY_1P;
|
Section::options = Section::Options::GAME_PLAY_1P;
|
||||||
#else // NORMAL GAME
|
#else // NORMAL GAME
|
||||||
Section::name = Section::Name::LOGO;
|
Section::name = Section::Name::LOGO;
|
||||||
|
|||||||
@@ -1,147 +1,96 @@
|
|||||||
#include "enter_name.h"
|
#include "enter_name.h"
|
||||||
|
|
||||||
#include <cstddef> // Para size_t
|
#include <array> // Para array
|
||||||
#include <cstdlib> // Para rand
|
#include <cstdlib> // Para rand
|
||||||
#include <string_view> // Para basic_string_view, string_view
|
#include <string_view> // Para basic_string_view, string_view
|
||||||
|
|
||||||
#include "utils.h" // Para trim
|
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
EnterName::EnterName()
|
EnterName::EnterName()
|
||||||
: character_list_(" ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789."),
|
: character_list_("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789. "),
|
||||||
character_index_{0} {}
|
selected_index_(0) {}
|
||||||
|
|
||||||
// Inicializa el objeto
|
// Inicializa el objeto
|
||||||
void EnterName::init(const std::string &name) {
|
void EnterName::init(const std::string &name) {
|
||||||
// No se pasa ningún nombre
|
name_ = sanitizeName(name);
|
||||||
if (name.empty()) {
|
selected_index_ = 0;
|
||||||
name_ = "A";
|
|
||||||
position_ = 0;
|
|
||||||
position_overflow_ = false;
|
|
||||||
}
|
|
||||||
// Se pasa un nombre
|
|
||||||
else {
|
|
||||||
name_ = name;
|
|
||||||
position_ = name_.length();
|
|
||||||
position_overflow_ = position_ >= NAME_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inicializa el vector de indices con el nombre y espacios
|
|
||||||
initCharacterIndex(name_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Incrementa la posición
|
// Incrementa el índice del carácter seleccionado
|
||||||
void EnterName::incPosition() {
|
|
||||||
if (position_overflow_) {
|
|
||||||
// Si ya estamos en overflow, no incrementamos más.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
++position_;
|
|
||||||
|
|
||||||
if (position_ >= NAME_SIZE) {
|
|
||||||
position_ = NAME_SIZE; // Mantenemos en el índice máximo válido.
|
|
||||||
position_overflow_ = true; // Activamos el flag de overflow.
|
|
||||||
} else if (position_ > 0) // No es necesario verificar position_ < MAX_NAME_LENGTH
|
|
||||||
{
|
|
||||||
// Copiamos el índice del carácter anterior si es posible.
|
|
||||||
// character_index_[position_] = character_index_[position_ - 1];
|
|
||||||
|
|
||||||
// Ponemos el caracter "espacio"
|
|
||||||
character_index_[position_] = 0;
|
|
||||||
} else {
|
|
||||||
// Si position_ es 0, inicializamos el carácter actual.
|
|
||||||
character_index_[position_] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateNameFromCharacterIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrementa la posición
|
|
||||||
void EnterName::decPosition() {
|
|
||||||
if (position_overflow_) {
|
|
||||||
// Si estaba en overflow, lo desactivamos y mantenemos position_ en el máximo.
|
|
||||||
position_overflow_ = false;
|
|
||||||
position_ = NAME_SIZE - 1;
|
|
||||||
} else {
|
|
||||||
if (position_ > 0) {
|
|
||||||
--position_;
|
|
||||||
|
|
||||||
// Limpiamos el carácter siguiente si el índice es válido.
|
|
||||||
if (position_ + 1 < NAME_SIZE) {
|
|
||||||
character_index_[position_ + 1] = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Si position_ es 0, aseguramos que no vaya a ser negativo y limpiamos el carácter actual.
|
|
||||||
position_ = 0;
|
|
||||||
// character_index_[position_] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Si position_ es menor que NAME_LENGTH, aseguramos que el overflow esté desactivado.
|
|
||||||
if (position_ < NAME_SIZE) {
|
|
||||||
position_overflow_ = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateNameFromCharacterIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Incrementa el índice
|
|
||||||
void EnterName::incIndex() {
|
void EnterName::incIndex() {
|
||||||
if (position_overflow_) {
|
++selected_index_;
|
||||||
return;
|
if (selected_index_ >= static_cast<int>(character_list_.size())) {
|
||||||
|
selected_index_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
++character_index_[position_];
|
|
||||||
if (character_index_[position_] >= static_cast<int>(character_list_.size())) {
|
|
||||||
character_index_[position_] = 0;
|
|
||||||
}
|
|
||||||
updateNameFromCharacterIndex();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrementa el índice
|
// Decrementa el índice del carácter seleccionado
|
||||||
void EnterName::decIndex() {
|
void EnterName::decIndex() {
|
||||||
if (position_overflow_) {
|
--selected_index_;
|
||||||
return;
|
if (selected_index_ < 0) {
|
||||||
}
|
selected_index_ = character_list_.size() - 1;
|
||||||
|
|
||||||
--character_index_[position_];
|
|
||||||
if (character_index_[position_] < 0) {
|
|
||||||
character_index_[position_] = character_list_.size() - 1;
|
|
||||||
}
|
|
||||||
updateNameFromCharacterIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actualiza el nombre a partir de la lista de índices
|
|
||||||
void EnterName::updateNameFromCharacterIndex() {
|
|
||||||
name_.clear();
|
|
||||||
for (size_t i = 0; i < NAME_SIZE; ++i) {
|
|
||||||
name_.push_back(character_list_[character_index_[i]]);
|
|
||||||
}
|
|
||||||
name_ = trim(name_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actualiza la variable
|
|
||||||
void EnterName::initCharacterIndex(const std::string &name) {
|
|
||||||
// Rellena de espacios
|
|
||||||
for (size_t i = 0; i < NAME_SIZE; ++i) {
|
|
||||||
character_index_[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Coloca los índices en función de los caracteres que forman el nombre
|
|
||||||
for (size_t i = 0; i < name.substr(0, NAME_SIZE).size(); ++i) {
|
|
||||||
character_index_[i] = findIndex(name.at(i));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encuentra el indice de un caracter en "character_list_"
|
// Añade el carácter seleccionado al nombre
|
||||||
auto EnterName::findIndex(char character) const -> int {
|
void EnterName::addCharacter() {
|
||||||
for (size_t i = 0; i < character_list_.size(); ++i) {
|
if (name_.length() < MAX_NAME_SIZE) {
|
||||||
if (character == character_list_.at(i)) {
|
name_.push_back(character_list_[selected_index_]);
|
||||||
return i;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Elimina el último carácter del nombre
|
||||||
|
void EnterName::removeLastCharacter() {
|
||||||
|
if (!name_.empty()) {
|
||||||
|
name_.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Devuelve el carácter seleccionado con offset relativo como string
|
||||||
|
auto EnterName::getSelectedCharacter(int offset) const -> std::string {
|
||||||
|
// Calcular el índice con offset, con wrap-around circular
|
||||||
|
int size = static_cast<int>(character_list_.size());
|
||||||
|
int index = (selected_index_ + offset) % size;
|
||||||
|
|
||||||
|
// Manejar índices negativos (hacer wrap-around hacia atrás)
|
||||||
|
if (index < 0) {
|
||||||
|
index += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::string(1, character_list_[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Devuelve el carrusel completo de caracteres centrado en el seleccionado
|
||||||
|
auto EnterName::getCarousel(int size) const -> std::string {
|
||||||
|
// Asegurar que el tamaño sea impar para tener un centro claro
|
||||||
|
if (size % 2 == 0) {
|
||||||
|
++size;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string carousel;
|
||||||
|
carousel.reserve(size); // Optimización: reservar memoria de antemano
|
||||||
|
|
||||||
|
int half = size / 2;
|
||||||
|
|
||||||
|
// Construir desde -half hasta +half (inclusive)
|
||||||
|
for (int offset = -half; offset <= half; ++offset) {
|
||||||
|
carousel += getSelectedCharacter(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return carousel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valida y limpia el nombre: solo caracteres legales y longitud máxima
|
||||||
|
auto EnterName::sanitizeName(const std::string &name) const -> std::string {
|
||||||
|
std::string sanitized;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < name.length() && sanitized.length() < MAX_NAME_SIZE; ++i) {
|
||||||
|
// Verifica si el carácter está en la lista permitida
|
||||||
|
if (character_list_.find(name[i]) != std::string::npos) {
|
||||||
|
sanitized.push_back(name[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
return sanitized;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Devuelve un nombre al azar
|
// Devuelve un nombre al azar
|
||||||
@@ -157,12 +106,11 @@ auto EnterName::getRandomName() -> std::string {
|
|||||||
"PEPE"};
|
"PEPE"};
|
||||||
return std::string(NAMES[rand() % NAMES.size()]);
|
return std::string(NAMES[rand() % NAMES.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtiene el nombre final introducido
|
// Obtiene el nombre final introducido
|
||||||
auto EnterName::getFinalName() -> std::string {
|
auto EnterName::getFinalName() -> std::string {
|
||||||
auto name = trim(name_.substr(0, position_ + 1)); // Devuelve el texto intruducido incluyendo el del selector
|
if (name_.empty()) {
|
||||||
if (name.empty()) {
|
name_ = getRandomName();
|
||||||
name = getRandomName();
|
|
||||||
}
|
}
|
||||||
name_ = name;
|
|
||||||
return name_;
|
return name_;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,36 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array> // Para array
|
|
||||||
#include <cstddef> // Para size_t
|
#include <cstddef> // Para size_t
|
||||||
#include <string> // Para allocator, string
|
#include <string> // Para allocator, string
|
||||||
|
|
||||||
#include "utils.h" // Para trim
|
|
||||||
|
|
||||||
// --- Constantes ---
|
|
||||||
constexpr size_t NAME_SIZE = 5; // Tamaño máximo del nombre
|
|
||||||
|
|
||||||
// --- Clase EnterName: gestor de entrada de nombre del jugador ---
|
// --- Clase EnterName: gestor de entrada de nombre del jugador ---
|
||||||
class EnterName {
|
class EnterName {
|
||||||
public:
|
public:
|
||||||
|
// --- Constantes ---
|
||||||
|
static constexpr size_t MAX_NAME_SIZE = 6; // Tamaño máximo del nombre
|
||||||
|
|
||||||
EnterName();
|
EnterName();
|
||||||
~EnterName() = default;
|
~EnterName() = default;
|
||||||
|
|
||||||
void init(const std::string &name = ""); // Inicializa con un nombre opcional
|
void init(const std::string &name = ""); // Inicializa con nombre opcional (vacío por defecto)
|
||||||
|
|
||||||
void incPosition(); // Incrementa la posición del carácter actual
|
void incIndex(); // Incrementa el índice del carácter seleccionado en la lista
|
||||||
void decPosition(); // Decrementa la posición del carácter actual
|
void decIndex(); // Decrementa el índice del carácter seleccionado en la lista
|
||||||
void incIndex(); // Incrementa el índice del carácter en la lista
|
|
||||||
void decIndex(); // Decrementa el índice del carácter en la lista
|
|
||||||
|
|
||||||
auto getFinalName() -> std::string; // Obtiene el nombre final introducido
|
void addCharacter(); // Añade el carácter seleccionado al nombre
|
||||||
[[nodiscard]] auto getCurrentName() const -> std::string { return trim(name_); } // Obtiene el nombre actual en proceso
|
void removeLastCharacter(); // Elimina el último carácter del nombre
|
||||||
|
|
||||||
[[nodiscard]] auto getPosition() const -> int { return position_; } // Posición actual del carácter editado
|
auto getFinalName() -> std::string; // Obtiene el nombre final (o aleatorio si vacío)
|
||||||
[[nodiscard]] auto getPositionOverflow() const -> bool { return position_overflow_; } // Indica si la posición excede el límite
|
[[nodiscard]] auto getCurrentName() const -> std::string { return name_; } // Obtiene el nombre actual en proceso
|
||||||
|
[[nodiscard]] auto getSelectedCharacter(int offset = 0) const -> std::string; // Devuelve el carácter seleccionado con offset relativo
|
||||||
|
[[nodiscard]] auto getCarousel(int size) const -> std::string; // Devuelve el carrusel de caracteres (size debe ser impar)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// --- Variables de estado ---
|
// --- Variables de estado ---
|
||||||
std::string character_list_; // Lista de caracteres permitidos
|
std::string character_list_; // Lista de caracteres permitidos
|
||||||
std::string name_; // Nombre en proceso
|
std::string name_; // Nombre en proceso
|
||||||
std::array<int, NAME_SIZE> character_index_; // Índices a "character_list_"
|
int selected_index_ = 0; // Índice del carácter seleccionado en "character_list_"
|
||||||
size_t position_ = 0; // Índice del carácter que se edita
|
|
||||||
bool position_overflow_ = false; // Flag para exceder límite
|
|
||||||
|
|
||||||
void updateNameFromCharacterIndex(); // Actualiza "name_" según "character_index_"
|
[[nodiscard]] auto sanitizeName(const std::string &name) const -> std::string; // Valida y limpia el nombre
|
||||||
void initCharacterIndex(const std::string &name); // Inicializa índices desde el nombre
|
static auto getRandomName() -> std::string; // Devuelve un nombre al azar
|
||||||
[[nodiscard]] auto findIndex(char character) const -> int; // Busca el índice de un carácter en "character_list_"
|
|
||||||
static auto getRandomName() -> std::string; // Devuelve un nombre al azar
|
|
||||||
};
|
};
|
||||||
@@ -128,16 +128,16 @@ void Player::setInputPlaying(Input::Action action) {
|
|||||||
// Procesa inputs para cuando está introduciendo el nombre
|
// Procesa inputs para cuando está introduciendo el nombre
|
||||||
void Player::setInputEnteringName(Input::Action action) {
|
void Player::setInputEnteringName(Input::Action action) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case Input::Action::LEFT:
|
case Input::Action::FIRE_LEFT:
|
||||||
enter_name_->decPosition();
|
enter_name_->addCharacter();
|
||||||
|
break;
|
||||||
|
case Input::Action::FIRE_CENTER:
|
||||||
|
enter_name_->removeLastCharacter();
|
||||||
break;
|
break;
|
||||||
case Input::Action::RIGHT:
|
case Input::Action::RIGHT:
|
||||||
enter_name_->incPosition();
|
|
||||||
break;
|
|
||||||
case Input::Action::UP:
|
|
||||||
enter_name_->incIndex();
|
enter_name_->incIndex();
|
||||||
break;
|
break;
|
||||||
case Input::Action::DOWN:
|
case Input::Action::LEFT:
|
||||||
enter_name_->decIndex();
|
enter_name_->decIndex();
|
||||||
break;
|
break;
|
||||||
case Input::Action::START:
|
case Input::Action::START:
|
||||||
@@ -540,8 +540,9 @@ void Player::updateScoreboard() {
|
|||||||
}
|
}
|
||||||
case State::ENTERING_NAME:
|
case State::ENTERING_NAME:
|
||||||
case State::ENTERING_NAME_GAME_COMPLETED: {
|
case State::ENTERING_NAME_GAME_COMPLETED: {
|
||||||
Scoreboard::get()->setRecordName(scoreboard_panel_, enter_name_->getCurrentName());
|
Scoreboard::get()->setEnterName(scoreboard_panel_, enter_name_->getCurrentName());
|
||||||
Scoreboard::get()->setSelectorPos(scoreboard_panel_, getRecordNamePos());
|
Scoreboard::get()->setCharacterSelected(scoreboard_panel_, enter_name_->getSelectedCharacter());
|
||||||
|
Scoreboard::get()->setCarousel(scoreboard_panel_, enter_name_->getCarousel(7));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -616,7 +617,7 @@ void Player::setPlayingState(State state) {
|
|||||||
case State::SHOWING_NAME: {
|
case State::SHOWING_NAME: {
|
||||||
showing_name_time_accumulator_ = 0.0f; // Inicializar acumulador time-based
|
showing_name_time_accumulator_ = 0.0f; // Inicializar acumulador time-based
|
||||||
setScoreboardMode(Scoreboard::Mode::SHOW_NAME);
|
setScoreboardMode(Scoreboard::Mode::SHOW_NAME);
|
||||||
Scoreboard::get()->setRecordName(scoreboard_panel_, last_enter_name_);
|
Scoreboard::get()->setEnterName(scoreboard_panel_, last_enter_name_);
|
||||||
addScoreToScoreBoard();
|
addScoreToScoreBoard();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -901,15 +902,6 @@ void Player::decNameEntryCounter() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtiene la posición que se está editando del nombre del jugador para la tabla de mejores puntuaciones
|
|
||||||
auto Player::getRecordNamePos() const -> int {
|
|
||||||
if (enter_name_) {
|
|
||||||
return enter_name_->getPosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recoloca los sprites
|
// Recoloca los sprites
|
||||||
void Player::shiftSprite() {
|
void Player::shiftSprite() {
|
||||||
player_sprite_->setPosX(pos_x_);
|
player_sprite_->setPosX(pos_x_);
|
||||||
|
|||||||
@@ -204,10 +204,8 @@ class Player {
|
|||||||
|
|
||||||
// Contadores y timers
|
// Contadores y timers
|
||||||
[[nodiscard]] auto getContinueCounter() const -> int { return continue_counter_; }
|
[[nodiscard]] auto getContinueCounter() const -> int { return continue_counter_; }
|
||||||
[[nodiscard]] auto getRecordNamePos() const -> int; // Obtiene la posición que se está editando del nombre del jugador para la tabla de mejores puntuaciones
|
|
||||||
[[nodiscard]] auto getRecordName() const -> std::string { return enter_name_ ? enter_name_->getFinalName() : "xxx"; }
|
[[nodiscard]] auto getRecordName() const -> std::string { return enter_name_ ? enter_name_->getFinalName() : "xxx"; }
|
||||||
[[nodiscard]] auto getLastEnterName() const -> std::string { return last_enter_name_; }
|
[[nodiscard]] auto getLastEnterName() const -> std::string { return last_enter_name_; }
|
||||||
[[nodiscard]] auto getEnterNamePositionOverflow() const -> bool { return enter_name_ ? enter_name_->getPositionOverflow() : false; }
|
|
||||||
|
|
||||||
// --- Configuración e interfaz externa ---
|
// --- Configuración e interfaz externa ---
|
||||||
void setName(const std::string& name) { name_ = name; }
|
void setName(const std::string& name) { name_ = name; }
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
#include "texture.h" // Para Texture
|
#include "texture.h" // Para Texture
|
||||||
|
|
||||||
// .at(SINGLETON) Hay que definir las variables estáticas, desde el .h sólo la hemos declarado
|
// .at(SINGLETON) Hay que definir las variables estáticas, desde el .h sólo la hemos declarado
|
||||||
Scoreboard *Scoreboard::instance = nullptr;
|
Scoreboard* Scoreboard::instance = nullptr;
|
||||||
|
|
||||||
// .at(SINGLETON) Crearemos el objeto score_board con esta función estática
|
// .at(SINGLETON) Crearemos el objeto score_board con esta función estática
|
||||||
void Scoreboard::init() {
|
void Scoreboard::init() {
|
||||||
@@ -31,7 +31,7 @@ void Scoreboard::destroy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// .at(SINGLETON) Con este método obtenemos el objeto score_board y podemos trabajar con él
|
// .at(SINGLETON) Con este método obtenemos el objeto score_board y podemos trabajar con él
|
||||||
auto Scoreboard::get() -> Scoreboard * {
|
auto Scoreboard::get() -> Scoreboard* {
|
||||||
return Scoreboard::instance;
|
return Scoreboard::instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,12 +40,11 @@ Scoreboard::Scoreboard()
|
|||||||
: renderer_(Screen::get()->getRenderer()),
|
: renderer_(Screen::get()->getRenderer()),
|
||||||
game_power_meter_texture_(Resource::get()->getTexture("game_power_meter.png")),
|
game_power_meter_texture_(Resource::get()->getTexture("game_power_meter.png")),
|
||||||
power_meter_sprite_(std::make_unique<Sprite>(game_power_meter_texture_)),
|
power_meter_sprite_(std::make_unique<Sprite>(game_power_meter_texture_)),
|
||||||
text_(Resource::get()->getText("8bithud")),
|
text_(Resource::get()->getText("8bithud")) {
|
||||||
enter_name_text_(Resource::get()->getText("smb2")) {
|
|
||||||
// Inicializa variables
|
// Inicializa variables
|
||||||
for (size_t i = 0; i < static_cast<size_t>(Id::SIZE); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(Id::SIZE); ++i) {
|
||||||
name_.at(i).clear();
|
name_.at(i).clear();
|
||||||
record_name_.at(i).clear();
|
enter_name_.at(i).clear();
|
||||||
selector_pos_.at(i) = 0;
|
selector_pos_.at(i) = 0;
|
||||||
score_.at(i) = 0;
|
score_.at(i) = 0;
|
||||||
mult_.at(i) = 0;
|
mult_.at(i) = 0;
|
||||||
@@ -74,8 +73,9 @@ Scoreboard::Scoreboard()
|
|||||||
// Rellena la textura de fondo
|
// Rellena la textura de fondo
|
||||||
fillBackgroundTexture();
|
fillBackgroundTexture();
|
||||||
|
|
||||||
// Inicializa el vector de colores para el nombre
|
// Inicializa el ciclo de colores para el nombre
|
||||||
iniNameColors();
|
name_color_cycle_ = Colors::generateMirroredCycle(color_.INVERSE(), ColorCycleStyle::VIBRANT);
|
||||||
|
animated_color_ = name_color_cycle_.at(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Scoreboard::~Scoreboard() {
|
Scoreboard::~Scoreboard() {
|
||||||
@@ -83,7 +83,7 @@ Scoreboard::~Scoreboard() {
|
|||||||
SDL_DestroyTexture(background_);
|
SDL_DestroyTexture(background_);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto *texture : panel_texture_) {
|
for (auto* texture : panel_texture_) {
|
||||||
if (texture != nullptr) {
|
if (texture != nullptr) {
|
||||||
SDL_DestroyTexture(texture);
|
SDL_DestroyTexture(texture);
|
||||||
}
|
}
|
||||||
@@ -107,11 +107,24 @@ void Scoreboard::updateTimeCounter() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Actualiza el índice del color animado del nombre
|
||||||
|
void Scoreboard::updateNameColorIndex() {
|
||||||
|
constexpr Uint64 COLOR_UPDATE_INTERVAL = 100; // 100ms entre cambios de color
|
||||||
|
|
||||||
|
if (SDL_GetTicks() - name_color_last_update_ >= COLOR_UPDATE_INTERVAL) {
|
||||||
|
++name_color_index_;
|
||||||
|
name_color_last_update_ = SDL_GetTicks();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precalcular el color actual del ciclo
|
||||||
|
animated_color_ = name_color_cycle_.at(name_color_index_ % name_color_cycle_.size());
|
||||||
|
}
|
||||||
|
|
||||||
// Actualiza la lógica del marcador
|
// Actualiza la lógica del marcador
|
||||||
void Scoreboard::update() {
|
void Scoreboard::update() {
|
||||||
fillBackgroundTexture();
|
fillBackgroundTexture();
|
||||||
updateTimeCounter();
|
updateTimeCounter();
|
||||||
++loop_counter_;
|
updateNameColorIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pinta el marcador
|
// Pinta el marcador
|
||||||
@@ -129,7 +142,7 @@ void Scoreboard::setColor(Color color) {
|
|||||||
// Aplica los colores
|
// Aplica los colores
|
||||||
power_meter_sprite_->getTexture()->setColor(text_color2_);
|
power_meter_sprite_->getTexture()->setColor(text_color2_);
|
||||||
fillBackgroundTexture();
|
fillBackgroundTexture();
|
||||||
iniNameColors();
|
name_color_cycle_ = Colors::generateMirroredCycle(color_.INVERSE(), ColorCycleStyle::VIBRANT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Establece el valor de la variable
|
// Establece el valor de la variable
|
||||||
@@ -145,7 +158,7 @@ void Scoreboard::setPos(SDL_FRect rect) {
|
|||||||
// Rellena los diferentes paneles del marcador
|
// Rellena los diferentes paneles del marcador
|
||||||
void Scoreboard::fillPanelTextures() {
|
void Scoreboard::fillPanelTextures() {
|
||||||
// Guarda a donde apunta actualmente el renderizador
|
// Guarda a donde apunta actualmente el renderizador
|
||||||
auto *temp = SDL_GetRenderTarget(renderer_);
|
auto* temp = SDL_GetRenderTarget(renderer_);
|
||||||
|
|
||||||
// Genera el contenido de cada panel_
|
// Genera el contenido de cada panel_
|
||||||
for (size_t i = 0; i < static_cast<int>(Id::SIZE); ++i) {
|
for (size_t i = 0; i < static_cast<int>(Id::SIZE); ++i) {
|
||||||
@@ -267,6 +280,7 @@ void Scoreboard::renderContinueMode(size_t panel_index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Scoreboard::renderEnterNameMode(size_t panel_index) {
|
void Scoreboard::renderEnterNameMode(size_t panel_index) {
|
||||||
|
/*
|
||||||
// SCORE
|
// SCORE
|
||||||
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
|
||||||
text_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
||||||
@@ -275,29 +289,19 @@ void Scoreboard::renderEnterNameMode(size_t panel_index) {
|
|||||||
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
|
||||||
|
|
||||||
renderNameInputField(panel_index);
|
renderNameInputField(panel_index);
|
||||||
}
|
*/
|
||||||
|
|
||||||
void Scoreboard::renderNameInputField(size_t panel_index) {
|
// SCORE
|
||||||
SDL_FRect rect = {
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
||||||
.x = enter_name_pos_.x,
|
|
||||||
.y = enter_name_pos_.y,
|
|
||||||
.w = static_cast<float>(enter_name_text_->getCharacterSize() - 2),
|
|
||||||
.h = static_cast<float>(enter_name_text_->getCharacterSize())};
|
|
||||||
|
|
||||||
// Recorre todos los slots de letras del nombre
|
// ENTER NAME
|
||||||
for (size_t j = 0; j < NAME_SIZE; ++j) {
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
|
||||||
// Dibuja la linea. Si coincide con el selector solo se dibuja 2 de cada 4 veces
|
|
||||||
if (j != selector_pos_.at(panel_index) || time_counter_ % 4 >= 2) {
|
|
||||||
SDL_SetRenderDrawColor(renderer_, text_color1_.r, text_color1_.g, text_color1_.b, 255);
|
|
||||||
SDL_RenderLine(renderer_, rect.x, rect.y + rect.h, rect.x + rect.w, rect.y + rect.h);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dibuja la letra
|
// NAME
|
||||||
if (j < record_name_.at(panel_index).size()) {
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, enter_name_.at(panel_index), 1, text_color2_);
|
||||||
enter_name_text_->writeColored(rect.x, rect.y, record_name_.at(panel_index).substr(j, 1), text_color2_);
|
|
||||||
}
|
// CARRUSEL
|
||||||
rect.x += enter_name_text_->getCharacterSize();
|
renderCarousel(panel_index, slot4_4_.x, slot4_4_.y);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scoreboard::renderShowNameMode(size_t panel_index) {
|
void Scoreboard::renderShowNameMode(size_t panel_index) {
|
||||||
@@ -309,7 +313,7 @@ void Scoreboard::renderShowNameMode(size_t panel_index) {
|
|||||||
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
|
||||||
|
|
||||||
// NOMBRE INTRODUCIDO
|
// NOMBRE INTRODUCIDO
|
||||||
enter_name_text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, record_name_.at(panel_index), 1, Colors::getColorLikeKnightRider(name_colors_, loop_counter_ / 5));
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, enter_name_.at(panel_index), 1, animated_color_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scoreboard::renderGameCompletedMode(size_t panel_index) {
|
void Scoreboard::renderGameCompletedMode(size_t panel_index) {
|
||||||
@@ -329,7 +333,7 @@ void Scoreboard::fillBackgroundTexture() {
|
|||||||
fillPanelTextures();
|
fillPanelTextures();
|
||||||
|
|
||||||
// Cambia el destino del renderizador
|
// Cambia el destino del renderizador
|
||||||
SDL_Texture *temp = SDL_GetRenderTarget(renderer_);
|
SDL_Texture* temp = SDL_GetRenderTarget(renderer_);
|
||||||
SDL_SetRenderTarget(renderer_, background_);
|
SDL_SetRenderTarget(renderer_, background_);
|
||||||
|
|
||||||
// Dibuja el fondo del marcador
|
// Dibuja el fondo del marcador
|
||||||
@@ -379,7 +383,7 @@ void Scoreboard::recalculateAnchors() {
|
|||||||
slot4_4_ = {.x = COL, .y = ROW4};
|
slot4_4_ = {.x = COL, .y = ROW4};
|
||||||
|
|
||||||
// Primer cuadrado para poner el nombre de record
|
// Primer cuadrado para poner el nombre de record
|
||||||
const int ENTER_NAME_LENGTH = enter_name_text_->length(std::string(NAME_SIZE, 'A'));
|
const int ENTER_NAME_LENGTH = text_->length(std::string(EnterName::MAX_NAME_SIZE, 'A'));
|
||||||
enter_name_pos_.x = COL - (ENTER_NAME_LENGTH / 2);
|
enter_name_pos_.x = COL - (ENTER_NAME_LENGTH / 2);
|
||||||
enter_name_pos_.y = ROW4;
|
enter_name_pos_.y = ROW4;
|
||||||
|
|
||||||
@@ -405,7 +409,7 @@ void Scoreboard::createBackgroundTexture() {
|
|||||||
// Crea las texturas de los paneles
|
// Crea las texturas de los paneles
|
||||||
void Scoreboard::createPanelTextures() {
|
void Scoreboard::createPanelTextures() {
|
||||||
// Elimina las texturas en caso de existir
|
// Elimina las texturas en caso de existir
|
||||||
for (auto *texture : panel_texture_) {
|
for (auto* texture : panel_texture_) {
|
||||||
if (texture != nullptr) {
|
if (texture != nullptr) {
|
||||||
SDL_DestroyTexture(texture);
|
SDL_DestroyTexture(texture);
|
||||||
}
|
}
|
||||||
@@ -413,8 +417,8 @@ void Scoreboard::createPanelTextures() {
|
|||||||
panel_texture_.clear();
|
panel_texture_.clear();
|
||||||
|
|
||||||
// Crea las texturas para cada panel_
|
// Crea las texturas para cada panel_
|
||||||
for (auto &i : panel_) {
|
for (auto& i : panel_) {
|
||||||
SDL_Texture *tex = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, i.pos.w, i.pos.h);
|
SDL_Texture* tex = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, i.pos.w, i.pos.h);
|
||||||
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
|
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
|
||||||
panel_texture_.push_back(tex);
|
panel_texture_.push_back(tex);
|
||||||
}
|
}
|
||||||
@@ -428,13 +432,55 @@ void Scoreboard::renderSeparator() {
|
|||||||
SDL_RenderLine(renderer_, 0, 0, rect_.w, 0);
|
SDL_RenderLine(renderer_, 0, 0, rect_.w, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inicializa el vector de colores para el nombre
|
// Pinta el carrusel de caracteres con efecto de color LERP
|
||||||
void Scoreboard::iniNameColors() {
|
void Scoreboard::renderCarousel(size_t panel_index, int center_x, int y) {
|
||||||
Color color = color_.INVERSE();
|
const std::string& carousel = carousel_.at(panel_index);
|
||||||
|
if (carousel.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
name_colors_.clear();
|
// Espacio extra entre letras (además del ancho natural de cada letra)
|
||||||
name_colors_.emplace_back(color.LIGHTEN(50));
|
constexpr int EXTRA_SPACING = 2;
|
||||||
name_colors_.emplace_back(color.LIGHTEN(25));
|
|
||||||
name_colors_.emplace_back(color);
|
// Índice de la letra central
|
||||||
name_colors_.emplace_back(color.DARKEN(25));
|
const int CENTER_INDEX = carousel.size() / 2;
|
||||||
|
|
||||||
|
// Calcular el ancho acumulado antes de la letra central
|
||||||
|
int width_before_center = 0;
|
||||||
|
for (int i = 0; i < CENTER_INDEX; ++i) {
|
||||||
|
std::string ch(1, carousel[i]);
|
||||||
|
width_before_center += text_->length(ch, 1) + EXTRA_SPACING;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calcular posición inicial para que la letra central esté centrada en center_x
|
||||||
|
std::string center_char(1, carousel[CENTER_INDEX]);
|
||||||
|
const int CENTER_CHAR_WIDTH = text_->length(center_char, 1);
|
||||||
|
const int CAROUSEL_START_X = center_x - width_before_center - (CENTER_CHAR_WIDTH / 2);
|
||||||
|
|
||||||
|
// Pintar cada letra individualmente
|
||||||
|
int current_x = CAROUSEL_START_X;
|
||||||
|
for (size_t i = 0; i < carousel.size(); ++i) {
|
||||||
|
std::string single_char(1, carousel[i]);
|
||||||
|
|
||||||
|
Color letter_color;
|
||||||
|
|
||||||
|
if (static_cast<int>(i) == CENTER_INDEX) {
|
||||||
|
// Letra central: usa animated_color_ sin modificar
|
||||||
|
letter_color = animated_color_;
|
||||||
|
} else {
|
||||||
|
// Letras laterales: LERP desde text_color1_ hacia color_, pero sin llegar al 100%
|
||||||
|
const int DISTANCE = std::abs(static_cast<int>(i) - CENTER_INDEX);
|
||||||
|
const float MAX_DISTANCE = static_cast<float>(CENTER_INDEX);
|
||||||
|
// Limitar el fade al 70% para que las letras sigan siendo visibles
|
||||||
|
const float LERP_FACTOR = (MAX_DISTANCE > 0) ? ((DISTANCE / MAX_DISTANCE) * 0.7f) : 0.0f;
|
||||||
|
|
||||||
|
letter_color = text_color1_.LERP(color_, LERP_FACTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pintar la letra
|
||||||
|
text_->writeDX(Text::COLOR, current_x, y, single_char, 1, letter_color);
|
||||||
|
|
||||||
|
// Avanzar posición X: ancho del caracter + espacio extra
|
||||||
|
current_x += text_->length(single_char, 1) + EXTRA_SPACING;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -45,9 +45,9 @@ class Scoreboard {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// --- Métodos de singleton ---
|
// --- Métodos de singleton ---
|
||||||
static void init(); // Crea el objeto Scoreboard
|
static void init(); // Crea el objeto Scoreboard
|
||||||
static void destroy(); // Libera el objeto Scoreboard
|
static void destroy(); // Libera el objeto Scoreboard
|
||||||
static auto get() -> Scoreboard *; // Obtiene el puntero al objeto Scoreboard
|
static auto get() -> Scoreboard*; // Obtiene el puntero al objeto Scoreboard
|
||||||
|
|
||||||
// --- Métodos principales ---
|
// --- Métodos principales ---
|
||||||
void update(); // Actualiza la lógica del marcador
|
void update(); // Actualiza la lógica del marcador
|
||||||
@@ -58,44 +58,49 @@ class Scoreboard {
|
|||||||
void setPos(SDL_FRect rect); // Establece la posición y tamaño del marcador
|
void setPos(SDL_FRect rect); // Establece la posición y tamaño del marcador
|
||||||
void setContinue(Id id, int continue_counter) { continue_counter_.at(static_cast<size_t>(id)) = continue_counter; }
|
void setContinue(Id id, int continue_counter) { continue_counter_.at(static_cast<size_t>(id)) = continue_counter; }
|
||||||
void setHiScore(int hi_score) { hi_score_ = hi_score; }
|
void setHiScore(int hi_score) { hi_score_ = hi_score; }
|
||||||
void setHiScoreName(const std::string &name) { hi_score_name_ = name; }
|
void setHiScoreName(const std::string& name) { hi_score_name_ = name; }
|
||||||
void setMode(Id id, Mode mode) { panel_.at(static_cast<size_t>(id)).mode = mode; }
|
void setMode(Id id, Mode mode) { panel_.at(static_cast<size_t>(id)).mode = mode; }
|
||||||
void setMult(Id id, float mult) { mult_.at(static_cast<size_t>(id)) = mult; }
|
void setMult(Id id, float mult) { mult_.at(static_cast<size_t>(id)) = mult; }
|
||||||
void setName(Id id, const std::string &name) { name_.at(static_cast<size_t>(id)) = name; }
|
void setName(Id id, const std::string& name) { name_.at(static_cast<size_t>(id)) = name; }
|
||||||
void setPower(float power) { power_ = power; }
|
void setPower(float power) { power_ = power; }
|
||||||
void setRecordName(Id id, const std::string &record_name) { record_name_.at(static_cast<size_t>(id)) = record_name; }
|
void setEnterName(Id id, const std::string& enter_name) { enter_name_.at(static_cast<size_t>(id)) = enter_name; }
|
||||||
|
void setCharacterSelected(Id id, const std::string& character_selected) { character_selected_.at(static_cast<size_t>(id)) = character_selected; }
|
||||||
|
void setCarousel(Id id, const std::string& carousel) { carousel_.at(static_cast<size_t>(id)) = carousel; }
|
||||||
void setScore(Id id, int score) { score_.at(static_cast<size_t>(id)) = score; }
|
void setScore(Id id, int score) { score_.at(static_cast<size_t>(id)) = score; }
|
||||||
void setSelectorPos(Id id, int pos) { selector_pos_.at(static_cast<size_t>(id)) = pos; }
|
void setSelectorPos(Id id, int pos) { selector_pos_.at(static_cast<size_t>(id)) = pos; }
|
||||||
void setStage(int stage) { stage_ = stage; }
|
void setStage(int stage) { stage_ = stage; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// --- Objetos y punteros ---
|
// --- Objetos y punteros ---
|
||||||
SDL_Renderer *renderer_; // El renderizador de la ventana
|
SDL_Renderer* renderer_; // El renderizador de la ventana
|
||||||
std::shared_ptr<Texture> game_power_meter_texture_; // Textura con el marcador de poder de la fase
|
std::shared_ptr<Texture> game_power_meter_texture_; // Textura con el marcador de poder de la fase
|
||||||
std::unique_ptr<Sprite> power_meter_sprite_; // Sprite para el medidor de poder de la fase
|
std::unique_ptr<Sprite> power_meter_sprite_; // Sprite para el medidor de poder de la fase
|
||||||
std::shared_ptr<Text> text_; // Fuente para el marcador del juego
|
std::shared_ptr<Text> text_; // Fuente para el marcador del juego
|
||||||
std::shared_ptr<Text> enter_name_text_; // Fuente para la introducción de nombre del jugador
|
SDL_Texture* background_ = nullptr; // Textura para dibujar el marcador
|
||||||
SDL_Texture *background_ = nullptr; // Textura para dibujar el marcador
|
std::vector<SDL_Texture*> panel_texture_; // Texturas para dibujar cada panel
|
||||||
std::vector<SDL_Texture *> panel_texture_; // Texturas para dibujar cada panel
|
|
||||||
|
|
||||||
// --- Variables de estado ---
|
// --- Variables de estado ---
|
||||||
std::array<std::string, static_cast<int>(Id::SIZE)> name_ = {}; // Nombre de cada jugador
|
std::array<std::string, static_cast<int>(Id::SIZE)> name_ = {}; // Nombre de cada jugador
|
||||||
std::array<std::string, static_cast<int>(Id::SIZE)> record_name_ = {}; // Nombre introducido para la tabla de records
|
std::array<std::string, static_cast<int>(Id::SIZE)> enter_name_ = {}; // Nombre introducido para la tabla de records
|
||||||
std::array<Panel, static_cast<int>(Id::SIZE)> panel_ = {}; // Lista con todos los paneles del marcador
|
std::array<std::string, static_cast<int>(Id::SIZE)> character_selected_ = {}; // Caracter seleccionado
|
||||||
std::vector<Color> name_colors_; // Colores para destacar el nombre una vez introducido
|
std::array<std::string, static_cast<int>(Id::SIZE)> carousel_ = {}; // Caracter seleccionado
|
||||||
std::string hi_score_name_; // Nombre del jugador con la máxima puntuación
|
std::array<Panel, static_cast<int>(Id::SIZE)> panel_ = {}; // Lista con todos los paneles del marcador
|
||||||
SDL_FRect rect_ = {0, 0, 320, 40}; // Posición y dimensiones del marcador
|
Colors::Cycle name_color_cycle_; // Ciclo de colores para destacar el nombre una vez introducido
|
||||||
Color color_; // Color del marcador
|
Color animated_color_; // Color actual animado (ciclo automático cada 100ms)
|
||||||
std::array<size_t, static_cast<int>(Id::SIZE)> selector_pos_ = {}; // Posición del selector de letra para introducir el nombre
|
std::string hi_score_name_; // Nombre del jugador con la máxima puntuación
|
||||||
std::array<int, static_cast<int>(Id::SIZE)> score_ = {}; // Puntuación de los jugadores
|
SDL_FRect rect_ = {0, 0, 320, 40}; // Posición y dimensiones del marcador
|
||||||
std::array<int, static_cast<int>(Id::SIZE)> continue_counter_ = {}; // Tiempo para continuar de los jugadores
|
Color color_; // Color del marcador
|
||||||
std::array<float, static_cast<int>(Id::SIZE)> mult_ = {}; // Multiplicador de los jugadores
|
std::array<size_t, static_cast<int>(Id::SIZE)> selector_pos_ = {}; // Posición del selector de letra para introducir el nombre
|
||||||
Uint64 ticks_ = SDL_GetTicks(); // Variable donde almacenar el valor de SDL_GetTicks()
|
std::array<int, static_cast<int>(Id::SIZE)> score_ = {}; // Puntuación de los jugadores
|
||||||
int stage_ = 1; // Número de fase actual
|
std::array<int, static_cast<int>(Id::SIZE)> continue_counter_ = {}; // Tiempo para continuar de los jugadores
|
||||||
int hi_score_ = 0; // Máxima puntuación
|
std::array<float, static_cast<int>(Id::SIZE)> mult_ = {}; // Multiplicador de los jugadores
|
||||||
int time_counter_ = 0; // Contador de segundos
|
Uint64 ticks_ = SDL_GetTicks(); // Variable donde almacenar el valor de SDL_GetTicks()
|
||||||
int loop_counter_ = 0; // Contador de bucle
|
int stage_ = 1; // Número de fase actual
|
||||||
float power_ = 0; // Poder actual de la fase
|
int hi_score_ = 0; // Máxima puntuación
|
||||||
|
int time_counter_ = 0; // Contador de segundos
|
||||||
|
Uint32 name_color_index_ = 0; // Índice actual del color en el ciclo de animación del nombre
|
||||||
|
Uint64 name_color_last_update_ = 0; // Último tick de actualización del color del nombre
|
||||||
|
float power_ = 0; // Poder actual de la fase
|
||||||
|
|
||||||
// --- Variables de aspecto ---
|
// --- Variables de aspecto ---
|
||||||
Color text_color1_, text_color2_; // Colores para los marcadores del texto;
|
Color text_color1_, text_color2_; // Colores para los marcadores del texto;
|
||||||
@@ -112,8 +117,8 @@ class Scoreboard {
|
|||||||
void fillPanelTextures(); // Rellena los diferentes paneles del marcador
|
void fillPanelTextures(); // Rellena los diferentes paneles del marcador
|
||||||
void fillBackgroundTexture(); // Rellena la textura de fondo
|
void fillBackgroundTexture(); // Rellena la textura de fondo
|
||||||
void updateTimeCounter(); // Actualiza el contador
|
void updateTimeCounter(); // Actualiza el contador
|
||||||
|
void updateNameColorIndex(); // Actualiza el índice del color animado del nombre
|
||||||
void renderSeparator(); // Dibuja la línea que separa la zona de juego del marcador
|
void renderSeparator(); // Dibuja la línea que separa la zona de juego del marcador
|
||||||
void iniNameColors(); // Inicializa el vector de colores para el nombre
|
|
||||||
void renderPanelContent(size_t panel_index);
|
void renderPanelContent(size_t panel_index);
|
||||||
void renderScoreMode(size_t panel_index);
|
void renderScoreMode(size_t panel_index);
|
||||||
void renderDemoMode();
|
void renderDemoMode();
|
||||||
@@ -125,11 +130,12 @@ class Scoreboard {
|
|||||||
void renderNameInputField(size_t panel_index);
|
void renderNameInputField(size_t panel_index);
|
||||||
void renderShowNameMode(size_t panel_index);
|
void renderShowNameMode(size_t panel_index);
|
||||||
void renderGameCompletedMode(size_t panel_index);
|
void renderGameCompletedMode(size_t panel_index);
|
||||||
|
void renderCarousel(size_t panel_index, int center_x, int y); // Pinta el carrusel de caracteres con colores LERP
|
||||||
|
|
||||||
// --- Constructores y destructor privados (singleton) ---
|
// --- Constructores y destructor privados (singleton) ---
|
||||||
Scoreboard(); // Constructor privado
|
Scoreboard(); // Constructor privado
|
||||||
~Scoreboard(); // Destructor privado
|
~Scoreboard(); // Destructor privado
|
||||||
|
|
||||||
// --- Instancia singleton ---
|
// --- Instancia singleton ---
|
||||||
static Scoreboard *instance; // Instancia única de Scoreboard
|
static Scoreboard* instance; // Instancia única de Scoreboard
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "balloon.h" // Para Balloon
|
#include "balloon.h" // Para Balloon
|
||||||
#include "balloon_manager.h" // Para BalloonManager
|
#include "balloon_manager.h" // Para BalloonManager
|
||||||
#include "bullet.h" // Para Bullet, Bullet::Type, BulletMoveStatus
|
#include "bullet.h" // Para Bullet, Bullet::Type, BulletMoveStatus
|
||||||
|
#include "bullet_manager.h" // Para BulletManager
|
||||||
#include "color.h" // Para Color, Colors::FLASH
|
#include "color.h" // Para Color, Colors::FLASH
|
||||||
#include "difficulty.h" // Para Code
|
#include "difficulty.h" // Para Code
|
||||||
#include "fade.h" // Para Fade, FadeType, FadeMode
|
#include "fade.h" // Para Fade, FadeType, FadeMode
|
||||||
@@ -57,6 +58,7 @@ Game::Game(Player::Id player_id, int current_stage, bool demo_enabled)
|
|||||||
pause_manager_(std::make_unique<PauseManager>([this](bool is_paused) { onPauseStateChanged(is_paused); })),
|
pause_manager_(std::make_unique<PauseManager>([this](bool is_paused) { onPauseStateChanged(is_paused); })),
|
||||||
stage_manager_(std::make_unique<StageManager>()),
|
stage_manager_(std::make_unique<StageManager>()),
|
||||||
balloon_manager_(std::make_unique<BalloonManager>(stage_manager_.get())),
|
balloon_manager_(std::make_unique<BalloonManager>(stage_manager_.get())),
|
||||||
|
bullet_manager_(std::make_unique<BulletManager>()),
|
||||||
background_(std::make_unique<Background>(stage_manager_->getPowerNeededToReachStage(stage_manager_->getTotalStages() - 1))),
|
background_(std::make_unique<Background>(stage_manager_->getPowerNeededToReachStage(stage_manager_->getTotalStages() - 1))),
|
||||||
fade_in_(std::make_unique<Fade>()),
|
fade_in_(std::make_unique<Fade>()),
|
||||||
fade_out_(std::make_unique<Fade>()),
|
fade_out_(std::make_unique<Fade>()),
|
||||||
@@ -112,6 +114,19 @@ Game::Game(Player::Id player_id, int current_stage, bool demo_enabled)
|
|||||||
pause_manager_->setServiceMenuPause(is_active);
|
pause_manager_->setServiceMenuPause(is_active);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Configura callbacks del BulletManager
|
||||||
|
bullet_manager_->setTabeCollisionCallback([this](const std::shared_ptr<Bullet>& bullet) {
|
||||||
|
return checkBulletTabeCollision(bullet);
|
||||||
|
});
|
||||||
|
|
||||||
|
bullet_manager_->setBalloonCollisionCallback([this](const std::shared_ptr<Bullet>& bullet) {
|
||||||
|
return checkBulletBalloonCollision(bullet);
|
||||||
|
});
|
||||||
|
|
||||||
|
bullet_manager_->setOutOfBoundsCallback([this](const std::shared_ptr<Bullet>& bullet) {
|
||||||
|
getPlayer(static_cast<Player::Id>(bullet->getOwner()))->decScoreMultiplier();
|
||||||
|
});
|
||||||
#ifdef RECORDING
|
#ifdef RECORDING
|
||||||
setState(State::PLAYING);
|
setState(State::PLAYING);
|
||||||
#endif
|
#endif
|
||||||
@@ -333,12 +348,12 @@ void Game::updateGameStateGameOver(float deltaTime) {
|
|||||||
updateBackground(deltaTime);
|
updateBackground(deltaTime);
|
||||||
balloon_manager_->update(deltaTime);
|
balloon_manager_->update(deltaTime);
|
||||||
tabe_->update(deltaTime);
|
tabe_->update(deltaTime);
|
||||||
updateBullets(deltaTime);
|
bullet_manager_->update(deltaTime);
|
||||||
updateItems(deltaTime);
|
updateItems(deltaTime);
|
||||||
updateSmartSprites(deltaTime);
|
updateSmartSprites(deltaTime);
|
||||||
updatePathSprites(deltaTime);
|
updatePathSprites(deltaTime);
|
||||||
updateTimeStopped(deltaTime);
|
updateTimeStopped(deltaTime);
|
||||||
checkBulletCollision();
|
bullet_manager_->checkCollisions();
|
||||||
cleanVectors();
|
cleanVectors();
|
||||||
|
|
||||||
if (game_over_timer_ < GAME_OVER_DURATION_S) {
|
if (game_over_timer_ < GAME_OVER_DURATION_S) {
|
||||||
@@ -369,7 +384,7 @@ void Game::updateGameStateCompleted(float deltaTime) {
|
|||||||
updateBackground(deltaTime);
|
updateBackground(deltaTime);
|
||||||
balloon_manager_->update(deltaTime);
|
balloon_manager_->update(deltaTime);
|
||||||
tabe_->update(deltaTime);
|
tabe_->update(deltaTime);
|
||||||
updateBullets(deltaTime);
|
bullet_manager_->update(deltaTime);
|
||||||
updateItems(deltaTime);
|
updateItems(deltaTime);
|
||||||
updateSmartSprites(deltaTime);
|
updateSmartSprites(deltaTime);
|
||||||
updatePathSprites(deltaTime);
|
updatePathSprites(deltaTime);
|
||||||
@@ -498,22 +513,6 @@ void Game::checkPlayerItemCollision(std::shared_ptr<Player>& player) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comprueba y procesa la colisión de las balas
|
|
||||||
void Game::checkBulletCollision() {
|
|
||||||
for (auto& bullet : bullets_) {
|
|
||||||
if (!bullet->isEnabled()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkBulletTabeCollision(bullet)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkBulletBalloonCollision(bullet)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maneja la colisión entre bala y Tabe
|
// Maneja la colisión entre bala y Tabe
|
||||||
auto Game::checkBulletTabeCollision(const std::shared_ptr<Bullet>& bullet) -> bool {
|
auto Game::checkBulletTabeCollision(const std::shared_ptr<Bullet>& bullet) -> bool {
|
||||||
@@ -601,37 +600,6 @@ void Game::handleBalloonDestruction(std::shared_ptr<Balloon> balloon, const std:
|
|||||||
updateHiScore();
|
updateHiScore();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mueve las balas activas
|
|
||||||
void Game::updateBullets(float deltaTime) {
|
|
||||||
for (auto& bullet : bullets_) {
|
|
||||||
if (bullet->update(deltaTime) == Bullet::MoveStatus::OUT) {
|
|
||||||
getPlayer(static_cast<Player::Id>(bullet->getOwner()))->decScoreMultiplier();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pinta las balas activas
|
|
||||||
void Game::renderBullets() {
|
|
||||||
for (auto& bullet : bullets_) {
|
|
||||||
bullet->render();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Crea un objeto bala
|
|
||||||
void Game::createBullet(int x, int y, Bullet::Type type, Bullet::Color color, int owner) {
|
|
||||||
bullets_.emplace_back(std::make_shared<Bullet>(x, y, type, color, owner));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vacia el vector de balas
|
|
||||||
void Game::freeBullets() {
|
|
||||||
if (!bullets_.empty()) {
|
|
||||||
for (int i = bullets_.size() - 1; i >= 0; --i) {
|
|
||||||
if (!bullets_[i]->isEnabled()) {
|
|
||||||
bullets_.erase(bullets_.begin() + i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actualiza los items
|
// Actualiza los items
|
||||||
void Game::updateItems(float deltaTime) {
|
void Game::updateItems(float deltaTime) {
|
||||||
@@ -982,7 +950,7 @@ void Game::fillCanvas() {
|
|||||||
renderSmartSprites(); // El cafe que sale cuando te golpean
|
renderSmartSprites(); // El cafe que sale cuando te golpean
|
||||||
renderItems();
|
renderItems();
|
||||||
tabe_->render();
|
tabe_->render();
|
||||||
renderBullets();
|
bullet_manager_->render();
|
||||||
renderPlayers();
|
renderPlayers();
|
||||||
|
|
||||||
renderPathSprites();
|
renderPathSprites();
|
||||||
@@ -1343,7 +1311,7 @@ void Game::handleFireInput(const std::shared_ptr<Player>& player, Bullet::Type t
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
createBullet(bullet.x, bullet.y, type, player->getNextBulletColor(), static_cast<int>(player->getId()));
|
bullet_manager_->createBullet(bullet.x, bullet.y, type, player->getNextBulletColor(), static_cast<int>(player->getId()));
|
||||||
playSound(player->getBulletSoundFile());
|
playSound(player->getBulletSoundFile());
|
||||||
|
|
||||||
// Establece un tiempo de espera para el próximo disparo.
|
// Establece un tiempo de espera para el próximo disparo.
|
||||||
@@ -1467,14 +1435,7 @@ void Game::handleNameInput(const std::shared_ptr<Player>& player) {
|
|||||||
player->passShowingName();
|
player->passShowingName();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (player->getEnterNamePositionOverflow()) {
|
player->setInput(Input::Action::FIRE_LEFT);
|
||||||
player->setInput(Input::Action::START);
|
|
||||||
player->setPlayingState(Player::State::SHOWING_NAME);
|
|
||||||
playSound("service_menu_select.wav");
|
|
||||||
updateHiScoreName();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
player->setInput(Input::Action::RIGHT);
|
|
||||||
playSound("service_menu_select.wav");
|
playSound("service_menu_select.wav");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1485,19 +1446,19 @@ void Game::handleNameInput(const std::shared_ptr<Player>& player) {
|
|||||||
player->passShowingName();
|
player->passShowingName();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
player->setInput(Input::Action::LEFT);
|
player->setInput(Input::Action::FIRE_CENTER);
|
||||||
playSound("service_menu_back.wav");
|
playSound("service_menu_back.wav");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input_->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
if (input_->checkAction(Input::Action::LEFT, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
||||||
player->setInput(Input::Action::UP);
|
player->setInput(Input::Action::LEFT);
|
||||||
playSound("service_menu_move.wav");
|
playSound("service_menu_move.wav");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input_->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
if (input_->checkAction(Input::Action::RIGHT, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
||||||
player->setInput(Input::Action::DOWN);
|
player->setInput(Input::Action::RIGHT);
|
||||||
playSound("service_menu_move.wav");
|
playSound("service_menu_move.wav");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1833,14 +1794,14 @@ void Game::updateGameStatePlaying(float deltaTime) {
|
|||||||
updateBackground(deltaTime);
|
updateBackground(deltaTime);
|
||||||
balloon_manager_->update(deltaTime);
|
balloon_manager_->update(deltaTime);
|
||||||
tabe_->update(deltaTime);
|
tabe_->update(deltaTime);
|
||||||
updateBullets(deltaTime);
|
bullet_manager_->update(deltaTime);
|
||||||
updateItems(deltaTime);
|
updateItems(deltaTime);
|
||||||
updateStage();
|
updateStage();
|
||||||
updateSmartSprites(deltaTime);
|
updateSmartSprites(deltaTime);
|
||||||
updatePathSprites(deltaTime);
|
updatePathSprites(deltaTime);
|
||||||
updateTimeStopped(deltaTime);
|
updateTimeStopped(deltaTime);
|
||||||
updateHelper();
|
updateHelper();
|
||||||
checkBulletCollision();
|
bullet_manager_->checkCollisions();
|
||||||
updateMenace();
|
updateMenace();
|
||||||
checkAndUpdateBalloonSpeed();
|
checkAndUpdateBalloonSpeed();
|
||||||
checkState();
|
checkState();
|
||||||
@@ -1849,7 +1810,7 @@ void Game::updateGameStatePlaying(float deltaTime) {
|
|||||||
|
|
||||||
// Vacía los vectores de elementos deshabilitados
|
// Vacía los vectores de elementos deshabilitados
|
||||||
void Game::cleanVectors() {
|
void Game::cleanVectors() {
|
||||||
freeBullets();
|
bullet_manager_->freeBullets();
|
||||||
balloon_manager_->freeBalloons();
|
balloon_manager_->freeBalloons();
|
||||||
freeItems();
|
freeItems();
|
||||||
freeSmartSprites();
|
freeSmartSprites();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "bullet.h" // Para Bullet
|
#include "bullet.h" // Para Bullet
|
||||||
|
#include "bullet_manager.h" // Para BulletManager
|
||||||
#include "hit.h" // Para Hit
|
#include "hit.h" // Para Hit
|
||||||
#include "item.h" // Para Item, ItemType
|
#include "item.h" // Para Item, ItemType
|
||||||
#include "manage_hiscore_table.h" // Para HiScoreEntry
|
#include "manage_hiscore_table.h" // Para HiScoreEntry
|
||||||
@@ -21,7 +22,7 @@
|
|||||||
class Background;
|
class Background;
|
||||||
class Balloon;
|
class Balloon;
|
||||||
class BalloonManager;
|
class BalloonManager;
|
||||||
class Bullet;
|
class BulletManager;
|
||||||
class Fade;
|
class Fade;
|
||||||
class Input;
|
class Input;
|
||||||
class PauseManager;
|
class PauseManager;
|
||||||
@@ -121,7 +122,6 @@ class Game {
|
|||||||
SDL_Texture* canvas_; // Textura para dibujar la zona de juego
|
SDL_Texture* canvas_; // Textura para dibujar la zona de juego
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Player>> players_; // Vector con los jugadores
|
std::vector<std::shared_ptr<Player>> players_; // Vector con los jugadores
|
||||||
std::vector<std::shared_ptr<Bullet>> bullets_; // Vector con las balas
|
|
||||||
std::vector<std::unique_ptr<Item>> items_; // Vector con los items
|
std::vector<std::unique_ptr<Item>> items_; // Vector con los items
|
||||||
std::vector<std::unique_ptr<SmartSprite>> smart_sprites_; // Vector con los smartsprites
|
std::vector<std::unique_ptr<SmartSprite>> smart_sprites_; // Vector con los smartsprites
|
||||||
std::vector<std::unique_ptr<PathSprite>> path_sprites_; // Vector con los pathsprites
|
std::vector<std::unique_ptr<PathSprite>> path_sprites_; // Vector con los pathsprites
|
||||||
@@ -137,6 +137,7 @@ class Game {
|
|||||||
std::unique_ptr<PauseManager> pause_manager_; // Objeto para gestionar la pausa
|
std::unique_ptr<PauseManager> pause_manager_; // Objeto para gestionar la pausa
|
||||||
std::unique_ptr<StageManager> stage_manager_; // Objeto para gestionar las fases
|
std::unique_ptr<StageManager> stage_manager_; // Objeto para gestionar las fases
|
||||||
std::unique_ptr<BalloonManager> balloon_manager_; // Objeto para gestionar los globos
|
std::unique_ptr<BalloonManager> balloon_manager_; // Objeto para gestionar los globos
|
||||||
|
std::unique_ptr<BulletManager> bullet_manager_; // Objeto para gestionar las balas
|
||||||
std::unique_ptr<Background> background_; // Objeto para dibujar el fondo del juego
|
std::unique_ptr<Background> background_; // Objeto para dibujar el fondo del juego
|
||||||
std::unique_ptr<Fade> fade_in_; // Objeto para renderizar fades
|
std::unique_ptr<Fade> fade_in_; // Objeto para renderizar fades
|
||||||
std::unique_ptr<Fade> fade_out_; // Objeto para renderizar fades
|
std::unique_ptr<Fade> fade_out_; // Objeto para renderizar fades
|
||||||
@@ -260,11 +261,7 @@ class Game {
|
|||||||
void demoHandlePlayerInput(const std::shared_ptr<Player>& player, int index); // Procesa entrada de jugador en demo
|
void demoHandlePlayerInput(const std::shared_ptr<Player>& player, int index); // Procesa entrada de jugador en demo
|
||||||
|
|
||||||
// --- Sistema de balas y proyectiles ---
|
// --- Sistema de balas y proyectiles ---
|
||||||
void updateBullets(float deltaTime); // Actualiza posición y estado de todas las balas (time-based)
|
void checkBulletCollision(); // Verifica colisiones de todas las balas (delegado a BulletManager)
|
||||||
void renderBullets(); // Renderiza todas las balas activas
|
|
||||||
void createBullet(int x, int y, Bullet::Type kind, Bullet::Color color, int owner); // Crea una nueva bala
|
|
||||||
void checkBulletCollision(); // Verifica colisiones de todas las balas
|
|
||||||
void freeBullets(); // Libera memoria del vector de balas
|
|
||||||
|
|
||||||
// --- Colisiones específicas de balas ---
|
// --- Colisiones específicas de balas ---
|
||||||
auto checkBulletTabeCollision(const std::shared_ptr<Bullet>& bullet) -> bool; // Detecta colisión bala-Tabe
|
auto checkBulletTabeCollision(const std::shared_ptr<Bullet>& bullet) -> bool; // Detecta colisión bala-Tabe
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ void Intro::switchText(int from_index, int to_index) {
|
|||||||
|
|
||||||
// Actualiza las variables del objeto
|
// Actualiza las variables del objeto
|
||||||
void Intro::update(float delta_time) {
|
void Intro::update(float delta_time) {
|
||||||
static auto *const SCREEN = Screen::get();
|
static auto* const SCREEN = Screen::get();
|
||||||
SCREEN->update(delta_time); // Actualiza el objeto screen
|
SCREEN->update(delta_time); // Actualiza el objeto screen
|
||||||
Audio::update(); // Actualiza el objeto Audio
|
Audio::update(); // Actualiza el objeto Audio
|
||||||
|
|
||||||
@@ -229,7 +229,7 @@ void Intro::update(float delta_time) {
|
|||||||
|
|
||||||
// Dibuja el objeto en pantalla
|
// Dibuja el objeto en pantalla
|
||||||
void Intro::render() {
|
void Intro::render() {
|
||||||
static auto *const SCREEN = Screen::get();
|
static auto* const SCREEN = Screen::get();
|
||||||
|
|
||||||
SCREEN->start(); // Prepara para empezar a dibujar en la textura de juego
|
SCREEN->start(); // Prepara para empezar a dibujar en la textura de juego
|
||||||
SCREEN->clean(); // Limpia la pantalla
|
SCREEN->clean(); // Limpia la pantalla
|
||||||
@@ -302,7 +302,7 @@ void Intro::initSprites() {
|
|||||||
card_texture->setBlendMode(SDL_BLENDMODE_BLEND);
|
card_texture->setBlendMode(SDL_BLENDMODE_BLEND);
|
||||||
|
|
||||||
// Apuntamos el renderizador a la textura
|
// Apuntamos el renderizador a la textura
|
||||||
auto *temp = SDL_GetRenderTarget(Screen::get()->getRenderer());
|
auto* temp = SDL_GetRenderTarget(Screen::get()->getRenderer());
|
||||||
card_texture->setAsRenderTarget(Screen::get()->getRenderer());
|
card_texture->setAsRenderTarget(Screen::get()->getRenderer());
|
||||||
|
|
||||||
// Limpia la textura
|
// Limpia la textura
|
||||||
@@ -343,7 +343,7 @@ void Intro::initSprites() {
|
|||||||
card_sprites_.at(2)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_FAST, easeOutQuint, 0.0f);
|
card_sprites_.at(2)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_FAST, easeOutQuint, 0.0f);
|
||||||
card_sprites_.at(3)->addPath(param.game.height, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_VERY_SLOW, easeInOutExpo, 0.0f);
|
card_sprites_.at(3)->addPath(param.game.height, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_VERY_SLOW, easeInOutExpo, 0.0f);
|
||||||
card_sprites_.at(4)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_MEDIUM, easeOutElastic, 0.0f);
|
card_sprites_.at(4)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_MEDIUM, easeOutElastic, 0.0f);
|
||||||
card_sprites_.at(5)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_SLOW, easeOutQuad, CARD_ANIM_DELAY_LONG);
|
card_sprites_.at(5)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_SLOW, easeOutQuad, CARD_ANIM_DELAY_LONG_S);
|
||||||
card_sprites_.at(5)->addPath(X_DEST, -CARD_WIDTH, PathType::HORIZONTAL, Y_DEST, CARD_ANIM_DURATION_SHORT, easeInElastic, 0.0f);
|
card_sprites_.at(5)->addPath(X_DEST, -CARD_WIDTH, PathType::HORIZONTAL, Y_DEST, CARD_ANIM_DURATION_SHORT, easeInElastic, 0.0f);
|
||||||
|
|
||||||
// Constantes
|
// Constantes
|
||||||
@@ -357,7 +357,7 @@ void Intro::initSprites() {
|
|||||||
shadow_texture->setBlendMode(SDL_BLENDMODE_BLEND);
|
shadow_texture->setBlendMode(SDL_BLENDMODE_BLEND);
|
||||||
|
|
||||||
// Apuntamos el renderizador a la textura
|
// Apuntamos el renderizador a la textura
|
||||||
auto *temp = SDL_GetRenderTarget(Screen::get()->getRenderer());
|
auto* temp = SDL_GetRenderTarget(Screen::get()->getRenderer());
|
||||||
shadow_texture->setAsRenderTarget(Screen::get()->getRenderer());
|
shadow_texture->setAsRenderTarget(Screen::get()->getRenderer());
|
||||||
|
|
||||||
// Limpia la textura
|
// Limpia la textura
|
||||||
@@ -394,7 +394,7 @@ void Intro::initSprites() {
|
|||||||
shadow_sprites_.at(2)->addPath(-SHADOW_SPRITE_WIDTH, S_X_DEST, PathType::HORIZONTAL, S_Y_DEST, CARD_ANIM_DURATION_FAST, easeOutQuint, 0.0f);
|
shadow_sprites_.at(2)->addPath(-SHADOW_SPRITE_WIDTH, S_X_DEST, PathType::HORIZONTAL, S_Y_DEST, CARD_ANIM_DURATION_FAST, easeOutQuint, 0.0f);
|
||||||
shadow_sprites_.at(3)->addPath(-SHADOW_SPRITE_HEIGHT, S_Y_DEST, PathType::VERTICAL, S_X_DEST, CARD_ANIM_DURATION_VERY_SLOW, easeInOutExpo, 0.0f);
|
shadow_sprites_.at(3)->addPath(-SHADOW_SPRITE_HEIGHT, S_Y_DEST, PathType::VERTICAL, S_X_DEST, CARD_ANIM_DURATION_VERY_SLOW, easeInOutExpo, 0.0f);
|
||||||
shadow_sprites_.at(4)->addPath(param.game.height, S_Y_DEST, PathType::VERTICAL, S_X_DEST, CARD_ANIM_DURATION_MEDIUM, easeOutElastic, 0.0f);
|
shadow_sprites_.at(4)->addPath(param.game.height, S_Y_DEST, PathType::VERTICAL, S_X_DEST, CARD_ANIM_DURATION_MEDIUM, easeOutElastic, 0.0f);
|
||||||
shadow_sprites_.at(5)->addPath(param.game.width, S_X_DEST, PathType::HORIZONTAL, S_Y_DEST, CARD_ANIM_DURATION_SLOW, easeOutQuad, CARD_ANIM_DELAY_LONG);
|
shadow_sprites_.at(5)->addPath(param.game.width, S_X_DEST, PathType::HORIZONTAL, S_Y_DEST, CARD_ANIM_DURATION_SLOW, easeOutQuad, CARD_ANIM_DELAY_LONG_S);
|
||||||
shadow_sprites_.at(5)->addPath(S_X_DEST, param.game.width, PathType::HORIZONTAL, S_Y_DEST, CARD_ANIM_DURATION_SHORT, easeInElastic, 0.0f);
|
shadow_sprites_.at(5)->addPath(S_X_DEST, param.game.width, PathType::HORIZONTAL, S_Y_DEST, CARD_ANIM_DURATION_SHORT, easeInElastic, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,25 +447,25 @@ void Intro::initTexts() {
|
|||||||
texts_.at(8)->setCaption(Lang::getText("[INTRO] 9"));
|
texts_.at(8)->setCaption(Lang::getText("[INTRO] 9"));
|
||||||
texts_.at(8)->setSpeedS(TEXT_SPEED_ULTRA_FAST);
|
texts_.at(8)->setSpeedS(TEXT_SPEED_ULTRA_FAST);
|
||||||
|
|
||||||
for (auto &text : texts_) {
|
for (auto& text : texts_) {
|
||||||
text->center(param.game.game_area.center_x);
|
text->center(param.game.game_area.center_x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza los sprites
|
// Actualiza los sprites
|
||||||
void Intro::updateSprites(float delta_time) {
|
void Intro::updateSprites(float delta_time) {
|
||||||
for (auto &sprite : card_sprites_) {
|
for (auto& sprite : card_sprites_) {
|
||||||
sprite->update(delta_time);
|
sprite->update(delta_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &sprite : shadow_sprites_) {
|
for (auto& sprite : shadow_sprites_) {
|
||||||
sprite->update(delta_time);
|
sprite->update(delta_time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza los textos
|
// Actualiza los textos
|
||||||
void Intro::updateTexts(float delta_time) {
|
void Intro::updateTexts(float delta_time) {
|
||||||
for (auto &text : texts_) {
|
for (auto& text : texts_) {
|
||||||
text->updateS(delta_time); // Usar updateS para delta_time en segundos
|
text->updateS(delta_time); // Usar updateS para delta_time en segundos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -478,7 +478,7 @@ void Intro::renderSprites() {
|
|||||||
|
|
||||||
// Dibuja los textos
|
// Dibuja los textos
|
||||||
void Intro::renderTexts() {
|
void Intro::renderTexts() {
|
||||||
for (const auto &text : texts_) {
|
for (const auto& text : texts_) {
|
||||||
text->render();
|
text->render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,8 +65,8 @@ class Intro {
|
|||||||
static constexpr float CARD_ANIM_DURATION_SLOW = 250.0f / 60.0f; // ≈ 4.1667 s
|
static constexpr float CARD_ANIM_DURATION_SLOW = 250.0f / 60.0f; // ≈ 4.1667 s
|
||||||
static constexpr float CARD_ANIM_DURATION_VERY_SLOW = 300.0f / 60.0f; // ≈ 5.0000 s
|
static constexpr float CARD_ANIM_DURATION_VERY_SLOW = 300.0f / 60.0f; // ≈ 5.0000 s
|
||||||
|
|
||||||
static constexpr float CARD_ANIM_DELAY_LONG = 0.45f; // Retraso largo antes de animación
|
static constexpr float CARD_ANIM_DELAY_LONG_S = 7.5F; // Retraso largo antes de animación
|
||||||
static constexpr float CARD_OFFSET_MARGIN = 10.0f; // Margen fuera de pantalla
|
static constexpr float CARD_OFFSET_MARGIN = 10.0F; // Margen fuera de pantalla
|
||||||
|
|
||||||
// --- Estados internos ---
|
// --- Estados internos ---
|
||||||
enum class State {
|
enum class State {
|
||||||
|
|||||||
@@ -91,10 +91,7 @@ void Title::update(float deltaTime) {
|
|||||||
updateFade();
|
updateFade();
|
||||||
updateState(deltaTime);
|
updateState(deltaTime);
|
||||||
updateStartPrompt(deltaTime);
|
updateStartPrompt(deltaTime);
|
||||||
|
updatePlayers(deltaTime);
|
||||||
for (auto& player : players_) {
|
|
||||||
player->update(deltaTime);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calcula el tiempo transcurrido desde el último frame
|
// Calcula el tiempo transcurrido desde el último frame
|
||||||
@@ -569,6 +566,13 @@ void Title::initPlayers() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Actualiza los jugadores
|
||||||
|
void Title::updatePlayers(float deltaTime) {
|
||||||
|
for (auto& player : players_) {
|
||||||
|
player->update(deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Renderiza los jugadores
|
// Renderiza los jugadores
|
||||||
void Title::renderPlayers() {
|
void Title::renderPlayers() {
|
||||||
for (auto const& player : players_) {
|
for (auto const& player : players_) {
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ class Title {
|
|||||||
|
|
||||||
// --- Gestión de jugadores ---
|
// --- Gestión de jugadores ---
|
||||||
void initPlayers(); // Inicializa los jugadores
|
void initPlayers(); // Inicializa los jugadores
|
||||||
void updatePlayers(); // Actualiza los jugadores
|
void updatePlayers(float deltaTime); // Actualiza los jugadores
|
||||||
void renderPlayers(); // Renderiza los jugadores
|
void renderPlayers(); // Renderiza los jugadores
|
||||||
auto getPlayer(Player::Id id) -> std::shared_ptr<Player>; // Obtiene un jugador a partir de su "id"
|
auto getPlayer(Player::Id id) -> std::shared_ptr<Player>; // Obtiene un jugador a partir de su "id"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user