Files
jdd_opendingux/source/game/gameplay/room.cpp
Sergio e97c951d0d Fase 4: Refactorización de Room - Extracción del sistema de parseo de archivos
## Cambios principales

### Nuevo componente: RoomLoader
- **room_loader.hpp/cpp**: Nueva clase estática para parseo de archivos
- Responsabilidades extraídas de Room:
  - Carga de archivos .room (loadRoomFile)
  - Carga de archivos .tmx de tilemap (loadRoomTileFile)
  - Parseo de claves y valores (parseKeyValue)
  - Asignación de valores a estructuras (setRoom, setEnemy, setItem)
  - Carga de bloques [enemy] y [item] (loadEnemyFromFile, loadItemFromFile)
  - Log de parámetros desconocidos (logUnknownParameter)

### Modificaciones en Room
- **room.hpp**:
  - Eliminados 9 métodos estáticos privados de parseo
  - Mantenidos 2 métodos públicos (loadRoomFile, loadRoomTileFile) que ahora delegan a RoomLoader
  - Añadido comentario indicando delegación a RoomLoader

- **room.cpp**:
  - Eliminadas ~285 líneas de código de parseo de archivos
  - Añadido #include "room_loader.hpp"
  - Implementaciones de loadRoomFile y loadRoomTileFile ahora son simples delegaciones:
    * `return RoomLoader::loadRoomFile(file_path, verbose);`
    * `return RoomLoader::loadRoomTileFile(file_path, verbose);`
  - Archivo reducido de 554 líneas a 277 líneas (50% de reducción)

### Build system
- **CMakeLists.txt**: Añadido room_loader.cpp a las fuentes del proyecto

## Diseño de RoomLoader

RoomLoader es una clase utility con solo métodos estáticos (no instanciable):
- Constructor/destructor eliminados
- No tiene estado (stateless)
- Todos los métodos son estáticos
- Encapsula toda la lógica de I/O y parseo de archivos de configuración

## Métricas
- **Código eliminado de Room**: ~285 líneas de lógica de parseo
- **Nuevo RoomLoader**: 300 líneas (room_loader.cpp)
- **Reducción en room.cpp**: De 554 a 277 líneas (50% de reducción)
- **Room.hpp**: Simplificado significativamente (9 declaraciones privadas eliminadas)

## Verificación
-  Compilación exitosa sin errores
-  Juego inicia y carga todos los archivos correctamente
-  clang-tidy: 1 warning de complejidad cognitiva (código heredado, no modificado)
-  cppcheck: Sin issues

## Progreso total de refactorización

Después de 4 fases, Room ha sido drásticamente simplificado:
- **Phase 1**: Gestión de entidades → EnemyManager & ItemManager
- **Phase 2**: Sistema de colisiones → CollisionMap (~465 líneas)
- **Phase 3**: Renderizado de tilemap → TilemapRenderer (~95 líneas)
- **Phase 4**: Parseo de archivos → RoomLoader (~285 líneas)

**Total eliminado de Room**: ~845+ líneas
**Reducción tamaño archivo**: De 1097 líneas originales a 277 líneas (75% de reducción)
**Nuevas clases especializadas**: 6 (EnemyManager, ItemManager, CollisionMap, TilemapRenderer, RoomLoader)

## Próximos pasos
- Fase 5: Limpieza final y optimización de Room como coordinador ligero

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-13 09:26:03 +01:00

278 lines
9.7 KiB
C++

#include "game/gameplay/room.hpp"
#include <algorithm> // Para std::ranges::any_of
#include <exception> // Para exception
#include <fstream> // Para basic_ostream, operator<<, basic_istream
#include <iostream> // Para cout, cerr
#include <sstream> // Para basic_stringstream
#include <utility>
#include "core/audio/audio.hpp" // Para Audio
#include "core/rendering/screen.hpp" // Para Screen
#include "core/rendering/surface.hpp" // Para Surface
#include "core/rendering/surface_sprite.hpp" // Para SSprite
#include "core/resources/resource_cache.hpp" // Para Resource
#include "core/resources/resource_helper.hpp" // Para ResourceHelper
#include "core/system/debug.hpp" // Para Debug
#include "game/gameplay/collision_map.hpp" // Para CollisionMap
#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/room_loader.hpp" // Para RoomLoader
#include "game/gameplay/tilemap_renderer.hpp" // Para TilemapRenderer
#include "game/gameplay/scoreboard.hpp" // Para Scoreboard::Data
#include "game/options.hpp" // Para Options, OptionsStats, options
#include "utils/defines.hpp" // Para BLOCK, PLAY_AREA_HEIGHT, PLAY_AREA_WIDTH
#include "utils/utils.hpp" // Para LineHorizontal, LineDiagonal, LineVertical
// Constructor
Room::Room(const std::string& room_path, std::shared_ptr<Scoreboard::Data> data)
: data_(std::move(std::move(data))) {
auto room = Resource::Cache::get()->getRoom(room_path);
// Crea los managers de enemigos e items
enemy_manager_ = std::make_unique<EnemyManager>();
item_manager_ = std::make_unique<ItemManager>(room->name, data_);
initializeRoom(*room);
// Crea el mapa de colisiones (necesita tile_map_, tile_set_width_, conveyor_belt_direction_)
collision_map_ = std::make_unique<CollisionMap>(tile_map_, tile_set_width_, conveyor_belt_direction_);
openTheJail(); // Abre la Jail si se da el caso
// Crea el renderizador del tilemap (necesita tile_map_, tile_set_width_, surface_, bg_color_, conveyor_belt_direction_)
tilemap_renderer_ = std::make_unique<TilemapRenderer>(tile_map_, tile_set_width_, surface_, bg_color_,
conveyor_belt_direction_);
tilemap_renderer_->initialize(collision_map_.get()); // Inicializa (crea map_surface, pinta tiles, busca animados)
Screen::get()->setBorderColor(stringToColor(border_color_)); // Establece el color del borde
}
// Destructor
Room::~Room() = default;
void Room::initializeRoom(const Data& room) {
// Asignar valores a las variables miembro
number_ = room.number;
name_ = room.name;
bg_color_ = room.bg_color;
border_color_ = room.border_color;
item_color1_ = room.item_color1.empty() ? "yellow" : room.item_color1;
item_color2_ = room.item_color2.empty() ? "magenta" : room.item_color2;
upper_room_ = room.upper_room;
lower_room_ = room.lower_room;
left_room_ = room.left_room;
right_room_ = room.right_room;
tile_set_file_ = room.tile_set_file;
tile_map_file_ = room.tile_map_file;
conveyor_belt_direction_ = room.conveyor_belt_direction;
tile_map_ = Resource::Cache::get()->getTileMap(room.tile_map_file);
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(std::make_shared<Enemy>(enemy_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.name, ITEM_POS)) {
// Crear una copia local de los datos del item
Item::Data item_copy = item;
item_copy.color1 = stringToColor(item_color1_);
item_copy.color2 = stringToColor(item_color2_);
// Crear el objeto Item usando la copia modificada
item_manager_->addItem(std::make_shared<Item>(item_copy));
}
}
}
// Abre la jail para poder entrar
void Room::openTheJail() {
if (data_->jail_is_open && name_ == "THE JAIL") {
// Elimina el último enemigo (Bry debe ser el último enemigo definido en el fichero)
if (!enemy_manager_->isEmpty()) {
enemy_manager_->removeLastEnemy();
}
// Abre las puertas
constexpr int TILE_A = 16 + (13 * 32);
constexpr int TILE_B = 16 + (14 * 32);
if (TILE_A < tile_map_.size()) {
tile_map_[TILE_A] = -1;
}
if (TILE_B < tile_map_.size()) {
tile_map_[TILE_B] = -1;
}
}
}
// Dibuja el mapa en pantalla
void Room::renderMap() {
tilemap_renderer_->render();
}
// Dibuja los enemigos en pantalla
void Room::renderEnemies() {
enemy_manager_->render();
}
// Dibuja los objetos en pantalla
void Room::renderItems() {
item_manager_->render();
}
// Actualiza las variables y objetos de la habitación
void Room::update(float delta_time) {
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 los items usando el manager
item_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);
}
// Devuelve la cadena del fichero de la habitación contigua segun el borde
auto Room::getRoom(Border border) -> std::string {
switch (border) {
case Border::TOP:
return upper_room_;
break;
case Border::BOTTOM:
return lower_room_;
break;
case Border::RIGHT:
return right_room_;
break;
case Border::LEFT:
return left_room_;
break;
default:
break;
}
return "";
}
// Devuelve el tipo de tile que hay en ese pixel
auto Room::getTile(SDL_FPoint point) -> Tile {
// Delega a CollisionMap y convierte el resultado
const auto collision_tile = collision_map_->getTile(point);
return static_cast<Tile>(collision_tile);
}
// Devuelve el tipo de tile en un índice del tilemap
auto Room::getTile(int index) -> Tile {
// Delega a CollisionMap y convierte el resultado
const auto collision_tile = collision_map_->getTile(index);
return static_cast<Tile>(collision_tile);
}
// Indica si hay colision con un enemigo a partir de un rectangulo
auto Room::enemyCollision(SDL_FRect& rect) -> bool {
return enemy_manager_->checkCollision(rect);
}
// Indica si hay colision con un objeto a partir de un rectangulo
auto Room::itemCollision(SDL_FRect& rect) -> bool {
return item_manager_->checkCollision(rect);
}
// Obten la coordenada de la cuesta a partir de un punto perteneciente a ese tile
auto Room::getSlopeHeight(SDL_FPoint p, Tile slope) -> int {
// Delega a CollisionMap (método estático)
const auto collision_tile = static_cast<CollisionMap::Tile>(slope);
return CollisionMap::getSlopeHeight(p, collision_tile);
}
// === Métodos de colisión (delegados a CollisionMap) ===
// Comprueba las colisiones con paredes derechas
auto Room::checkRightSurfaces(SDL_FRect& rect) -> int {
return collision_map_->checkRightSurfaces(rect);
}
// Comprueba las colisiones con paredes izquierdas
auto Room::checkLeftSurfaces(SDL_FRect& rect) -> int {
return collision_map_->checkLeftSurfaces(rect);
}
// Comprueba las colisiones con techos
auto Room::checkTopSurfaces(SDL_FRect& rect) -> int {
return collision_map_->checkTopSurfaces(rect);
}
// Comprueba las colisiones punto con techos
auto Room::checkTopSurfaces(SDL_FPoint& p) -> bool {
return collision_map_->checkTopSurfaces(p);
}
// Comprueba las colisiones con suelos
auto Room::checkBottomSurfaces(SDL_FRect& rect) -> int {
return collision_map_->checkBottomSurfaces(rect);
}
// Comprueba las colisiones con conveyor belts
auto Room::checkAutoSurfaces(SDL_FRect& rect) -> int {
return collision_map_->checkAutoSurfaces(rect);
}
// Comprueba las colisiones punto con conveyor belts
auto Room::checkConveyorBelts(SDL_FPoint& p) -> bool {
return collision_map_->checkConveyorBelts(p);
}
// Comprueba las colisiones línea con rampas izquierdas
auto Room::checkLeftSlopes(const LineVertical& line) -> int {
return collision_map_->checkLeftSlopes(line);
}
// Comprueba las colisiones punto con rampas izquierdas
auto Room::checkLeftSlopes(SDL_FPoint& p) -> bool {
return collision_map_->checkLeftSlopes(p);
}
// Comprueba las colisiones línea con rampas derechas
auto Room::checkRightSlopes(const LineVertical& line) -> int {
return collision_map_->checkRightSlopes(line);
}
// Comprueba las colisiones punto con rampas derechas
auto Room::checkRightSlopes(SDL_FPoint& p) -> bool {
return collision_map_->checkRightSlopes(p);
}
// Carga las variables desde un fichero de mapa (delegado a RoomLoader)
auto Room::loadRoomFile(const std::string& file_path, bool verbose) -> Data {
return RoomLoader::loadRoomFile(file_path, verbose);
}
// Carga las variables y texturas desde un fichero de mapa de tiles (delegado a RoomLoader)
auto Room::loadRoomTileFile(const std::string& file_path, bool verbose) -> std::vector<int> {
return RoomLoader::loadRoomTileFile(file_path, verbose);
}