#include "collision_map.hpp" #include // Para std::ranges::any_of #ifdef _DEBUG #include "core/system/debug.hpp" // Para Debug #endif #include "utils/defines.hpp" // Para Collision // Constructor CollisionMap::CollisionMap(std::vector collision_map, int conveyor_belt_direction) : tile_map_(std::move(collision_map)), conveyor_belt_direction_(conveyor_belt_direction) { // Inicializa todas las superficies de colisión initializeSurfaces(); } // Inicializa todas las superficies de colisión void CollisionMap::initializeSurfaces() { setBottomSurfaces(); setTopSurfaces(); setLeftSurfaces(); setRightSurfaces(); setAutoSurfaces(); } // Devuelve el tipo de tile que hay en ese pixel auto CollisionMap::getTile(SDL_FPoint point) const -> Tile { const int POS = ((point.y / TILE_SIZE) * MAP_WIDTH) + (point.x / TILE_SIZE); return getTile(POS); } // Devuelve el tipo de tile que hay en ese indice // Mapeo directo desde collisionmap: 1=WALL, 2=PASSABLE, 3=ANIMATED, 6=KILL auto CollisionMap::getTile(int index) const -> Tile { const bool ON_RANGE = (index > -1) && (index < (int)tile_map_.size()); if (ON_RANGE) { switch (tile_map_[index]) { case 1: return Tile::WALL; case 2: return Tile::PASSABLE; case 3: return Tile::ANIMATED; case 6: return Tile::KILL; default: return Tile::EMPTY; } } return Tile::EMPTY; } // === Queries de colisión === // Comprueba las colisiones con paredes derechas auto CollisionMap::checkRightSurfaces(const SDL_FRect& rect) -> int { for (const auto& s : right_walls_) { if (checkCollision(s, rect)) { return s.x; } } return Collision::NONE; } // Comprueba las colisiones con paredes izquierdas auto CollisionMap::checkLeftSurfaces(const SDL_FRect& rect) -> int { for (const auto& s : left_walls_) { if (checkCollision(s, rect)) { return s.x; } } return Collision::NONE; } // Comprueba las colisiones con techos auto CollisionMap::checkTopSurfaces(const SDL_FRect& rect) -> int { for (const auto& s : top_floors_) { if (checkCollision(s, rect)) { return s.y; } } return Collision::NONE; } // Comprueba las colisiones punto con techos auto CollisionMap::checkTopSurfaces(const SDL_FPoint& p) -> bool { return std::ranges::any_of(top_floors_, [&](const auto& s) { return checkCollision(s, p); }); } // Comprueba las colisiones con suelos auto CollisionMap::checkBottomSurfaces(const SDL_FRect& rect) -> int { for (const auto& s : bottom_floors_) { if (checkCollision(s, rect)) { return s.y; } } return Collision::NONE; } // Comprueba las colisiones con conveyor belts auto CollisionMap::checkAutoSurfaces(const SDL_FRect& rect) -> int { for (const auto& s : conveyor_belt_floors_) { if (checkCollision(s, rect)) { return s.y; } } return Collision::NONE; } // Comprueba las colisiones punto con conveyor belts auto CollisionMap::checkConveyorBelts(const SDL_FPoint& p) -> bool { return std::ranges::any_of(conveyor_belt_floors_, [&](const auto& s) { return checkCollision(s, p); }); } // === Helpers para recopilar tiles === // Helper: recopila tiles inferiores (muros sin muro debajo) auto CollisionMap::collectBottomTiles() -> std::vector { std::vector tile; // Busca todos los tiles de tipo muro que no tengan debajo otro muro // Hay que recorrer la habitación por filas (excepto los de la última fila) for (int i = 0; i < (int)tile_map_.size() - MAP_WIDTH; ++i) { if (getTile(i) == Tile::WALL && getTile(i + MAP_WIDTH) != Tile::WALL) { tile.push_back(i); // Si llega al final de la fila, introduce un separador if (i % MAP_WIDTH == MAP_WIDTH - 1) { tile.push_back(-1); } } } // Añade un terminador tile.push_back(-1); return tile; } // Helper: recopila tiles superiores (muros o pasables sin muro encima) auto CollisionMap::collectTopTiles() -> std::vector { std::vector tile; // Busca todos los tiles de tipo muro o pasable que no tengan encima un muro // Hay que recorrer la habitación por filas (excepto los de la primera fila) for (int i = MAP_WIDTH; i < (int)tile_map_.size(); ++i) { if ((getTile(i) == Tile::WALL || getTile(i) == Tile::PASSABLE) && getTile(i - MAP_WIDTH) != Tile::WALL) { tile.push_back(i); // Si llega al final de la fila, introduce un separador if (i % MAP_WIDTH == MAP_WIDTH - 1) { tile.push_back(-1); } } } // Añade un terminador tile.push_back(-1); return tile; } // Helper: recopila tiles animados (para superficies automaticas/conveyor belts) auto CollisionMap::collectAnimatedTiles() -> std::vector { std::vector tile; // Busca todos los tiles de tipo animado // Hay que recorrer la habitación por filas (excepto los de la primera fila) for (int i = MAP_WIDTH; i < (int)tile_map_.size(); ++i) { if (getTile(i) == Tile::ANIMATED) { tile.push_back(i); // Si llega al final de la fila, introduce un separador if (i % MAP_WIDTH == MAP_WIDTH - 1) { tile.push_back(-1); } } } // Añade un terminador si hay tiles if (!tile.empty()) { tile.push_back(-1); } return tile; } // Helper: construye lineas horizontales a partir de tiles consecutivos void CollisionMap::buildHorizontalLines(const std::vector& tiles, std::vector& lines, bool is_bottom_surface) { if (tiles.size() <= 1) { return; } int i = 0; while (i < (int)tiles.size() - 1) { LineHorizontal line; line.x1 = (tiles[i] % MAP_WIDTH) * TILE_SIZE; // Calcula Y segun si es superficie inferior o superior if (is_bottom_surface) { line.y = ((tiles[i] / MAP_WIDTH) * TILE_SIZE) + TILE_SIZE - 1; } else { line.y = (tiles[i] / MAP_WIDTH) * TILE_SIZE; } int last_one = i; i++; // Encuentra tiles consecutivos if (i < (int)tiles.size()) { while (tiles[i] == tiles[i - 1] + 1) { last_one = i; i++; if (i >= (int)tiles.size()) { break; } } } line.x2 = ((tiles[last_one] % MAP_WIDTH) * TILE_SIZE) + TILE_SIZE - 1; lines.push_back(line); // Salta separadores if (i < (int)tiles.size() && tiles[i] == -1) { i++; } } } // === Métodos de generación de geometría === // Calcula las superficies inferiores void CollisionMap::setBottomSurfaces() { std::vector tile = collectBottomTiles(); buildHorizontalLines(tile, bottom_floors_, true); } // Calcula las superficies superiores void CollisionMap::setTopSurfaces() { std::vector tile = collectTopTiles(); buildHorizontalLines(tile, top_floors_, false); } // Calcula las superficies laterales izquierdas void CollisionMap::setLeftSurfaces() { std::vector tile; // Busca todos los tiles de tipo muro que no tienen a su izquierda un tile de tipo muro // Hay que recorrer la habitación por columnas (excepto los de la primera columna) for (int i = 1; i < MAP_WIDTH; ++i) { for (int j = 0; j < MAP_HEIGHT; ++j) { const int POS = ((j * MAP_WIDTH) + i); if (getTile(POS) == Tile::WALL && getTile(POS - 1) != Tile::WALL) { tile.push_back(POS); } } } // Añade un terminador tile.push_back(-1); // Recorre el vector de tiles buscando tiles consecutivos // (Los tiles de la misma columna, la diferencia entre ellos es de mapWidth) // para localizar las superficies if ((int)tile.size() > 1) { int i = 0; do { LineVertical line; line.x = (tile[i] % MAP_WIDTH) * TILE_SIZE; line.y1 = ((tile[i] / MAP_WIDTH) * TILE_SIZE); while (tile[i] + MAP_WIDTH == tile[i + 1]) { if (i == (int)tile.size() - 1) { break; } i++; } line.y2 = ((tile[i] / MAP_WIDTH) * TILE_SIZE) + TILE_SIZE - 1; left_walls_.push_back(line); i++; } while (i < (int)tile.size() - 1); } } // Calcula las superficies laterales derechas void CollisionMap::setRightSurfaces() { std::vector tile; // Busca todos los tiles de tipo muro que no tienen a su derecha un tile de tipo muro // Hay que recorrer la habitación por columnas (excepto los de la última columna) for (int i = 0; i < MAP_WIDTH - 1; ++i) { for (int j = 0; j < MAP_HEIGHT; ++j) { const int POS = ((j * MAP_WIDTH) + i); if (getTile(POS) == Tile::WALL && getTile(POS + 1) != Tile::WALL) { tile.push_back(POS); } } } // Añade un terminador tile.push_back(-1); // Recorre el vector de tiles buscando tiles consecutivos // (Los tiles de la misma columna, la diferencia entre ellos es de mapWidth) // para localizar las superficies if ((int)tile.size() > 1) { int i = 0; do { LineVertical line; line.x = ((tile[i] % MAP_WIDTH) * TILE_SIZE) + TILE_SIZE - 1; line.y1 = ((tile[i] / MAP_WIDTH) * TILE_SIZE); while (tile[i] + MAP_WIDTH == tile[i + 1]) { if (i == (int)tile.size() - 1) { break; } i++; } line.y2 = ((tile[i] / MAP_WIDTH) * TILE_SIZE) + TILE_SIZE - 1; right_walls_.push_back(line); i++; } while (i < (int)tile.size() - 1); } } // Calcula las superficies automaticas (conveyor belts) void CollisionMap::setAutoSurfaces() { std::vector tile = collectAnimatedTiles(); buildHorizontalLines(tile, conveyor_belt_floors_, false); }