#include "tilemap_renderer.hpp" #include "core/rendering/screen.hpp" #include "core/rendering/sprite/sprite.hpp" #include "core/rendering/surface.hpp" #ifdef _DEBUG #include "core/resources/resource_cache.hpp" // Para Resource::Cache (collision.gif) #include "core/system/debug.hpp" #endif TilemapRenderer::TilemapRenderer(std::vector tile_map, int tile_set_width, std::shared_ptr tileset_surface, Uint8 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_(bg_color), conveyor_belt_direction_(conveyor_belt_direction) { map_surface_ = std::make_shared(PlayArea::WIDTH, PlayArea::HEIGHT); } void TilemapRenderer::initialize(const std::vector& collision_tile_map) { setAnimatedTiles(collision_tile_map); fillMapTexture(collision_tile_map); } void TilemapRenderer::update(float delta_time) { if (is_paused_) { return; } time_accumulator_ += delta_time; updateAnimatedTiles(); } void TilemapRenderer::render() { SDL_FRect dest = {.x = 0, .y = 0, .w = PlayArea::WIDTH, .h = PlayArea::HEIGHT}; map_surface_->render(nullptr, &dest); #ifdef _DEBUG if (!Debug::get()->isEnabled()) { renderAnimatedTiles(); } #else renderAnimatedTiles(); #endif } #ifdef _DEBUG // Redibuja el tilemap (para actualizar modo debug: pinta collision tilemap o tiles normales) void TilemapRenderer::redrawMap(const std::vector& collision_tile_map) { fillMapTexture(collision_tile_map); } void TilemapRenderer::setTile(int index, int tile_value) { if (index < 0 || index >= static_cast(tile_map_.size())) { return; } tile_map_[index] = tile_value; int col = index % MAP_WIDTH; int row = index / MAP_WIDTH; auto previous_renderer = Screen::get()->getRendererSurface(); Screen::get()->setRendererSurface(map_surface_); SDL_FRect cell = {.x = static_cast(col * TILE_SIZE), .y = static_cast(row * TILE_SIZE), .w = static_cast(TILE_SIZE), .h = static_cast(TILE_SIZE)}; map_surface_->fillRect(&cell, bg_color_); if (tile_value > -1) { const bool IS_ANIMATED = (tile_value >= 18 * tile_set_width_) && (tile_value < 19 * tile_set_width_); if (!IS_ANIMATED) { SDL_FRect clip = {.x = static_cast((tile_value % tile_set_width_) * TILE_SIZE), .y = static_cast((tile_value / tile_set_width_) * TILE_SIZE), .w = static_cast(TILE_SIZE), .h = static_cast(TILE_SIZE)}; tileset_surface_->render(col * TILE_SIZE, row * TILE_SIZE, &clip); } } Screen::get()->setRendererSurface(previous_renderer); } // Renderiza el collision tilemap superpuesto (modo debug) static void renderDebugCollisionTilemap(const std::vector& collision_tile_map) { auto collision_surface = Resource::Cache::get()->getSurface("collision.gif"); if (!collision_surface) { return; } for (int y = 0; y < Map::HEIGHT; ++y) { for (int x = 0; x < Map::WIDTH; ++x) { int index = (y * Map::WIDTH) + x; if (index >= static_cast(collision_tile_map.size())) { continue; } int tile = collision_tile_map[index]; if (tile <= 0) { continue; } SDL_FRect clip = { .x = static_cast(tile * Tile::SIZE), .y = 0, .w = static_cast(Tile::SIZE), .h = static_cast(Tile::SIZE)}; collision_surface->render(x * Tile::SIZE, y * Tile::SIZE, &clip); } } } #endif // Pinta el mapa estático void TilemapRenderer::fillMapTexture(const std::vector& collision_tile_map) { auto previous_renderer = Screen::get()->getRendererSurface(); Screen::get()->setRendererSurface(map_surface_); map_surface_->clear(bg_color_); SDL_FRect clip = {.x = 0, .y = 0, .w = TILE_SIZE, .h = TILE_SIZE}; for (int y = 0; y < MAP_HEIGHT; ++y) { for (int x = 0; x < MAP_WIDTH; ++x) { const int INDEX = (y * MAP_WIDTH) + x; // Los tiles animados (fila 18 del tileset) no se pintan en la textura estática const bool IS_ANIMATED = (tile_map_[INDEX] >= 18 * tile_set_width_) && (tile_map_[INDEX] < 19 * tile_set_width_); const bool HAS_TILE = tile_map_[INDEX] > -1; if (HAS_TILE && !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 // En modo debug, pintar el collision tilemap en vez de las líneas de colisión antiguas if (Debug::get()->isEnabled()) { renderDebugCollisionTilemap(collision_tile_map); } #endif Screen::get()->setRendererSurface(previous_renderer); } // Localiza tiles animados (conveyor belts) usando el collision_tile_map void TilemapRenderer::setAnimatedTiles(const std::vector& collision_tile_map) { for (int i = 0; i < static_cast(tile_map_.size()); ++i) { // Un tile es animado si su valor en el collision tilemap es COLLISION_ANIMATED (6) if (i < static_cast(collision_tile_map.size()) && collision_tile_map[i] == COLLISION_ANIMATED) { const int X = (i % MAP_WIDTH) * TILE_SIZE; const int Y = (i / MAP_WIDTH) * TILE_SIZE; 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); } } } void TilemapRenderer::updateAnimatedTiles() { const int NUM_FRAMES = 4; const int CURRENT_FRAME = static_cast(time_accumulator_ / CONVEYOR_FRAME_DURATION) % NUM_FRAMES; 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); } } void TilemapRenderer::renderAnimatedTiles() { for (const auto& a : animated_tiles_) { a.sprite->render(); } }