#include "tilemap_renderer.hpp" #include "core/rendering/screen.hpp" #include "core/rendering/surface.hpp" #include "core/rendering/surface_sprite.hpp" #ifdef _DEBUG #include "core/system/debug.hpp" #endif #include "game/gameplay/collision_map.hpp" #include "utils/color.hpp" #include "utils/utils.hpp" // Constructor TilemapRenderer::TilemapRenderer(std::vector tile_map, int tile_set_width, std::shared_ptr tileset_surface, std::string bg_color, int conveyor_belt_direction) : tile_map_(std::move(tile_map)), tile_set_width_(tile_set_width), tileset_surface_(std::move(tileset_surface)), bg_color_(std::move(bg_color)), conveyor_belt_direction_(conveyor_belt_direction) { // Crear la surface del mapa map_surface_ = std::make_shared(PlayArea::WIDTH, PlayArea::HEIGHT); } // Inicializa el renderizador void TilemapRenderer::initialize(const CollisionMap* collision_map) { setAnimatedTiles(collision_map); fillMapTexture(collision_map); } // Actualiza las animaciones de tiles void TilemapRenderer::update(float delta_time) { if (is_paused_) { return; } // Actualiza el acumulador de tiempo time_accumulator_ += delta_time; // Actualiza los tiles animados updateAnimatedTiles(); } // Renderiza el mapa completo en pantalla void TilemapRenderer::render() { // Dibuja la textura con el mapa en pantalla SDL_FRect dest = {0, 0, PlayArea::WIDTH, PlayArea::HEIGHT}; map_surface_->render(nullptr, &dest); // Dibuja los tiles animados #ifdef _DEBUG if (!Debug::get()->isEnabled()) { renderAnimatedTiles(); } #else renderAnimatedTiles(); #endif } #ifdef _DEBUG // Renderiza las superficies de colisión en modo debug (función helper estática) static void renderDebugCollisionSurfaces(const CollisionMap* collision_map) { auto surface = Screen::get()->getRendererSurface(); // BottomSurfaces for (auto l : collision_map->getBottomFloors()) { surface->drawLine(l.x1, l.y, l.x2, l.y, Color::index(Color::Cpc::BLUE)); } // TopSurfaces for (auto l : collision_map->getTopFloors()) { surface->drawLine(l.x1, l.y, l.x2, l.y, Color::index(Color::Cpc::RED)); } // LeftSurfaces for (auto l : collision_map->getLeftWalls()) { surface->drawLine(l.x, l.y1, l.x, l.y2, Color::index(Color::Cpc::GREEN)); } // RightSurfaces for (auto l : collision_map->getRightWalls()) { surface->drawLine(l.x, l.y1, l.x, l.y2, Color::index(Color::Cpc::MAGENTA)); } // AutoSurfaces (Conveyor Belts) for (auto l : collision_map->getConveyorBeltFloors()) { surface->drawLine(l.x1, l.y, l.x2, l.y, Color::index(Color::Cpc::WHITE)); } } // Redibuja el tilemap (para actualizar modo debug) void TilemapRenderer::redrawMap(const CollisionMap* collision_map) { fillMapTexture(collision_map); } #endif // Pinta el mapa estático y debug lines void TilemapRenderer::fillMapTexture(const CollisionMap* collision_map) { const Uint8 COLOR = stringToColor(bg_color_); auto previous_renderer = Screen::get()->getRendererSurface(); Screen::get()->setRendererSurface(map_surface_); map_surface_->clear(COLOR); // Los tileSetFiles son de 20x20 tiles. El primer tile es el 0. Cuentan hacia la derecha y hacia abajo SDL_FRect clip = {0, 0, TILE_SIZE, TILE_SIZE}; for (int y = 0; y < MAP_HEIGHT; ++y) { for (int x = 0; x < MAP_WIDTH; ++x) { // Tiled pone los tiles vacios del mapa como cero y empieza a contar de 1 a n. // Al cargar el mapa en memoria, se resta uno, por tanto los tiles vacios son -1 // Tampoco hay que dibujar los tiles animados (se dibujan en renderAnimatedTiles) const int INDEX = (y * MAP_WIDTH) + x; const bool IS_ANIMATED = collision_map->getTile(INDEX) == CollisionMap::Tile::ANIMATED; const bool IS_VISIBLE = tile_map_[INDEX] > -1; if (IS_VISIBLE && !IS_ANIMATED) { clip.x = (tile_map_[INDEX] % tile_set_width_) * TILE_SIZE; clip.y = (tile_map_[INDEX] / tile_set_width_) * TILE_SIZE; #ifdef _DEBUG if (!Debug::get()->isEnabled()) { tileset_surface_->render(x * TILE_SIZE, y * TILE_SIZE, &clip); } #else tileset_surface_->render(x * TILE_SIZE, y * TILE_SIZE, &clip); #endif } } } #ifdef _DEBUG // Pinta las superficies en el modo debug if (Debug::get()->isEnabled()) { renderDebugCollisionSurfaces(collision_map); } #endif // _DEBUG Screen::get()->setRendererSurface(previous_renderer); } // Localiza todos los tiles animados void TilemapRenderer::setAnimatedTiles(const CollisionMap* collision_map) { // Recorre la habitación entera por filas buscando tiles de tipo t_animated for (int i = 0; i < (int)tile_map_.size(); ++i) { const auto TILE_TYPE = collision_map->getTile(i); if (TILE_TYPE == CollisionMap::Tile::ANIMATED) { // La i es la ubicación const int X = (i % MAP_WIDTH) * TILE_SIZE; const int Y = (i / MAP_WIDTH) * TILE_SIZE; // TileMap[i] es el tile a poner const int XC = (tile_map_[i] % tile_set_width_) * TILE_SIZE; const int YC = (tile_map_[i] / tile_set_width_) * TILE_SIZE; AnimatedTile at; at.sprite = std::make_shared(tileset_surface_, X, Y, 8, 8); at.sprite->setClip(XC, YC, 8, 8); at.x_orig = XC; animated_tiles_.push_back(at); } } } // Actualiza tiles animados void TilemapRenderer::updateAnimatedTiles() { const int NUM_FRAMES = 4; // Calcular frame actual basado en tiempo const int CURRENT_FRAME = static_cast(time_accumulator_ / CONVEYOR_FRAME_DURATION) % NUM_FRAMES; // Calcular offset basado en dirección int offset = 0; if (conveyor_belt_direction_ == -1) { offset = CURRENT_FRAME * TILE_SIZE; } else { offset = (NUM_FRAMES - 1 - CURRENT_FRAME) * TILE_SIZE; } for (auto& a : animated_tiles_) { SDL_FRect rect = a.sprite->getClip(); rect.x = a.x_orig + offset; a.sprite->setClip(rect); } } // Renderiza tiles animados void TilemapRenderer::renderAnimatedTiles() { for (const auto& a : animated_tiles_) { a.sprite->render(); } }