#include "map.h" // Constructor Map::Map(std::string file, SDL_Renderer *renderer, Asset *asset, ItemTracker *itemTracker) { // Inicializa variables tile_size = 8; map_width = 40; map_height = 26; name = file.substr(file.find_last_of("\\/") + 1); enemy_file = ""; bgColor1 = bgColor2 = {0, 0, 0}; // Copia los punteros a objetos this->asset = asset; this->renderer = renderer; this->itemTracker = itemTracker; // Crea los objetos load(file); texture_tile = new LTexture(renderer, asset->get(tileset_img)); tileset_width = texture_tile->getWidth() / tile_size; // Crea las texturas para dibujar el mapa map_layer0 = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, GAMECANVAS_WIDTH, GAMECANVAS_HEIGHT); if (map_layer0 == NULL) printf("Error: map_layer0 could not be created!\nSDL Error: %s\n", SDL_GetError()); map_layer1 = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, GAMECANVAS_WIDTH, GAMECANVAS_HEIGHT); if (map_layer1 == NULL) printf("Error: map_layer1 could not be created!\nSDL Error: %s\n", SDL_GetError()); // Pinta el mapa de la habitación en la textura fillMapTexture(); } // Destructor Map::~Map() { // Reclama la memoria utilizada por los objetos texture_tile->unload(); delete texture_tile; SDL_DestroyTexture(map_layer1); for (auto actor : actors) { delete actor; } actors.clear(); } // Carga las variables desde un fichero bool Map::load(std::string file_path) { // Indicador de éxito en la carga bool success = true; 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 printf("Reading file %s\n\n", filename.c_str()); while (std::getline(file, line)) { // Si la linea contiene el texto [tilemap] se realiza el proceso de carga del fichero tmx if (line == "[tilemap]") { do { std::getline(file, line); if (line.find(".tmx") != std::string::npos) { std::ifstream file2(asset->get(line)); // Abre el fichero tmx if (file2.good()) { bool map_col_read = false; bool map_l0_read = false; bool map_l1_read = false; while (std::getline(file2, line)) // Lee el fichero linea a linea { if (!map_l0_read) { // Lee lineas hasta que encuentre donde empiezan los datos del mapa int pos = 0; do { std::getline(file2, line); pos = line.find("data encoding"); } while (pos == std::string::npos); do { // Se introducen los valores separados por comas en un vector map_l0_read = true; std::getline(file2, line); if (line != "") { std::stringstream ss(line); std::string tmp; while (getline(ss, tmp, ',')) { tilemap_l0.push_back(std::stoi(tmp)); } } } while (line != ""); } if (!map_l1_read) { // Lee lineas hasta que encuentre donde empiezan los datos del mapa int pos = 0; do { std::getline(file2, line); pos = line.find("data encoding"); } while (pos == std::string::npos); do { // Se introducen los valores separados por comas en un vector map_l1_read = true; std::getline(file2, line); if (line != "") { std::stringstream ss(line); std::string tmp; while (getline(ss, tmp, ',')) { tilemap_l1.push_back(std::stoi(tmp)); } } } while (line != ""); } if (!map_col_read) { // Lee lineas hasta que encuentre donde empiezan los datos del mapa int pos = 0; do { std::getline(file2, line); pos = line.find("data encoding"); } while (pos == std::string::npos); do { // Se introducen los valores separados por comas en un vector map_col_read = true; std::getline(file2, line); if (line != "") { std::stringstream ss(line); std::string tmp; while (getline(ss, tmp, ',')) { collisionmap.push_back(std::stoi(tmp)); } } } while (line != ""); } } } } } while (line != "[/tilemap]"); } // Si la linea contiene el texto [actor] se realiza el proceso de carga de los actores else if (line == "[actors]") { do { std::getline(file, line); if (line == "[moving platform]") { actor_t actor; actor.asset = asset; actor.renderer = renderer; actor.name = a_moving_platform; SDL_Point p1, p2; do { std::getline(file, line); // Encuentra la posición del caracter '=' int pos = line.find("="); // Procesa las dos subcadenas if (!setActor(&actor, &p1, &p2, line.substr(0, pos), line.substr(pos + 1, line.length()))) { printf("Warning: file %s\n, unknown parameter \"%s\"\n", filename.c_str(), line.substr(0, pos).c_str()); success = false; } } while (line != "[/moving platform]"); printf("** actor moving platform loaded\n\n"); actors.push_back(new ActorMovingPlatform(actor, p1, p2)); } if (line == "[diamond]") { actor_t actor; actor.asset = asset; actor.renderer = renderer; actor.name = a_diamond; actor.vx = 0.0f; actor.vy = 0.0f; SDL_Point p1, p2; do { std::getline(file, line); // Encuentra la posición del caracter '=' int pos = line.find("="); // Procesa las dos subcadenas if (!setActor(&actor, &p1, &p2, line.substr(0, pos), line.substr(pos + 1, line.length()))) { printf("Warning: file %s\n, unknown parameter \"%s\"\n", filename.c_str(), line.substr(0, pos).c_str()); success = false; } } while (line != "[/diamond]"); // Comprueba si el actor no ha sido recogido previamente if (!itemTracker->hasBeenPicked(name, {(int)actor.x, (int)actor.y})) { printf("** actor diamond loaded\n\n"); actors.push_back(new ActorDiamond(actor)); } } } while (line != "[/actors]"); } // 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 if (!setVars(line.substr(0, pos), line.substr(pos + 1, line.length()))) { printf("Warning: file %s, unknown parameter \"%s\"\n", filename.c_str(), line.substr(0, pos).c_str()); success = false; } } } // Cierra el fichero printf("Closing file %s\n\n", filename.c_str()); file.close(); } // El fichero no se puede abrir else { printf("Warning: Unable to open %s file\n", filename.c_str()); success = false; } return success; } // Asigna variables a partir de dos cadenas bool Map::setVars(std::string var, std::string value) { // Indicador de éxito en la asignación bool success = true; if (var == "tileset_img") { tileset_img = value; } else if (var == "bgColor1") { // Se introducen los valores separados por comas en un vector std::stringstream ss(value); std::string tmp; getline(ss, tmp, ','); bgColor1.r = std::stoi(tmp); getline(ss, tmp, ','); bgColor1.g = std::stoi(tmp); getline(ss, tmp, ','); bgColor1.b = std::stoi(tmp); } else if (var == "bgColor2") { // Se introducen los valores separados por comas en un vector std::stringstream ss(value); std::string tmp; getline(ss, tmp, ','); bgColor2.r = std::stoi(tmp); getline(ss, tmp, ','); bgColor2.g = std::stoi(tmp); getline(ss, tmp, ','); bgColor2.b = std::stoi(tmp); } else if (var == "room_up") { room_up = value; } else if (var == "room_down") { room_down = value; } else if (var == "room_left") { room_left = value; } else if (var == "room_right") { room_right = value; } else if (var == "enemy_file") { enemy_file = value; } else if (var == "") { } else { success = false; } return success; } // Asigna variables a una estructura enemy_t bool Map::setActor(actor_t *actor, SDL_Point *p1, SDL_Point *p2, std::string var, std::string value) { // Indicador de éxito en la asignación bool success = true; if (var == "tileset") { actor->tileset = value; } else if (var == "animation") { actor->animation = value; } else if (var == "width") { actor->w = std::stoi(value); } else if (var == "height") { actor->h = std::stoi(value); } else if (var == "x") { actor->x = std::stof(value) * tile_size; } else if (var == "y") { actor->y = std::stof(value) * tile_size; } else if (var == "vx") { actor->vx = std::stof(value); } else if (var == "vy") { actor->vy = std::stof(value); } else if (var == "x1") { p1->x = std::stoi(value) * tile_size; } else if (var == "x2") { p2->x = std::stoi(value) * tile_size; } else if (var == "y1") { p1->y = std::stoi(value) * tile_size; } else if (var == "y2") { p2->y = std::stoi(value) * tile_size; } else if ((var == "[/moving platform]") || (var == "[/diamond]")) { } else { success = false; } return success; } // Crea la textura con el mapeado de la habitación void Map::fillMapTexture() { // Crea variables SDL_Rect clip = {0, 0, tile_size, tile_size}; // Rellena la capa 0 { SDL_SetRenderTarget(renderer, map_layer0); SDL_SetTextureBlendMode(map_layer0, SDL_BLENDMODE_BLEND); SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF); SDL_RenderClear(renderer); // Dibuja el degradado de fondo const float num_lines = PLAY_AREA_BOTTOM - PLAY_AREA_TOP; for (int i = PLAY_AREA_TOP; i < PLAY_AREA_BOTTOM; i++) { float step = ((float)i / num_lines); int r = bgColor1.r + ((bgColor2.r - bgColor1.r) * step); int g = bgColor1.g + ((bgColor2.g - bgColor1.g) * step); int b = bgColor1.b + ((bgColor2.b - bgColor1.b) * step); SDL_SetRenderDrawColor(renderer, r, g, b, 0xFF); SDL_RenderDrawLine(renderer, 0, i, 319, i); } // Dibuja el mapeado de tiles for (int y = 0; y < map_height; y++) for (int x = 0; x < map_width; x++) { // Resta uno porque Tiled almacena los indices empezando de 1 en vez de 0. // El problema es que los tiles vacios los pone como 0 y aqui pasan a ser -1 // con lo que esta pintando desde fuera de la textura clip.x = ((tilemap_l0[(y * map_width) + x] - 1) % tileset_width) * tile_size; clip.y = ((tilemap_l0[(y * map_width) + x] - 1) / tileset_width) * tile_size; texture_tile->render(renderer, x * tile_size, (y * tile_size) + PLAY_AREA_TOP, &clip); } } // Rellena la capa 1 { SDL_SetRenderTarget(renderer, map_layer1); SDL_SetTextureBlendMode(map_layer1, SDL_BLENDMODE_BLEND); SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00); SDL_RenderClear(renderer); // Dibuja el mapeado de tiles for (int y = 0; y < map_height; y++) for (int x = 0; x < map_width; x++) { // Resta uno porque Tiled almacena los indices empezando de 1 en vez de 0. // El problema es que los tiles vacios los pone como 0 y aqui pasan a ser -1 // con lo que esta pintando desde fuera de la textura clip.x = ((tilemap_l1[(y * map_width) + x] - 1) % tileset_width) * tile_size; clip.y = ((tilemap_l1[(y * map_width) + x] - 1) / tileset_width) * tile_size; texture_tile->render(renderer, x * tile_size, (y * tile_size) + PLAY_AREA_TOP, &clip); } } // Vuelve a colocar el renderizador SDL_SetRenderTarget(renderer, nullptr); } // Dibuja el mapa en pantalla void Map::render() { // Dibuja la textura con el mapa en pantalla SDL_Rect rect = {0, 0, GAMECANVAS_WIDTH, GAMECANVAS_HEIGHT}; SDL_RenderCopy(renderer, map_layer0, &rect, NULL); SDL_RenderCopy(renderer, map_layer1, &rect, NULL); for (auto actor : actors) { actor->render(); } } // Actualiza todas las variables void Map::update() { for (auto actor : actors) { actor->update(); } } // Devuelve el tipo de tile que hay en un punto e_tile_map Map::getTile(SDL_Point p) { // Normalizamos los puntos para que no busque fuera del mapa const int x = std::max(getPlayArea(b_left), (std::min(p.x, getPlayArea(b_right) - 1))); const int y = std::max(getPlayArea(b_top), (std::min(p.y, getPlayArea(b_bottom) - 1))); // Calcula el tile const int tile = collisionmap[(((y + PLAY_AREA_TOP) / tile_size) * map_width) + (x / tile_size)]; // Las 8 primeras filas son tiles de fondo if (tile == 0) { return nothing; } else if (tile == 5627) { return wall; } else if (tile == 5628) { return passable; } return nothing; } // Devuelve el valor de la variable int Map::getTileSize() { return tile_size; } // Devuelve el valor de los bordes de la zona de juego int Map::getPlayArea(e_border border) { switch (border) { case b_top: return 0; break; case b_left: return 0; break; case b_right: return tile_size * map_width; break; case b_bottom: return tile_size * map_height; break; default: break; } return -1; } // Devuelve el nombre del fichero de la habitación en funcion del borde std::string Map::getRoomFileName(e_border border) { switch (border) { case b_top: return room_up; break; case b_left: return room_left; break; case b_right: return room_right; break; case b_bottom: return room_down; break; default: break; } return ""; } // Devuelve el nombre del fichero de la habitación std::string Map::getName() { return name; } // Indica si hay colision con un actor a partir de un rectangulo int Map::actorCollision(SDL_Rect &rect) { int index = 0; for (auto actor : actors) { if (checkCollision(rect, actor->getCollider())) { return index; } index++; } return -1; } // Indica si hay colision con un actor a partir de un rectangulo int Map::actorCollision(SDL_Point &p) { int index = 0; for (auto actor : actors) { if (checkCollision(p, actor->getCollider())) { return index; } index++; } return -1; } // Devuelve el nombre del actor a pàrtir de un índice int Map::getActorName(int index) { if (index != -1) { return actors[index]->getName(); } return -1; } // Devuelve el rectangulo de colisión SDL_Rect Map::getActorCollider(int index) { SDL_Rect rect = {0, 0, 0, 0}; if (index != -1) { rect = actors[index]->getCollider(); } return rect; } // Devuelve el desplazamiento relativo del actor int Map::getActorIncX(int index) { int shift = 0; if (index != -1) { shift = actors[index]->getIncX(); } return shift; } // Elimina un actor bool Map::deleteActor(int index) { bool success = false; if (actors[index] != nullptr) { delete actors[index]; success = true; } actors.erase(actors.begin() + index); return success; } // Coge un item void Map::getItem(int index) { const SDL_Rect r = getActorCollider(index); itemTracker->addItem(name, {r.x, r.y}); } // Obtiene el valor de la variable std::string Map::getEnemyFile() { return enemy_file; }