treballant en sistema de portes i claus

This commit is contained in:
2026-04-10 09:47:48 +02:00
parent 9aff4432df
commit 97c30bf9a1
37 changed files with 1236 additions and 110 deletions

View File

@@ -25,9 +25,12 @@ void CollisionMap::buildExtendedCenter() {
// Copia una región rectangular de src (MW×MH) al mapa extendido
void CollisionMap::copyRegion(const std::vector<int>& src,
int src_col, int src_row,
int dst_col, int dst_row,
int cols, int rows) {
int src_col,
int src_row,
int dst_col,
int dst_row,
int cols,
int rows) {
for (int r = 0; r < rows; ++r) {
for (int c = 0; c < cols; ++c) {
extended_tile_map_[((dst_row + r) * EW) + (dst_col + c)] =
@@ -78,7 +81,6 @@ void CollisionMap::updateBorders(const AdjacentData& adj) {
}
}
#ifdef _DEBUG
void CollisionMap::setCollisionTile(int index, int value) {
if (index >= 0 && index < static_cast<int>(collision_tile_map_.size())) {
collision_tile_map_[index] = value;
@@ -88,4 +90,3 @@ void CollisionMap::setCollisionTile(int index, int value) {
extended_tile_map_[((row + B) * EW) + (col + B)] = value;
}
}
#endif

View File

@@ -43,29 +43,35 @@ class CollisionMap {
[[nodiscard]] auto getConveyorBeltDirection() const -> int { return conveyor_belt_direction_; }
[[nodiscard]] auto getCollisionTileMap() const -> const std::vector<int>& { return collision_tile_map_; }
#ifdef _DEBUG
// Modifica un tile del mapa de colisiones (original + extendido) en runtime.
// Lo usan: el editor de mapas (debug) y el DoorManager para mostrar/ocultar muros de puertas.
void setCollisionTile(int index, int value);
#ifdef _DEBUG
void setConveyorBeltDirection(int direction) { conveyor_belt_direction_ = direction; }
#endif
private:
static constexpr int B = CollisionBorder::TILES; // Tiles de borde
static constexpr int MW = Map::WIDTH; // Ancho original (32)
static constexpr int MH = Map::HEIGHT; // Alto original (21)
static constexpr int EW = ExtendedMap::WIDTH; // Ancho extendido (38)
static constexpr int EH = ExtendedMap::HEIGHT; // Alto extendido (27)
static constexpr int B = CollisionBorder::TILES; // Tiles de borde
static constexpr int MW = Map::WIDTH; // Ancho original (32)
static constexpr int MH = Map::HEIGHT; // Alto original (21)
static constexpr int EW = ExtendedMap::WIDTH; // Ancho extendido (38)
static constexpr int EH = ExtendedMap::HEIGHT; // Alto extendido (27)
std::vector<int> collision_tile_map_; // Original (32×21)
std::vector<int> extended_tile_map_; // Extendido (38×27) — referenciado por TileCollider
std::vector<int> collision_tile_map_; // Original (32×21)
std::vector<int> extended_tile_map_; // Extendido (38×27) — referenciado por TileCollider
int conveyor_belt_direction_;
TileCollider tile_collider_; // Debe ir después de extended_tile_map_ (usa referencia)
TileCollider tile_collider_; // Debe ir después de extended_tile_map_ (usa referencia)
// Copia el centro (room actual) al mapa extendido. Los bordes quedan como EMPTY (0).
void buildExtendedCenter();
// Copia una región rectangular de un tilemap fuente (MW×MH) al mapa extendido.
void copyRegion(const std::vector<int>& src,
int src_col, int src_row,
int dst_col, int dst_row,
int cols, int rows);
int src_col,
int src_row,
int dst_col,
int dst_row,
int cols,
int rows);
};

View File

@@ -0,0 +1,92 @@
#include "door_manager.hpp"
#include <utility> // Para std::move
#include "game/entities/door.hpp" // Para Door
#include "game/gameplay/collision_map.hpp" // Para CollisionMap
#include "game/gameplay/door_tracker.hpp" // Para DoorTracker
#include "game/gameplay/inventory.hpp" // Para Inventory
#include "game/gameplay/tile_collider.hpp" // Para TileCollider::Tile
#include "utils/defines.hpp" // Para Map::WIDTH, Tile::SIZE
#include "utils/utils.hpp" // Para checkCollision
// Constructor
DoorManager::DoorManager(std::string room_id, CollisionMap* collision_map)
: room_id_(std::move(room_id)),
collision_map_(collision_map) {
}
// Añade una puerta. Si está bloqueante (CLOSED), pinta sus 4 tiles como WALL
// en el CollisionMap. Si ya está OPENED (porque venía persistida del
// DoorTracker), no se tocan los tiles.
void DoorManager::addDoor(std::shared_ptr<Door> door) { // NOLINT(readability-identifier-naming)
if (door->isBlocking()) {
writeDoorTiles(*door, static_cast<int>(TileCollider::Tile::WALL));
}
doors_.push_back(std::move(door));
}
// Elimina todas las puertas
void DoorManager::clear() {
doors_.clear();
}
// Actualiza animaciones y procesa transiciones a OPENED
void DoorManager::update(float delta_time) {
for (const auto& door : doors_) {
door->update(delta_time);
// Si la puerta acaba de transicionar a OPENED, liberar los tiles y persistir
if (door->justOpened()) {
writeDoorTiles(*door, static_cast<int>(TileCollider::Tile::EMPTY));
DoorTracker::get()->addDoor(room_id_, door->getPos());
}
}
}
// Renderiza todas las puertas
void DoorManager::render() {
for (const auto& door : doors_) {
door->render();
}
}
// Pausa/despausa todas las puertas
void DoorManager::setPaused(bool paused) {
for (const auto& door : doors_) {
door->setPaused(paused);
}
}
// Intenta abrir las puertas cercanas al jugador
void DoorManager::tryUnlock(const SDL_FRect& player_rect) {
// Infla el rect del jugador 1 px en cada lado para detectar contacto
const SDL_FRect INFLATED = {
.x = player_rect.x - 1.0F,
.y = player_rect.y - 1.0F,
.w = player_rect.w + 2.0F,
.h = player_rect.h + 2.0F,
};
for (const auto& door : doors_) {
if (door->getState() != Door::State::CLOSED) {
continue;
}
if (::checkCollision(INFLATED, door->getCollider()) && Inventory::get()->hasKey(door->getId())) {
door->startOpening();
}
}
}
// Setea las 4 celdas que ocupa la puerta (1 col × 4 filas) al valor indicado
void DoorManager::writeDoorTiles(const Door& door, int tile_value) {
// Convertir posición en píxeles a coordenadas de tile
const SDL_FPoint POS = door.getPos();
const int COL = static_cast<int>(POS.x) / Tile::SIZE;
const int ROW = static_cast<int>(POS.y) / Tile::SIZE;
for (int i = 0; i < DOOR_TILES_HEIGHT; ++i) {
const int INDEX = ((ROW + i) * Map::WIDTH) + COL;
collision_map_->setCollisionTile(INDEX, tile_value);
}
}

View File

@@ -0,0 +1,69 @@
#pragma once
#include <SDL3/SDL.h>
#include <memory> // Para shared_ptr
#include <string> // Para string
#include <vector> // Para vector
#include "game/entities/door.hpp" // Para Door, Door::Data
class CollisionMap;
/**
* @brief Gestor de puertas de una habitación
*
* Responsabilidades:
* - Almacenar y gestionar la colección de puertas
* - Actualizar y renderizar todas las puertas
* - Detectar contacto del jugador con puertas cerradas y disparar la apertura
* si tiene la llave correspondiente (consultando el Inventory global)
* - Sincronizar el estado bloqueante con el CollisionMap: cuando una puerta
* está CLOSED u OPENING, sus 4 tiles son WALL; cuando pasa a OPENED, se
* ponen a EMPTY
* - Persistir el estado abierto en DoorTracker
*/
class DoorManager {
public:
DoorManager(std::string room_id, CollisionMap* collision_map);
~DoorManager() = default;
// Prohibir copia y movimiento para evitar duplicación accidental
DoorManager(const DoorManager&) = delete;
auto operator=(const DoorManager&) -> DoorManager& = delete;
DoorManager(DoorManager&&) = delete;
auto operator=(DoorManager&&) -> DoorManager& = delete;
// Gestión de puertas
void addDoor(std::shared_ptr<Door> door); // Añade una puerta y aplica WALLs si está cerrada
void clear(); // Elimina todas las puertas
// Actualización y renderizado
void update(float delta_time); // Actualiza animaciones y procesa transiciones a OPENED
void render(); // Renderiza todas las puertas
// Estado
void setPaused(bool paused); // Pausa/despausa todas las puertas
/**
* @brief Intenta abrir las puertas cercanas al jugador si tiene la llave
*
* Infla el rectángulo del jugador en 1 px para detectar contacto
* (las puertas cerradas son muros sólidos, así que el jugador queda
* adyacente sin solapar). Para cada puerta CLOSED que solape el rect
* inflado, si el Inventory contiene la llave correspondiente, llama a
* door->startOpening().
*
* @param player_rect Rectángulo de colisión del jugador
*/
void tryUnlock(const SDL_FRect& player_rect);
private:
static constexpr int DOOR_TILES_HEIGHT = 4; // Una puerta ocupa 4 tiles verticalmente
void writeDoorTiles(const Door& door, int tile_value); // Setea las 4 celdas en el CollisionMap
std::vector<std::shared_ptr<Door>> doors_; // Colección de puertas
std::string room_id_; // Identificador de la habitación
CollisionMap* collision_map_; // Referencia no propietaria al CollisionMap de la Room
};

View File

@@ -0,0 +1,74 @@
#include "game/gameplay/door_tracker.hpp"
// [SINGLETON]
DoorTracker* DoorTracker::door_tracker = nullptr;
// [SINGLETON] Crearemos el objeto con esta función estática
void DoorTracker::init() {
DoorTracker::door_tracker = new DoorTracker();
}
// [SINGLETON] Destruiremos el objeto con esta función estática
void DoorTracker::destroy() {
delete DoorTracker::door_tracker;
}
// [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él
auto DoorTracker::get() -> DoorTracker* {
return DoorTracker::door_tracker;
}
// Comprueba si la puerta ya está abierta
auto DoorTracker::hasBeenOpened(const std::string& name, SDL_FPoint pos) -> bool {
if (const int INDEX = findByName(name); INDEX != NOT_FOUND) {
if (findByPos(INDEX, pos) != NOT_FOUND) {
return true;
}
}
return false;
}
// Marca la puerta como abierta
void DoorTracker::addDoor(const std::string& name, SDL_FPoint pos) {
if (!hasBeenOpened(name, pos)) {
if (const int INDEX = findByName(name); INDEX != NOT_FOUND) {
doors_.at(INDEX).pos.push_back(pos);
} else {
doors_.emplace_back(name, pos);
}
}
}
// Vacía el tracker
void DoorTracker::clear() {
doors_.clear();
}
// Busca una entrada en la lista por nombre
auto DoorTracker::findByName(const std::string& name) -> int {
int i = 0;
for (const auto& door : doors_) {
if (door.name == name) {
return i;
}
i++;
}
return NOT_FOUND;
}
// Busca una entrada en la lista por posición
auto DoorTracker::findByPos(int index, SDL_FPoint pos) -> int {
int i = 0;
for (const auto& door : doors_[index].pos) {
if ((door.x == pos.x) && (door.y == pos.y)) {
return i;
}
i++;
}
return NOT_FOUND;
}

View File

@@ -0,0 +1,58 @@
#pragma once
#include <SDL3/SDL.h>
#include <string> // Para string, basic_string
#include <utility>
#include <vector> // Para vector
/**
* @brief Tracker global de puertas abiertas (singleton)
*
* Mantiene un registro por habitación de las posiciones de las puertas que
* ya han sido abiertas, de modo que al volver a entrar a una habitación las
* puertas ya abiertas se reconstruyen directamente en estado OPENED.
* Mismo patrón que ItemTracker.
*/
class DoorTracker {
public:
// Gestión singleton
static void init(); // Inicialización
static void destroy(); // Destrucción
static auto get() -> DoorTracker*; // Acceso al singleton
// Gestión de puertas abiertas
auto hasBeenOpened(const std::string& name, SDL_FPoint pos) -> bool; // Comprueba si la puerta ya está abierta
void addDoor(const std::string& name, SDL_FPoint pos); // Marca la puerta como abierta
void clear(); // Vacía el tracker (reset de partida)
private:
// Tipos anidados privados
struct Data {
std::string name; // Nombre de la habitación
std::vector<SDL_FPoint> pos; // Lista de posiciones de puertas abiertas
// Constructor para facilitar creación con posición inicial
Data(std::string name, const SDL_FPoint& position)
: name(std::move(name)) {
pos.push_back(position);
}
};
// Constantes privadas
static constexpr int NOT_FOUND = -1; // Valor de retorno cuando no se encuentra un elemento
// Constantes singleton
static DoorTracker* door_tracker; // [SINGLETON] Objeto privado
// Métodos privados
auto findByName(const std::string& name) -> int; // Busca una entrada en la lista por nombre
auto findByPos(int index, SDL_FPoint pos) -> int; // Busca una entrada en la lista por posición
// Constructor y destructor privados [SINGLETON]
DoorTracker() = default;
~DoorTracker() = default;
// Variables miembro
std::vector<Data> doors_; // Lista de puertas abiertas por habitación
};

View File

@@ -0,0 +1,34 @@
#include "game/gameplay/inventory.hpp"
// [SINGLETON]
Inventory* Inventory::inventory = nullptr;
// [SINGLETON] Crearemos el objeto con esta función estática
void Inventory::init() {
Inventory::inventory = new Inventory();
}
// [SINGLETON] Destruiremos el objeto con esta función estática
void Inventory::destroy() {
delete Inventory::inventory;
}
// [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él
auto Inventory::get() -> Inventory* {
return Inventory::inventory;
}
// Añade una llave al inventario
void Inventory::addKey(const std::string& key_id) {
keys_.insert(key_id);
}
// Comprueba si el inventario contiene una llave
auto Inventory::hasKey(const std::string& key_id) const -> bool {
return keys_.contains(key_id);
}
// Vacía el inventario
void Inventory::clear() {
keys_.clear();
}

View File

@@ -0,0 +1,34 @@
#pragma once
#include <set> // Para set
#include <string> // Para string
/**
* @brief Inventario global del jugador (singleton)
*
* Mantiene el conjunto de identificadores de llaves recogidas durante la partida.
* Persiste entre habitaciones y entre muertes (igual que ItemTracker).
*/
class Inventory {
public:
// Gestión singleton
static void init(); // Inicialización
static void destroy(); // Destrucción
static auto get() -> Inventory*; // Acceso al singleton
// Gestión de llaves
void addKey(const std::string& key_id); // Añade una llave al inventario
[[nodiscard]] auto hasKey(const std::string& key_id) const -> bool; // Comprueba si tiene una llave
void clear(); // Vacía el inventario (reset de partida)
private:
// Constantes singleton
static Inventory* inventory; // [SINGLETON] Objeto privado
// Constructor y destructor privados [SINGLETON]
Inventory() = default;
~Inventory() = default;
// Variables miembro
std::set<std::string> keys_; // Conjunto de identificadores de llaves recogidas
};

View File

@@ -0,0 +1,69 @@
#include "key_manager.hpp"
#include <utility> // Para std::move
#include "core/audio/audio.hpp" // Para Audio
#include "game/defaults.hpp" // Para Defaults::Sound::Files
#include "game/entities/key.hpp" // Para Key
#include "game/gameplay/inventory.hpp" // Para Inventory
#include "game/gameplay/key_tracker.hpp" // Para KeyTracker
#include "utils/utils.hpp" // Para checkCollision
// Constructor
KeyManager::KeyManager(std::string room_id)
: room_id_(std::move(room_id)) {
}
// Añade una llave a la colección
void KeyManager::addKey(std::shared_ptr<Key> key) { // NOLINT(readability-identifier-naming)
keys_.push_back(std::move(key));
}
// Elimina todas las llaves
void KeyManager::clear() {
keys_.clear();
}
// Actualiza todas las llaves
void KeyManager::update(float delta_time) {
for (const auto& key : keys_) {
key->update(delta_time);
}
}
// Renderiza todas las llaves
void KeyManager::render() {
for (const auto& key : keys_) {
key->render();
}
}
// Pausa/despausa todas las llaves
void KeyManager::setPaused(bool paused) {
for (const auto& key : keys_) {
key->setPaused(paused);
}
}
// Comprueba si hay colisión con alguna llave
auto KeyManager::checkCollision(SDL_FRect& rect) -> bool {
for (int i = 0; i < static_cast<int>(keys_.size()); ++i) {
if (::checkCollision(rect, keys_.at(i)->getCollider())) {
// Añade el id de la llave al Inventory global
Inventory::get()->addKey(keys_.at(i)->getId());
// Registra la posición en KeyTracker (para no respawnear)
KeyTracker::get()->addKey(room_id_, keys_.at(i)->getPos());
// Elimina la llave de la colección
keys_.erase(keys_.begin() + i);
// Reproduce el sonido de pickup (reutiliza el sonido de item)
Audio::get()->playSound(Defaults::Sound::Files::ITEM, Audio::Group::GAME);
return true;
}
}
return false;
}

View File

@@ -0,0 +1,59 @@
#pragma once
#include <SDL3/SDL.h>
#include <memory> // Para shared_ptr
#include <string> // Para string
#include <vector> // Para vector
#include "game/entities/key.hpp" // Para Key, Key::Data
/**
* @brief Gestor de llaves de una habitación
*
* Responsabilidades:
* - Almacenar y gestionar la colección de llaves
* - Actualizar y renderizar todas las llaves
* - Detectar colisiones con el jugador y gestionar pickup
* - Integración con Inventory (singleton global) y KeyTracker (persistencia por room)
*/
class KeyManager {
public:
explicit KeyManager(std::string room_id);
~KeyManager() = default;
// Prohibir copia y movimiento para evitar duplicación accidental
KeyManager(const KeyManager&) = delete;
auto operator=(const KeyManager&) -> KeyManager& = delete;
KeyManager(KeyManager&&) = delete;
auto operator=(KeyManager&&) -> KeyManager& = delete;
// Gestión de llaves
void addKey(std::shared_ptr<Key> key); // Añade una llave a la colección
void clear(); // Elimina todas las llaves
// Actualización y renderizado
void update(float delta_time); // Actualiza todas las llaves
void render(); // Renderiza todas las llaves
// Estado
void setPaused(bool paused); // Pausa/despausa todas las llaves
/**
* @brief Comprueba si hay colisión con alguna llave
*
* Si hay colisión:
* - Añade el id de la llave al Inventory global
* - Registra la posición en KeyTracker (para no respawnear)
* - Elimina la llave de la colección
* - Reproduce el sonido de pickup
*
* @param rect Rectángulo de colisión
* @return true si hubo colisión, false en caso contrario
*/
auto checkCollision(SDL_FRect& rect) -> bool;
private:
std::vector<std::shared_ptr<Key>> keys_; // Colección de llaves
std::string room_id_; // Identificador de la habitación
};

View File

@@ -0,0 +1,74 @@
#include "game/gameplay/key_tracker.hpp"
// [SINGLETON]
KeyTracker* KeyTracker::key_tracker = nullptr;
// [SINGLETON] Crearemos el objeto con esta función estática
void KeyTracker::init() {
KeyTracker::key_tracker = new KeyTracker();
}
// [SINGLETON] Destruiremos el objeto con esta función estática
void KeyTracker::destroy() {
delete KeyTracker::key_tracker;
}
// [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él
auto KeyTracker::get() -> KeyTracker* {
return KeyTracker::key_tracker;
}
// Comprueba si la llave ya ha sido cogida
auto KeyTracker::hasBeenPicked(const std::string& name, SDL_FPoint pos) -> bool {
if (const int INDEX = findByName(name); INDEX != NOT_FOUND) {
if (findByPos(INDEX, pos) != NOT_FOUND) {
return true;
}
}
return false;
}
// Añade la llave a la lista de llaves recogidas
void KeyTracker::addKey(const std::string& name, SDL_FPoint pos) {
if (!hasBeenPicked(name, pos)) {
if (const int INDEX = findByName(name); INDEX != NOT_FOUND) {
keys_.at(INDEX).pos.push_back(pos);
} else {
keys_.emplace_back(name, pos);
}
}
}
// Vacía el tracker
void KeyTracker::clear() {
keys_.clear();
}
// Busca una entrada en la lista por nombre
auto KeyTracker::findByName(const std::string& name) -> int {
int i = 0;
for (const auto& key : keys_) {
if (key.name == name) {
return i;
}
i++;
}
return NOT_FOUND;
}
// Busca una entrada en la lista por posición
auto KeyTracker::findByPos(int index, SDL_FPoint pos) -> int {
int i = 0;
for (const auto& key : keys_[index].pos) {
if ((key.x == pos.x) && (key.y == pos.y)) {
return i;
}
i++;
}
return NOT_FOUND;
}

View File

@@ -0,0 +1,57 @@
#pragma once
#include <SDL3/SDL.h>
#include <string> // Para string, basic_string
#include <utility>
#include <vector> // Para vector
/**
* @brief Tracker global de llaves recogidas (singleton)
*
* Mantiene un registro por habitación de las posiciones de las llaves recogidas,
* de modo que al volver a entrar a una habitación las llaves ya cogidas no
* vuelvan a aparecer. Mismo patrón que ItemTracker.
*/
class KeyTracker {
public:
// Gestión singleton
static void init(); // Inicialización
static void destroy(); // Destrucción
static auto get() -> KeyTracker*; // Acceso al singleton
// Gestión de llaves recogidas
auto hasBeenPicked(const std::string& name, SDL_FPoint pos) -> bool; // Comprueba si la llave ya ha sido cogida
void addKey(const std::string& name, SDL_FPoint pos); // Añade la llave a la lista
void clear(); // Vacía el tracker (reset de partida)
private:
// Tipos anidados privados
struct Data {
std::string name; // Nombre de la habitación
std::vector<SDL_FPoint> pos; // Lista de posiciones de llaves cogidas
// Constructor para facilitar creación con posición inicial
Data(std::string name, const SDL_FPoint& position)
: name(std::move(name)) {
pos.push_back(position);
}
};
// Constantes privadas
static constexpr int NOT_FOUND = -1; // Valor de retorno cuando no se encuentra un elemento
// Constantes singleton
static KeyTracker* key_tracker; // [SINGLETON] Objeto privado
// Métodos privados
auto findByName(const std::string& name) -> int; // Busca una entrada en la lista por nombre
auto findByPos(int index, SDL_FPoint pos) -> int; // Busca una entrada en la lista por posición
// Constructor y destructor privados [SINGLETON]
KeyTracker() = default;
~KeyTracker() = default;
// Variables miembro
std::vector<Data> keys_; // Lista de llaves recogidas por habitación
};

View File

@@ -6,9 +6,13 @@
#include "core/resources/resource_cache.hpp" // Para Resource
#include "game/defaults.hpp" // Para Defaults::Game
#include "game/gameplay/collision_map.hpp" // Para CollisionMap
#include "game/gameplay/door_manager.hpp" // Para DoorManager
#include "game/gameplay/door_tracker.hpp" // Para DoorTracker
#include "game/gameplay/enemy_manager.hpp" // Para EnemyManager
#include "game/gameplay/item_manager.hpp" // Para ItemManager
#include "game/gameplay/item_tracker.hpp" // Para ItemTracker
#include "game/gameplay/key_manager.hpp" // Para KeyManager
#include "game/gameplay/key_tracker.hpp" // Para KeyTracker
#include "game/gameplay/platform_manager.hpp" // Para PlatformManager
#include "game/gameplay/room_loader.hpp" // Para RoomLoader
#include "game/gameplay/scoreboard.hpp" // Para Scoreboard::Data
@@ -21,18 +25,27 @@ Room::Room(const std::string& room_path, std::shared_ptr<Scoreboard::Data> data)
: data_(std::move(data)) {
auto room = Resource::Cache::get()->getRoom(room_path);
// Crea los managers de enemigos, items y plataformas
// Crea los managers de enemigos, items, plataformas y llaves
enemy_manager_ = std::make_unique<EnemyManager>();
item_manager_ = std::make_unique<ItemManager>(room->number, data_);
platform_manager_ = std::make_unique<PlatformManager>();
key_manager_ = std::make_unique<KeyManager>(room->number);
initializeRoom(*room);
// Crea el mapa de colisiones desde el collision_tile_map
// Crea el mapa de colisiones desde el collision_tile_map (debe ir antes
// del DoorManager porque éste lo necesita para mutar tiles dinámicamente)
collision_map_ = std::make_unique<CollisionMap>(room->collision_tile_map, conveyor_belt_direction_);
// Crea el renderizador del tilemap (necesita tile_map_, tile_set_width_, surface_, bg_color_, conveyor_belt_direction_)
// Crea el manager de puertas (necesita el CollisionMap para sincronizar muros)
door_manager_ = std::make_unique<DoorManager>(room->number, collision_map_.get());
initializeRoom(*room);
// Crea el renderizador del tilemap (necesita tile_map_, tile_set_width_, surface_, bg_color_, conveyor_belt_direction_).
// Se inicializa con el collision tile map ya modificado por las puertas (que han marcado sus
// celdas como WALL en el collision_map_, pero el renderer solo lo usa para detectar superficies
// de dibujo, no para colisión, así que pasamos la versión actualizada del collision_map_).
tilemap_renderer_ = std::make_unique<TilemapRenderer>(tile_map_, tile_set_width_, surface_, bg_color_, conveyor_belt_direction_);
tilemap_renderer_->initialize(room->collision_tile_map);
tilemap_renderer_->initialize(collision_map_->getCollisionTileMap());
}
// Destructor
@@ -79,6 +92,23 @@ void Room::initializeRoom(const Data& room) {
item_manager_->addItem(std::make_shared<Item>(item_copy));
}
}
// Crear las llaves usando el manager (saltar las ya recogidas)
for (const auto& key_data : room.keys) {
const SDL_FPoint KEY_POS{.x = key_data.x, .y = key_data.y};
if (!KeyTracker::get()->hasBeenPicked(room.number, KEY_POS)) {
key_manager_->addKey(std::make_shared<Key>(key_data));
}
}
// Crear las puertas usando el manager. Las que ya hayan sido abiertas
// antes (DoorTracker) se construyen directamente en estado OPENED y no
// pintan WALLs en el collision_map_.
for (const auto& door_data : room.doors) {
const SDL_FPoint DOOR_POS{.x = door_data.x, .y = door_data.y};
const bool ALREADY_OPENED = DoorTracker::get()->hasBeenOpened(room.number, DOOR_POS);
door_manager_->addDoor(std::make_shared<Door>(door_data, ALREADY_OPENED));
}
}
// Dibuja el mapa en pantalla
@@ -101,6 +131,16 @@ void Room::renderItems() {
item_manager_->render();
}
// Dibuja las llaves en pantalla
void Room::renderKeys() {
key_manager_->render();
}
// Dibuja las puertas en pantalla
void Room::renderDoors() {
door_manager_->render();
}
#ifdef _DEBUG
// Redibuja el mapa (para actualizar modo debug)
void Room::redrawMap() {
@@ -202,6 +242,12 @@ void Room::update(float delta_time) { // NOLINT(readability-make-member-functio
// Actualiza los items usando el manager
item_manager_->update(delta_time);
// Actualiza las llaves usando el manager
key_manager_->update(delta_time);
// Actualiza las puertas usando el manager (procesa transiciones a OPENED)
door_manager_->update(delta_time);
}
// Pone el mapa en modo pausa
@@ -209,6 +255,8 @@ void Room::setPaused(bool value) {
is_paused_ = value;
tilemap_renderer_->setPaused(value);
item_manager_->setPaused(value);
key_manager_->setPaused(value);
door_manager_->setPaused(value);
}
auto Room::getTileCollider() const -> const TileCollider& {
@@ -247,6 +295,14 @@ auto Room::itemCollision(SDL_FRect& rect) -> bool {
return item_manager_->checkCollision(rect);
}
auto Room::keyCollision(SDL_FRect& rect) -> bool {
return key_manager_->checkCollision(rect);
}
void Room::tryUnlockDoors(const SDL_FRect& player_rect) {
door_manager_->tryUnlock(player_rect);
}
auto Room::checkPlayerOnPlatform(const SDL_FRect& player_collider, float player_vy) -> MovingPlatform* {
return platform_manager_->checkPlayerOnPlatform(player_collider, player_vy);
}

View File

@@ -6,17 +6,21 @@
#include <string> // Para string
#include <vector> // Para vector
#include "game/defaults.hpp" // Para Defaults::Game::Room
#include "game/entities/enemy.hpp" // Para EnemyData
#include "game/entities/item.hpp" // Para ItemData
#include "game/entities/moving_platform.hpp" // Para MovingPlatform::Data
#include "game/gameplay/collision_map.hpp" // Para CollisionMap::AdjacentData
#include "game/gameplay/scoreboard.hpp" // Para Scoreboard::Data
#include "utils/defines.hpp" // Para Tile::SIZE, Map::WIDTH, Map::HEIGHT
#include "game/defaults.hpp" // Para Defaults::Game::Room
#include "game/entities/door.hpp" // Para Door::Data
#include "game/entities/enemy.hpp" // Para EnemyData
#include "game/entities/item.hpp" // Para ItemData
#include "game/entities/key.hpp" // Para Key::Data
#include "game/entities/moving_platform.hpp" // Para MovingPlatform::Data
#include "game/gameplay/collision_map.hpp" // Para CollisionMap::AdjacentData
#include "game/gameplay/scoreboard.hpp" // Para Scoreboard::Data
#include "utils/defines.hpp" // Para Tile::SIZE, Map::WIDTH, Map::HEIGHT
class Surface;
class EnemyManager;
class ItemManager;
class PlatformManager;
class KeyManager;
class DoorManager;
class TileCollider;
class TilemapRenderer;
@@ -47,6 +51,8 @@ class Room {
std::vector<Enemy::Data> enemies;
std::vector<Item::Data> items;
std::vector<MovingPlatform::Data> platforms;
std::vector<Key::Data> keys;
std::vector<Door::Data> doors;
};
// Constructor y destructor
@@ -60,6 +66,8 @@ class Room {
void renderEnemies();
void renderPlatforms();
void renderItems();
void renderKeys();
void renderDoors();
#ifdef _DEBUG
void redrawMap();
void updateEditorMode(float delta_time);
@@ -67,6 +75,8 @@ class Room {
auto getEnemyManager() -> EnemyManager* { return enemy_manager_.get(); }
auto getItemManager() -> ItemManager* { return item_manager_.get(); }
auto getPlatformManager() -> PlatformManager* { return platform_manager_.get(); }
auto getKeyManager() -> KeyManager* { return key_manager_.get(); }
auto getDoorManager() -> DoorManager* { return door_manager_.get(); }
void setItemColors(Uint8 color1, Uint8 color2);
void setTile(int index, int tile_value);
void setCollisionTile(int index, int value);
@@ -80,6 +90,8 @@ class Room {
auto getRoom(Border border) -> std::string;
auto enemyCollision(SDL_FRect& rect) -> bool;
auto itemCollision(SDL_FRect& rect) -> bool;
auto keyCollision(SDL_FRect& rect) -> bool;
void tryUnlockDoors(const SDL_FRect& player_rect);
auto checkPlayerOnPlatform(const SDL_FRect& player_collider, float player_vy) -> MovingPlatform*;
void setPaused(bool value);
[[nodiscard]] auto getConveyorBeltDirection() const -> int { return conveyor_belt_direction_; }
@@ -98,6 +110,8 @@ class Room {
std::unique_ptr<EnemyManager> enemy_manager_;
std::unique_ptr<ItemManager> item_manager_;
std::unique_ptr<PlatformManager> platform_manager_;
std::unique_ptr<KeyManager> key_manager_;
std::unique_ptr<DoorManager> door_manager_;
std::unique_ptr<CollisionMap> collision_map_;
std::unique_ptr<TilemapRenderer> tilemap_renderer_;
std::shared_ptr<Surface> surface_;

View File

@@ -382,6 +382,86 @@ void RoomLoader::parsePlatforms(const fkyaml::node& yaml, Room::Data& room, bool
}
}
// Parsea los datos de una llave individual
auto RoomLoader::parseKeyData(const fkyaml::node& key_node) -> Key::Data { // NOLINT(readability-convert-member-functions-to-static)
Key::Data key;
if (key_node.contains("animation")) {
key.animation_path = key_node["animation"].get_value<std::string>();
}
if (key_node.contains("id")) {
key.id = key_node["id"].get_value<std::string>();
}
if (key_node.contains("position")) {
const auto& pos = key_node["position"];
if (pos.contains("x")) {
key.x = pos["x"].get_value<float>() * Tile::SIZE;
}
if (pos.contains("y")) {
key.y = pos["y"].get_value<float>() * Tile::SIZE;
}
}
return key;
}
// Parsea la lista de llaves de la habitación
void RoomLoader::parseKeys(const fkyaml::node& yaml, Room::Data& room, bool verbose) { // NOLINT(readability-convert-member-functions-to-static)
if (!yaml.contains("keys") || yaml["keys"].is_null()) {
return;
}
const auto& keys_node = yaml["keys"];
for (const auto& key_node : keys_node) {
room.keys.push_back(parseKeyData(key_node));
}
if (verbose) {
std::cout << "Loaded " << room.keys.size() << " keys\n";
}
}
// Parsea los datos de una puerta individual
auto RoomLoader::parseDoorData(const fkyaml::node& door_node) -> Door::Data { // NOLINT(readability-convert-member-functions-to-static)
Door::Data door;
if (door_node.contains("animation")) {
door.animation_path = door_node["animation"].get_value<std::string>();
}
if (door_node.contains("id")) {
door.id = door_node["id"].get_value<std::string>();
}
if (door_node.contains("position")) {
const auto& pos = door_node["position"];
if (pos.contains("x")) {
door.x = pos["x"].get_value<float>() * Tile::SIZE;
}
if (pos.contains("y")) {
door.y = pos["y"].get_value<float>() * Tile::SIZE;
}
}
return door;
}
// Parsea la lista de puertas de la habitación
void RoomLoader::parseDoors(const fkyaml::node& yaml, Room::Data& room, bool verbose) { // NOLINT(readability-convert-member-functions-to-static)
if (!yaml.contains("doors") || yaml["doors"].is_null()) {
return;
}
const auto& doors_node = yaml["doors"];
for (const auto& door_node : doors_node) {
room.doors.push_back(parseDoorData(door_node));
}
if (verbose) {
std::cout << "Loaded " << room.doors.size() << " doors\n";
}
}
#ifdef _DEBUG
// Carga una habitación desde un string YAML (para el editor de mapas, evita el resource pack)
auto RoomLoader::loadFromString(const std::string& yaml_content, const std::string& file_name) -> Room::Data {
@@ -393,6 +473,8 @@ auto RoomLoader::loadFromString(const std::string& yaml_content, const std::stri
parseEnemies(yaml, room, false);
parseItems(yaml, room, false);
parsePlatforms(yaml, room, false);
parseKeys(yaml, room, false);
parseDoors(yaml, room, false);
} catch (const fkyaml::exception& e) {
std::cerr << "YAML parsing error in " << file_name << ": " << e.what() << '\n';
} catch (const std::exception& e) {
@@ -428,6 +510,8 @@ auto RoomLoader::loadYAML(const std::string& file_path, bool verbose) -> Room::D
parseEnemies(yaml, room, verbose);
parseItems(yaml, room, verbose);
parsePlatforms(yaml, room, verbose);
parseKeys(yaml, room, verbose);
parseDoors(yaml, room, verbose);
if (verbose) {
std::cout << "Room loaded successfully: " << FILE_NAME << '\n';

View File

@@ -4,8 +4,10 @@
#include <vector> // Para vector
#include "external/fkyaml_node.hpp" // Para fkyaml::node
#include "game/entities/door.hpp" // Para Door::Data
#include "game/entities/enemy.hpp" // Para Enemy::Data
#include "game/entities/item.hpp" // Para Item::Data
#include "game/entities/key.hpp" // Para Key::Data
#include "game/entities/moving_platform.hpp" // Para MovingPlatform::Data
#include "game/gameplay/room.hpp" // Para Room::Data
@@ -137,4 +139,24 @@ class RoomLoader {
static void parsePlatforms(const fkyaml::node& yaml, Room::Data& room, bool verbose);
static auto parsePlatformData(const fkyaml::node& platform_node) -> MovingPlatform::Data;
/**
* @brief Parsea la lista de llaves de la habitación
*/
static void parseKeys(const fkyaml::node& yaml, Room::Data& room, bool verbose);
/**
* @brief Parsea los datos de una llave individual
*/
static auto parseKeyData(const fkyaml::node& key_node) -> Key::Data;
/**
* @brief Parsea la lista de puertas de la habitación
*/
static void parseDoors(const fkyaml::node& yaml, Room::Data& room, bool verbose);
/**
* @brief Parsea los datos de una puerta individual
*/
static auto parseDoorData(const fkyaml::node& door_node) -> Door::Data;
};

View File

@@ -5,7 +5,10 @@
#include "utils/defines.hpp"
TileCollider::TileCollider(const std::vector<int>& extended_tile_map, int width, int height, int border_px)
: width_(width), height_(height), border_px_(border_px), tile_map_(extended_tile_map) {}
: width_(width),
height_(height),
border_px_(border_px),
tile_map_(extended_tile_map) {}
// --- Queries básicas ---