#include "room.h" #include // Para SDL_BLENDMODE_BLEND #include // Para SDL_GetError #include // Para SDL_PIXELFORMAT_RGBA8888 #include // Para rand #include // Para basic_ostream, operator<<, basic_ist... #include // Para cout #include // Para basic_stringstream #include "asset.h" // Para Asset #include "defines.h" // Para BLOCK, PLAY_AREA_HEIGHT, PLAY_AREA_W... #include "debug.h" // Para Debug #include "item_tracker.h" // Para ItemTracker #include "jail_audio.h" // Para JA_DeleteSound, JA_LoadSound, JA_Pla... #include "screen.h" // Para Screen #include "sprite.h" // Para Sprite #include "texture.h" // Para Texture #include "options.h" #include "utils.h" // Para stringToBool, stringToColor #include "resource.h" // Carga las variables y texturas desde un fichero de mapa de tiles std::vector loadRoomTileFile(const std::string &file_path, bool verbose) { std::vector tileMapFile; const std::string filename = file_path.substr(file_path.find_last_of("\\/") + 1); std::string line; std::ifstream file(file_path); // El fichero se puede abrir if (file.good()) { // Procesa el fichero linea a linea while (std::getline(file, line)) { // Lee el fichero linea a linea if (line.find("data encoding") != std::string::npos) { // Lee la primera linea std::getline(file, line); while (line != "") { // Procesa lineas mientras haya std::stringstream ss(line); std::string tmp; while (getline(ss, tmp, ',')) { tileMapFile.push_back(std::stoi(tmp) - 1); } // Lee la siguiente linea std::getline(file, line); } } } // Cierra el fichero if (verbose) { std::cout << "TileMap loaded: " << filename.c_str() << std::endl; } file.close(); } else { // El fichero no se puede abrir if (verbose) { std::cout << "Warning: Unable to open " << filename.c_str() << " file" << std::endl; } } return tileMapFile; } // Carga las variables desde un fichero de mapa RoomData loadRoomFile(const std::string &file_path, bool verbose) { RoomData room; room.item_color1 = "yellow"; room.item_color2 = "magenta"; room.auto_surface_direction = 1; const std::string fileName = file_path.substr(file_path.find_last_of("\\/") + 1); room.number = fileName.substr(0, fileName.find_last_of(".")); std::string line; std::ifstream file(file_path); // El fichero se puede abrir if (file.good()) { // Procesa el fichero linea a linea while (std::getline(file, line)) { // Si la linea contiene el texto [enemy] se realiza el proceso de carga de un enemigo if (line == "[enemy]") { EnemyData enemy; enemy.flip = false; enemy.mirror = false; enemy.frame = -1; do { std::getline(file, line); // Encuentra la posición del caracter '=' int pos = line.find("="); // Procesa las dos subcadenas std::string key = line.substr(0, pos); std::string value = line.substr(pos + 1, line.length()); if (!setEnemy(&enemy, key, value)) if (verbose) { std::cout << "Warning: file " << fileName.c_str() << "\n, unknown parameter \"" << line.substr(0, pos).c_str() << "\"" << std::endl; } } while (line != "[/enemy]"); // Añade el enemigo al vector de enemigos room.enemies.push_back(enemy); } // Si la linea contiene el texto [item] se realiza el proceso de carga de un item else if (line == "[item]") { ItemData item; item.counter = 0; item.color1 = stringToColor(Palette::ZXSPECTRUM, "yellow"); item.color2 = stringToColor(Palette::ZXSPECTRUM, "magenta"); do { std::getline(file, line); // Encuentra la posición del caracter '=' int pos = line.find("="); // Procesa las dos subcadenas std::string key = line.substr(0, pos); std::string value = line.substr(pos + 1, line.length()); if (!setItem(&item, key, value)) { if (verbose) { std::cout << "Warning: file " << fileName.c_str() << "\n, unknown parameter \"" << line.substr(0, pos).c_str() << "\"" << std::endl; } } } while (line != "[/item]"); room.items.push_back(item); } // En caso contrario se parsea el fichero para buscar las variables y los valores else { // Encuentra la posición del caracter '=' int pos = line.find("="); // Procesa las dos subcadenas std::string key = line.substr(0, pos); std::string value = line.substr(pos + 1, line.length()); if (!setRoom(&room, key, value)) { if (verbose) { std::cout << "Warning: file " << fileName.c_str() << "\n, unknown parameter \"" << line.substr(0, pos).c_str() << "\"" << std::endl; } } } } // Cierra el fichero if (verbose) { std::cout << "Room loaded: " << fileName.c_str() << std::endl; } file.close(); } // El fichero no se puede abrir else { { std::cout << "Warning: Unable to open " << fileName.c_str() << " file" << std::endl; } } return room; } // Asigna variables a una estructura RoomData bool setRoom(RoomData *room, const std::string &key, const std::string &value) { // Indicador de éxito en la asignación bool success = true; try { if (key == "tileMapFile") { room->tile_map_file = value; } else if (key == "name") { room->name = value; } else if (key == "bgColor") { room->bg_color = value; } else if (key == "border") { room->border_color = value; } else if (key == "itemColor1") { room->item_color1 = value; } else if (key == "itemColor2") { room->item_color2 = value; } else if (key == "tileSetFile") { room->tile_set_file = value; } else if (key == "roomUp") { room->room_top = value; } else if (key == "roomDown") { room->room_bottom = value; } else if (key == "roomLeft") { room->room_left = value; } else if (key == "roomRight") { room->room_right = value; } else if (key == "autoSurface") { room->auto_surface_direction = (value == "right") ? 1 : -1; } else if (key == "" || key.substr(0, 1) == "#") { // No se realiza ninguna acción para estas claves } else { success = false; } } catch (const std::exception &e) { std::cerr << "Error al asignar la clave " << key << " con valor " << value << ": " << e.what() << std::endl; success = false; } return success; } // Asigna variables a una estructura EnemyData bool setEnemy(EnemyData *enemy, const std::string &key, const std::string &value) { // Indicador de éxito en la asignación bool success = true; try { if (key == "tileSetFile") { enemy->texture_path = value; } else if (key == "animation") { enemy->animation_path = value; } else if (key == "width") { enemy->w = std::stoi(value); } else if (key == "height") { enemy->h = std::stoi(value); } else if (key == "x") { enemy->x = std::stof(value) * BLOCK; } else if (key == "y") { enemy->y = std::stof(value) * BLOCK; } else if (key == "vx") { enemy->vx = std::stof(value); } else if (key == "vy") { enemy->vy = std::stof(value); } else if (key == "x1") { enemy->x1 = std::stoi(value) * BLOCK; } else if (key == "x2") { enemy->x2 = std::stoi(value) * BLOCK; } else if (key == "y1") { enemy->y1 = std::stoi(value) * BLOCK; } else if (key == "y2") { enemy->y2 = std::stoi(value) * BLOCK; } else if (key == "flip") { enemy->flip = stringToBool(value); } else if (key == "mirror") { enemy->mirror = stringToBool(value); } else if (key == "color") { enemy->color = value; } else if (key == "frame") { enemy->frame = std::stoi(value); } else if (key == "[/enemy]" || key == "tileSetFile" || key.substr(0, 1) == "#") { // No se realiza ninguna acción para estas claves } else { success = false; } } catch (const std::exception &e) { std::cerr << "Error al asignar la clave " << key << " con valor " << value << ": " << e.what() << std::endl; success = false; } return success; } // Asigna variables a una estructura ItemData bool setItem(ItemData *item, const std::string &key, const std::string &value) { // Indicador de éxito en la asignación bool success = true; try { if (key == "tileSetFile") { item->tile_set_file = value; } else if (key == "counter") { item->counter = std::stoi(value); } else if (key == "x") { item->x = std::stof(value) * BLOCK; } else if (key == "y") { item->y = std::stof(value) * BLOCK; } else if (key == "tile") { item->tile = std::stof(value); } else if (key == "[/item]") { // No se realiza ninguna acción para esta clave } else { success = false; } } catch (const std::exception &e) { std::cerr << "Error al asignar la clave " << key << " con valor " << value << ": " << e.what() << std::endl; success = false; } return success; } // Constructor Room::Room(std::shared_ptr room, std::shared_ptr data) : screen_(Screen::get()), renderer_(Screen::get()->getRenderer()), asset_(Asset::get()), debug_(Debug::get()), data_(data) { number_ = room->number; name_ = room->name; bg_color_ = room->bg_color; border_color_ = room->border_color; item_color1_ = room->item_color1 == "" ? "yellow" : room->item_color1; item_color2_ = room->item_color2 == "" ? "magenta" : room->item_color2; room_top_ = room->room_top; room_bottom_ = room->room_bottom; room_left_ = room->room_left; room_right_ = room->room_right; tile_set_file_ = room->tile_set_file; tile_map_file_ = room->tile_map_file; auto_surface_direction_ = room->auto_surface_direction; tile_map_ = Resource::get()->getTileMap(room->tile_map_file); texture_ = (options.video.palette == Palette::ZXSPECTRUM) ? Resource::get()->getTexture(room->tile_set_file) : Resource::get()->getTexture(room->tile_set_file); // Inicializa variables tile_set_width_ = texture_->getWidth() / TILE_SIZE_; paused_ = false; counter_ = 0; // Crea los enemigos for (auto &enemy_data : room->enemies) { enemies_.emplace_back(std::make_shared(enemy_data)); } // Crea los items for (auto &item : room->items) { const SDL_Point itemPos = {item.x, item.y}; if (!ItemTracker::get()->hasBeenPicked(room->name, itemPos)) { item.color1 = stringToColor(options.video.palette, item_color1_); item.color2 = stringToColor(options.video.palette, item_color2_); items_.emplace_back(std::make_shared(item)); } } // Carga los sonidos item_sound_ = Resource::get()->getSound("item.wav"); // Abre la jail para poder entrar if (data_->jail_is_open) { openTheJail(); } // Calcula las superficies setBottomSurfaces(); setTopSurfaces(); setLeftSurfaces(); setRightSurfaces(); setLeftSlopes(); setRightSlopes(); setAutoSurfaces(); // Busca los tiles animados setAnimatedTiles(); // Crea la textura para el mapa de tiles de la habitación map_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, PLAY_AREA_WIDTH, PLAY_AREA_HEIGHT); if (map_texture_ == nullptr) { if (options.console) { std::cout << "Error: mapTexture could not be created!\nSDL Error: " << SDL_GetError() << std::endl; } } SDL_SetTextureBlendMode(map_texture_, SDL_BLENDMODE_BLEND); // Pinta el mapa de la habitación en la textura fillMapTexture(); // Establece el color del borde screen_->setBorderColor(stringToColor(options.video.palette, room->border_color)); } // Destructor Room::~Room() { // Reclama la memoria utilizada por los objetos SDL_DestroyTexture(map_texture_); } // Devuelve el nombre de la habitación std::string Room::getName() { return name_; } // Devuelve el color de la habitación Color Room::getBGColor() { return stringToColor(options.video.palette, bg_color_); } // Devuelve el color del borde Color Room::getBorderColor() { return stringToColor(options.video.palette, border_color_); } // Crea la textura con el mapeado de la habitación void Room::fillMapTexture() { const Color color = stringToColor(options.video.palette, bg_color_); SDL_SetRenderTarget(renderer_, map_texture_); SDL_SetRenderDrawColor(renderer_, color.r, color.g, color.b, 0xFF); SDL_RenderClear(renderer_); // Los tileSetFiles son de 20x20 tiles. El primer tile es el 0. Cuentan hacia la derecha y hacia abajo SDL_Rect 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 que estan en la fila 19 (indices) const int index = (y * MAP_WIDTH_) + x; const bool a = (tile_map_[index] >= 18 * tile_set_width_) && (tile_map_[index] < 19 * tile_set_width_); const bool b = tile_map_[index] > -1; if (b && !a) { clip.x = (tile_map_[index] % tile_set_width_) * TILE_SIZE_; clip.y = (tile_map_[index] / tile_set_width_) * TILE_SIZE_; texture_->render(x * TILE_SIZE_, y * TILE_SIZE_, &clip); #ifdef DEBUG if (debug_->getEnabled()) { if (clip.x != -TILE_SIZE_) { clip.x = x * TILE_SIZE_; clip.y = y * TILE_SIZE_; SDL_SetRenderDrawColor(renderer_, 64, 64, 64, 224); SDL_RenderFillRect(renderer_, &clip); } } #endif } } #ifdef DEBUG if (debug_->getEnabled()) { // BottomSurfaces if (true) { for (auto l : bottom_surfaces_) { SDL_SetRenderDrawColor(renderer_, (rand() % 128) + 96, (rand() % 128) + 96, (rand() % 128) + 96, 0xFF); SDL_SetRenderDrawColor(renderer_, 255, 0, 0, 0xFF); SDL_RenderDrawLine(renderer_, l.x1, l.y, l.x2, l.y); } } // TopSurfaces if (true) { for (auto l : top_surfaces_) { SDL_SetRenderDrawColor(renderer_, (rand() % 128) + 96, (rand() % 128) + 96, (rand() % 128) + 96, 0xFF); SDL_SetRenderDrawColor(renderer_, 0, 255, 0, 0xFF); SDL_RenderDrawLine(renderer_, l.x1, l.y, l.x2, l.y); } } // LeftSurfaces if (true) { for (auto l : left_surfaces_) { SDL_SetRenderDrawColor(renderer_, (rand() % 128) + 96, (rand() % 128) + 96, (rand() % 128) + 96, 0xFF); SDL_SetRenderDrawColor(renderer_, 128, 128, 255, 0xFF); SDL_RenderDrawLine(renderer_, l.x, l.y1, l.x, l.y2); } } // RightSurfaces if (true) { for (auto l : right_surfaces_) { SDL_SetRenderDrawColor(renderer_, (rand() % 128) + 96, (rand() % 128) + 96, (rand() % 128) + 96, 0xFF); SDL_SetRenderDrawColor(renderer_, 255, 255, 0, 0xFF); SDL_RenderDrawLine(renderer_, l.x, l.y1, l.x, l.y2); } } // LeftSlopes if (true) { for (auto l : left_slopes_) { SDL_SetRenderDrawColor(renderer_, (rand() % 128) + 96, (rand() % 128) + 96, (rand() % 128) + 96, 0xFF); SDL_SetRenderDrawColor(renderer_, 0, 255, 255, 0xFF); SDL_RenderDrawLine(renderer_, l.x1, l.y1, l.x2, l.y2); } } // RightSlopes if (true) { for (auto l : right_slopes_) { SDL_SetRenderDrawColor(renderer_, (rand() % 128) + 96, (rand() % 128) + 96, (rand() % 128) + 96, 0xFF); SDL_SetRenderDrawColor(renderer_, 255, 0, 255, 0xFF); SDL_RenderDrawLine(renderer_, l.x1, l.y1, l.x2, l.y2); } } // AutoSurfaces if (true) { for (auto l : auto_surfaces_) { SDL_SetRenderDrawColor(renderer_, (rand() % 128) + 96, (rand() % 128) + 96, (rand() % 128) + 96, 0xFF); SDL_RenderDrawLine(renderer_, l.x1, l.y, l.x2, l.y); } } } #endif SDL_SetRenderTarget(renderer_, nullptr); } // Dibuja el mapa en pantalla void Room::renderMap() { // Dibuja la textura con el mapa en pantalla SDL_Rect dest = {0, 0, PLAY_AREA_WIDTH, PLAY_AREA_HEIGHT}; SDL_RenderCopy(renderer_, map_texture_, nullptr, &dest); // Dibuja los tiles animados #ifdef DEBUG if (!debug_->getEnabled()) { renderAnimatedTiles(); } #else renderAnimatedTiles(); #endif } // Dibuja los enemigos en pantalla void Room::renderEnemies() { for (auto enemy : enemies_) { enemy->render(); } } // Dibuja los objetos en pantalla void Room::renderItems() { for (auto item : items_) { item->render(); } } // Actualiza las variables y objetos de la habitación void Room::update() { if (paused_) { // Si está en modo pausa no se actualiza nada return; } // Actualiza el contador counter_++; // Actualiza los tiles animados updateAnimatedTiles(); for (auto enemy : enemies_) { // Actualiza los enemigos enemy->update(); } for (auto item : items_) { // Actualiza los items item->update(); } } // Devuelve la cadena del fichero de la habitación contigua segun el borde std::string Room::getRoom(int border) { switch (border) { case BORDER_TOP: return room_top_; break; case BORDER_BOTTOM: return room_bottom_; break; case BORDER_RIGHT: return room_right_; break; case BORDER_LEFT: return room_left_; break; default: break; } return ""; } // Devuelve el tipo de tile que hay en ese pixel tile_e Room::getTile(SDL_Point point) { 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 tile_e Room::getTile(int index) { // const bool onRange = (index > -1) && (index < mapWidth * mapHeight); const bool onRange = (index > -1) && (index < (int)tile_map_.size()); if (onRange) { // Las filas 0-8 son de tiles t_wall if ((tile_map_[index] >= 0) && (tile_map_[index] < 9 * tile_set_width_)) { return t_wall; } // Las filas 9-17 son de tiles t_passable else if ((tile_map_[index] >= 9 * tile_set_width_) && (tile_map_[index] < 18 * tile_set_width_)) { return t_passable; } // Las filas 18-20 es de tiles t_animated else if ((tile_map_[index] >= 18 * tile_set_width_) && (tile_map_[index] < 21 * tile_set_width_)) { return t_animated; } // La fila 21 es de tiles t_slope_r else if ((tile_map_[index] >= 21 * tile_set_width_) && (tile_map_[index] < 22 * tile_set_width_)) { return t_slope_r; } // La fila 22 es de tiles t_slope_l else if ((tile_map_[index] >= 22 * tile_set_width_) && (tile_map_[index] < 23 * tile_set_width_)) { return t_slope_l; } // La fila 23 es de tiles t_kill else if ((tile_map_[index] >= 23 * tile_set_width_) && (tile_map_[index] < 24 * tile_set_width_)) { return t_kill; } } return t_empty; } // Indica si hay colision con un enemigo a partir de un rectangulo bool Room::enemyCollision(SDL_Rect &rect) { bool collision = false; for (auto enemy : enemies_) { collision |= checkCollision(rect, enemy->getCollider()); } return collision; } // Indica si hay colision con un objeto a partir de un rectangulo bool Room::itemCollision(SDL_Rect &rect) { for (int i = 0; i < (int)items_.size(); ++i) { if (checkCollision(rect, items_[i]->getCollider())) { ItemTracker::get()->addItem(name_, items_[i]->getPos()); items_.erase(items_.begin() + i); JA_PlaySound(item_sound_); data_->items++; options.stats.items = data_->items; return true; } } return false; } // Recarga la textura void Room::reLoadTexture() { texture_->reLoad(); fillMapTexture(); for (auto enemy : enemies_) { enemy->reLoadTexture(); } for (auto item : items_) { item->reLoadTexture(); } } // Recarga la paleta void Room::reLoadPalette() { // Cambia el color de los items for (auto item : items_) { item->setColors(stringToColor(options.video.palette, item_color1_), stringToColor(options.video.palette, item_color2_)); } // Cambia el color de los enemigos for (auto enemy : enemies_) { enemy->setPalette(options.video.palette); } // Establece el color del borde screen_->setBorderColor(stringToColor(options.video.palette, border_color_)); // Cambia la textura //texture_ = (options.video.palette == Palette::ZXSPECTRUM) ? Resource::get()->getTexture(room->tile_set_file) : Resource::get()->getTexture(room->tile_set_file); // Pone la nueva textura a los tiles animados for (auto tile : animated_tiles_) { tile.sprite->setTexture(texture_); } // Recarga las texturas reLoadTexture(); } // Obten el tamaño del tile int Room::getTileSize() { return TILE_SIZE_; } // Obten la coordenada de la cuesta a partir de un punto perteneciente a ese tile int Room::getSlopeHeight(SDL_Point p, tile_e slope) { // Calcula la base del tile int base = ((p.y / TILE_SIZE_) * TILE_SIZE_) + TILE_SIZE_; #ifdef DEBUG debug_->add("BASE = " + std::to_string(base)); #endif // Calcula cuanto se ha entrado en el tile horizontalmente const int pos = (p.x % TILE_SIZE_); // Esto da un valor entre 0 y 7 #ifdef DEBUG debug_->add("POS = " + std::to_string(pos)); #endif // Se resta a la base la cantidad de pixeles pos en funcion de la rampa if (slope == t_slope_r) { base -= pos + 1; #ifdef DEBUG debug_->add("BASE_R = " + std::to_string(base)); #endif } else { base -= (TILE_SIZE_ - pos); #ifdef DEBUG debug_->add("BASE_L = " + std::to_string(base)); #endif } return base; } // Calcula las superficies inferiores void Room::setBottomSurfaces() { 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) == t_wall && getTile(i + MAP_WIDTH_) != t_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); // Recorre el vector de tiles buscando tiles consecutivos para localizar las superficies if ((int)tile.size() > 1) { int i = 0; int lastOne = 0; do { h_line_t line; line.x1 = (tile[i] % MAP_WIDTH_) * TILE_SIZE_; line.y = ((tile[i] / MAP_WIDTH_) * TILE_SIZE_) + TILE_SIZE_ - 1; lastOne = i; i++; if (i <= (int)tile.size() - 1) { while (tile[i] == tile[i - 1] + 1) { lastOne = i; if (i == (int)tile.size() - 1) { break; } i++; } } line.x2 = ((tile[lastOne] % MAP_WIDTH_) * TILE_SIZE_) + TILE_SIZE_ - 1; bottom_surfaces_.push_back(line); if (i <= (int)tile.size() - 1) { if (tile[i] == -1) { // Si el siguiente elemento es un separador, hay que saltarlo i++; } } } while (i < (int)tile.size() - 1); } } // Calcula las superficies superiores void Room::setTopSurfaces() { 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) == t_wall || getTile(i) == t_passable) && getTile(i - MAP_WIDTH_) != t_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); // Recorre el vector de tiles buscando tiles consecutivos para localizar las superficies if ((int)tile.size() > 1) { int i = 0; int lastOne = 0; do { h_line_t line; line.x1 = (tile[i] % MAP_WIDTH_) * TILE_SIZE_; line.y = (tile[i] / MAP_WIDTH_) * TILE_SIZE_; lastOne = i; i++; if (i <= (int)tile.size() - 1) { while (tile[i] == tile[i - 1] + 1) { lastOne = i; if (i == (int)tile.size() - 1) { break; } i++; } } line.x2 = ((tile[lastOne] % MAP_WIDTH_) * TILE_SIZE_) + TILE_SIZE_ - 1; top_surfaces_.push_back(line); if (i <= (int)tile.size() - 1) { if (tile[i] == -1) { // Si el siguiente elemento es un separador, hay que saltarlo i++; } } } while (i < (int)tile.size() - 1); } } // Calcula las superficies laterales izquierdas void Room::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) == t_wall && getTile(pos - 1) != t_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 { v_line_t 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_surfaces_.push_back(line); i++; } while (i < (int)tile.size() - 1); } } // Calcula las superficies laterales derechas void Room::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) == t_wall && getTile(pos + 1) != t_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 { v_line_t 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_surfaces_.push_back(line); i++; } while (i < (int)tile.size() - 1); } } // Encuentra todas las rampas que suben hacia la izquierda void Room::setLeftSlopes() { // Recorre la habitación entera por filas buscando tiles de tipo t_slope_l std::vector found; for (int i = 0; i < (int)tile_map_.size(); ++i) { if (getTile(i) == t_slope_l) { found.push_back(i); } } // El primer elemento es el inicio de una rampa. Se añade ese elemento y se buscan los siguientes, // que seran i + mapWidth + 1. Conforme se añaden se eliminan y se vuelve a escudriñar el vector de // tiles encontrados hasta que esté vacío while (found.size() > 0) { d_line_t line; line.x1 = (found[0] % MAP_WIDTH_) * TILE_SIZE_; line.y1 = (found[0] / MAP_WIDTH_) * TILE_SIZE_; int lookingFor = found[0] + MAP_WIDTH_ + 1; int lastOneFound = found[0]; found.erase(found.begin()); for (int i = 0; i < (int)found.size(); ++i) { if (found[i] == lookingFor) { lastOneFound = lookingFor; lookingFor += MAP_WIDTH_ + 1; found.erase(found.begin() + i); i--; } } line.x2 = ((lastOneFound % MAP_WIDTH_) * TILE_SIZE_) + TILE_SIZE_ - 1; line.y2 = ((lastOneFound / MAP_WIDTH_) * TILE_SIZE_) + TILE_SIZE_ - 1; left_slopes_.push_back(line); } } // Encuentra todas las rampas que suben hacia la derecha void Room::setRightSlopes() { // Recorre la habitación entera por filas buscando tiles de tipo t_slope_r std::vector found; for (int i = 0; i < (int)tile_map_.size(); ++i) { if (getTile(i) == t_slope_r) { found.push_back(i); } } // El primer elemento es el inicio de una rampa. Se añade ese elemento y se buscan los siguientes, // que seran i + mapWidth - 1. Conforme se añaden se eliminan y se vuelve a escudriñar el vector de // tiles encontrados hasta que esté vacío while (found.size() > 0) { d_line_t line; line.x1 = ((found[0] % MAP_WIDTH_) * TILE_SIZE_) + TILE_SIZE_ - 1; line.y1 = (found[0] / MAP_WIDTH_) * TILE_SIZE_; int lookingFor = found[0] + MAP_WIDTH_ - 1; int lastOneFound = found[0]; found.erase(found.begin()); for (int i = 0; i < (int)found.size(); ++i) { if (found[i] == lookingFor) { lastOneFound = lookingFor; lookingFor += MAP_WIDTH_ - 1; found.erase(found.begin() + i); i--; } } line.x2 = (lastOneFound % MAP_WIDTH_) * TILE_SIZE_; line.y2 = ((lastOneFound / MAP_WIDTH_) * TILE_SIZE_) + TILE_SIZE_ - 1; right_slopes_.push_back(line); } } // Calcula las superficies automaticas void Room::setAutoSurfaces() { 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) == t_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); } } } // Recorre el vector de tiles buscando tiles consecutivos para localizar las superficies int i = 0; int lastOne = 0; if ((int)tile.size() > 0) { do { h_line_t line; line.x1 = (tile[i] % MAP_WIDTH_) * TILE_SIZE_; line.y = (tile[i] / MAP_WIDTH_) * TILE_SIZE_; lastOne = i; i++; if (i <= (int)tile.size() - 1) { while (tile[i] == tile[i - 1] + 1) { lastOne = i; if (i == (int)tile.size() - 1) { break; } i++; } } line.x2 = ((tile[lastOne] % MAP_WIDTH_) * TILE_SIZE_) + TILE_SIZE_ - 1; auto_surfaces_.push_back(line); if (i <= (int)tile.size() - 1) { if (tile[i] == -1) { // Si el siguiente elemento es un separador, hay que saltarlo i++; } } } while (i < (int)tile.size() - 1); } } // Localiza todos los tiles animados de la habitación void Room::setAnimatedTiles() { // Recorre la habitación entera por filas buscando tiles de tipo t_animated for (int i = 0; i < (int)tile_map_.size(); ++i) { if (getTile(i) == t_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_; aTile_t at; at.sprite = std::make_shared(texture_, x, y, 8, 8); at.sprite->setClip(xc, yc, 8, 8); at.x_orig = xc; animated_tiles_.push_back(at); } } } // Actualiza los tiles animados void Room::updateAnimatedTiles() { const int numFrames = 4; int offset = 0; if (auto_surface_direction_ == -1) { offset = ((counter_ / 3) % numFrames * TILE_SIZE_); } else { offset = ((numFrames - 1 - ((counter_ / 3) % numFrames)) * TILE_SIZE_); } for (auto &a : animated_tiles_) { SDL_Rect rect = a.sprite->getClip(); rect.x = a.x_orig + offset; a.sprite->setClip(rect); } } // Pinta los tiles animados en pantalla void Room::renderAnimatedTiles() { for (auto a : animated_tiles_) { a.sprite->render(); } } // Comprueba las colisiones int Room::checkRightSurfaces(SDL_Rect *rect) { for (auto s : right_surfaces_) { if (checkCollision(s, *rect)) { return s.x; } } return -1; } // Comprueba las colisiones int Room::checkLeftSurfaces(SDL_Rect *rect) { for (auto s : left_surfaces_) { if (checkCollision(s, *rect)) { return s.x; } } return -1; } // Comprueba las colisiones int Room::checkTopSurfaces(SDL_Rect *rect) { for (auto s : top_surfaces_) { if (checkCollision(s, *rect)) { return s.y; } } return -1; } // Comprueba las colisiones int Room::checkBottomSurfaces(SDL_Rect *rect) { for (auto s : bottom_surfaces_) { if (checkCollision(s, *rect)) { return s.y; } } return -1; } // Comprueba las colisiones int Room::checkAutoSurfaces(SDL_Rect *rect) { for (auto s : auto_surfaces_) { if (checkCollision(s, *rect)) { return s.y; } } return -1; } // Comprueba las colisiones bool Room::checkTopSurfaces(SDL_Point *p) { for (auto s : top_surfaces_) { if (checkCollision(s, *p)) { return true; } } return false; } // Comprueba las colisiones bool Room::checkAutoSurfaces(SDL_Point *p) { for (auto s : auto_surfaces_) { if (checkCollision(s, *p)) { return true; } } return false; } // Comprueba las colisiones int Room::checkLeftSlopes(v_line_t *line) { for (auto s : left_slopes_) { const SDL_Point p = checkCollision(s, *line); if (p.x != -1) { return p.y; } } return -1; } // Comprueba las colisiones bool Room::checkLeftSlopes(SDL_Point *p) { for (auto s : left_slopes_) { if (checkCollision(*p, s)) { return true; } } return false; } // Comprueba las colisiones int Room::checkRightSlopes(v_line_t *line) { for (auto s : right_slopes_) { const SDL_Point p = checkCollision(s, *line); if (p.x != -1) { return p.y; } } return -1; } // Comprueba las colisiones bool Room::checkRightSlopes(SDL_Point *p) { for (auto s : right_slopes_) { if (checkCollision(*p, s)) { return true; } } return false; } // Pone el mapa en modo pausa void Room::pause() { paused_ = true; } // Quita el modo pausa del mapa void Room::resume() { paused_ = false; } // Obten la direccion de las superficies automaticas int Room::getAutoSurfaceDirection() { return auto_surface_direction_; } // Abre la jail para poder entrar void Room::openTheJail() { if (name_ == "THE JAIL") { // Elimina el último enemigo (Bry debe ser el ultimo enemigo definido en el fichero) enemies_.pop_back(); // Abre las puertas const int tileA = 16 + (13 * 32); const int tileB = 16 + (14 * 32); tile_map_[tileA] = -1; tile_map_[tileB] = -1; } }