#include "game/gameplay/room.hpp" #include // Para std::move #include "core/rendering/surface.hpp" // Para Surface #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_format.hpp" // Para RoomFormat #include "game/gameplay/scoreboard.hpp" // Para Scoreboard::Data #include "game/gameplay/tilemap_renderer.hpp" // Para TilemapRenderer #include "utils/defines.hpp" // Para TILE_SIZE #include "utils/utils.hpp" // Constructor Room::Room(const std::string& room_path, std::shared_ptr data) : data_(std::move(data)) { auto room = Resource::Cache::get()->getRoom(room_path); // Crea los managers de enemigos, items, plataformas y llaves enemy_manager_ = std::make_unique(); item_manager_ = std::make_unique(room->number, data_); platform_manager_ = std::make_unique(); key_manager_ = std::make_unique(room->number); // 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(room->collision_tile_map, conveyor_belt_direction_); // Crea el manager de puertas (necesita el CollisionMap para sincronizar muros) door_manager_ = std::make_unique(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(tile_map_, tile_set_width_, surface_, bg_color_, conveyor_belt_direction_); tilemap_renderer_->initialize(collision_map_->getCollisionTileMap()); } // Destructor Room::~Room() = default; void Room::initializeRoom(const Data& room) { // Asignar valores a las variables miembro number_ = room.number; bg_color_ = room.bg_color; item_color1_ = room.item_color1; item_color2_ = room.item_color2; upper_room_ = room.upper_room; lower_room_ = room.lower_room; left_room_ = room.left_room; right_room_ = room.right_room; zone_ = room.zone; tile_set_file_ = room.tile_set_file; music_ = room.music; conveyor_belt_direction_ = room.conveyor_belt_direction; tile_map_ = room.tile_map; // Tilemap viene embebido en el YAML surface_ = Resource::Cache::get()->getSurface(room.tile_set_file); tile_set_width_ = surface_->getWidth() / TILE_SIZE; is_paused_ = false; // Crear los enemigos usando el manager for (const auto& enemy_data : room.enemies) { enemy_manager_->addEnemy(Enemy::create(enemy_data)); } // Crear las plataformas usando el manager for (const auto& plat_data : room.platforms) { platform_manager_->addPlatform(std::make_shared(plat_data)); } // Crear los items usando el manager for (const auto& item : room.items) { const SDL_FPoint ITEM_POS = {item.x, item.y}; if (!ItemTracker::get()->hasBeenPicked(room.number, ITEM_POS)) { // Crear una copia local de los datos del item Item::Data item_copy = item; item_copy.color1 = item_color1_; item_copy.color2 = item_color2_; // Crear el objeto Item usando la copia modificada item_manager_->addItem(std::make_shared(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_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_data, ALREADY_OPENED)); } } // Dibuja el mapa en pantalla void Room::renderMap() { tilemap_renderer_->render(); } // Dibuja las plataformas en pantalla void Room::renderPlatforms() { platform_manager_->render(); } // Dibuja los enemigos en pantalla void Room::renderEnemies() { enemy_manager_->render(); } // Dibuja los objetos en pantalla 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() { tilemap_renderer_->redrawMap(collision_map_->getCollisionTileMap()); } // Actualiza animaciones sin mover enemigos (para editor de mapas) void Room::updateEditorMode(float delta_time) { tilemap_renderer_->update(delta_time); enemy_manager_->updateAnimations(delta_time); platform_manager_->updateAnimations(delta_time); item_manager_->update(delta_time); } // Resetea enemigos a posiciones iniciales (para editor de mapas) void Room::resetEnemyPositions(const std::vector& enemy_data) { enemy_manager_->resetPositions(enemy_data); } // Cambia un tile y repinta la celda (para editor) void Room::setTile(int index, int tile_value) { if (index >= 0 && index < static_cast(tile_map_.size())) { tile_map_[index] = tile_value; tilemap_renderer_->setTile(index, tile_value); } } // Cambia colores de items en vivo (para editor) void Room::setItemColors(Uint8 color1, Uint8 color2) { item_color1_ = color1; item_color2_ = color2; auto* item_mgr = item_manager_.get(); for (int i = 0; i < item_mgr->getCount(); ++i) { item_mgr->getItem(i)->setColors(color1, color2); } } // Cambia un collision tile en vivo (para editor) void Room::setCollisionTile(int index, int value) { collision_map_->setCollisionTile(index, value); } // Cambia una conexión de habitación en vivo (para editor) void Room::setConnection(Border border, const std::string& room_name) { switch (border) { case Border::TOP: upper_room_ = room_name; break; case Border::BOTTOM: lower_room_ = room_name; break; case Border::LEFT: left_room_ = room_name; break; case Border::RIGHT: right_room_ = room_name; break; default: break; } } // Cambia el tileset en vivo (para editor) void Room::setTileSet(const std::string& tile_set_file) { tile_set_file_ = tile_set_file; surface_ = Resource::Cache::get()->getSurface(tile_set_file); tile_set_width_ = surface_->getWidth() / TILE_SIZE; tilemap_renderer_ = std::make_unique( tile_map_, tile_set_width_, surface_, bg_color_, conveyor_belt_direction_); tilemap_renderer_->initialize(collision_map_->getCollisionTileMap()); } // Cambia la dirección del conveyor belt en vivo (para editor) void Room::setConveyorBeltDirection(int direction) { conveyor_belt_direction_ = direction; collision_map_->setConveyorBeltDirection(direction); } #endif // Actualiza las variables y objetos de la habitación void Room::update(float delta_time) { // NOLINT(readability-make-member-function-const) if (is_paused_) { // Si está en modo pausa no se actualiza nada return; } // Actualiza los tiles animados usando el renderer tilemap_renderer_->update(delta_time); // Actualiza los enemigos usando el manager enemy_manager_->update(delta_time); // Actualiza las plataformas usando el manager platform_manager_->update(delta_time); // 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 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& { return collision_map_->getTileCollider(); } auto Room::getCollisionTileMap() const -> const std::vector& { return collision_map_->getCollisionTileMap(); } void Room::updateCollisionBorders(const CollisionMap::AdjacentData& adjacent) { collision_map_->updateBorders(adjacent); } // Devuelve la cadena del fichero de la habitación contigua segun el borde auto Room::getRoom(Border border) -> std::string { // NOLINT(readability-convert-member-functions-to-static) switch (border) { case Border::TOP: return upper_room_; case Border::BOTTOM: return lower_room_; case Border::RIGHT: return right_room_; case Border::LEFT: return left_room_; default: return ""; } } auto Room::enemyCollision(SDL_FRect& rect) -> bool { return enemy_manager_->checkCollision(rect); } 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); } // Carga una habitación desde un archivo YAML (delegado a RoomFormat) auto Room::loadYAML(const std::string& file_path, bool verbose) -> Data { // NOLINT(readability-convert-member-functions-to-static) return RoomFormat::loadYAML(file_path, verbose); }