Files
projecte_2026/source/game/gameplay/tilemap_renderer.cpp

179 lines
6.6 KiB
C++

#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<int> tile_map, int tile_set_width, std::shared_ptr<Surface> 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<Surface>(PlayArea::WIDTH, PlayArea::HEIGHT);
}
void TilemapRenderer::initialize(const std::vector<int>& 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<int>& collision_tile_map) {
fillMapTexture(collision_tile_map);
}
void TilemapRenderer::setTile(int index, int tile_value) {
if (index < 0 || index >= static_cast<int>(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<float>(col * TILE_SIZE), .y = static_cast<float>(row * TILE_SIZE), .w = static_cast<float>(TILE_SIZE), .h = static_cast<float>(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<float>((tile_value % tile_set_width_) * TILE_SIZE),
.y = static_cast<float>((tile_value / tile_set_width_) * TILE_SIZE),
.w = static_cast<float>(TILE_SIZE),
.h = static_cast<float>(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<int>& 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<int>(collision_tile_map.size())) { continue; }
int tile = collision_tile_map[index];
if (tile <= 0) { continue; }
SDL_FRect clip = {
.x = static_cast<float>(tile * Tile::SIZE),
.y = 0,
.w = static_cast<float>(Tile::SIZE),
.h = static_cast<float>(Tile::SIZE)};
collision_surface->render(x * Tile::SIZE, y * Tile::SIZE, &clip);
}
}
}
#endif
// Pinta el mapa estático
void TilemapRenderer::fillMapTexture(const std::vector<int>& 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<int>& collision_tile_map) {
for (int i = 0; i < static_cast<int>(tile_map_.size()); ++i) {
// Un tile es animado si su valor en el collision tilemap es COLLISION_ANIMATED (6)
if (i < static_cast<int>(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<Sprite>(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<int>(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();
}
}