#include "map.h" // Constructor Map::Map(std::string file, SDL_Renderer *renderer, Asset *asset, ItemTracker *itemTracker) { // Inicializa variables tileSize = 8; mapWidth = 40; mapHeight = 30; name = file.substr(file.find_last_of("\\/") + 1); enemyFile = ""; bgColor1 = bgColor2 = {0, 0, 0}; bgScroll = false; counter = 0; // Copia los punteros a objetos this->asset = asset; this->renderer = renderer; this->itemTracker = itemTracker; // Crea las texturas para dibujar el mapa mapLayerBG = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, PLAY_AREA_WIDTH, PLAY_AREA_HEIGHT); if (mapLayerBG == NULL) { printf("Error: mapLayer0 could not be created!\nSDL Error: %s\n", SDL_GetError()); } SDL_SetTextureBlendMode(mapLayerBG, SDL_BLENDMODE_BLEND); mapLayer0 = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, PLAY_AREA_WIDTH, PLAY_AREA_HEIGHT); if (mapLayer0 == NULL) { printf("Error: mapLayer0 could not be created!\nSDL Error: %s\n", SDL_GetError()); } SDL_SetTextureBlendMode(mapLayer0, SDL_BLENDMODE_BLEND); mapLayer1 = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, PLAY_AREA_WIDTH, PLAY_AREA_HEIGHT); if (mapLayer1 == NULL) { printf("Error: mapLayer1 could not be created!\nSDL Error: %s\n", SDL_GetError()); } SDL_SetTextureBlendMode(mapLayer1, SDL_BLENDMODE_BLEND); // Crea los objetos loadMapFile(file); textureTile = new LTexture(renderer, asset->get(tileset)); tilesetWidth = textureTile->getWidth() / tileSize; loadMapTileFile(asset->get(tileMapFile)); } // Destructor Map::~Map() { // Reclama la memoria utilizada por los objetos textureTile->unload(); delete textureTile; SDL_DestroyTexture(mapLayerBG); SDL_DestroyTexture(mapLayer0); SDL_DestroyTexture(mapLayer1); for (auto actor : actors) { delete actor; } actors.clear(); } // Carga las variables desde un fichero de mapa bool Map::loadMapFile(std::string file_path) { 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 printf("Reading file %s\n\n", filename.c_str()); while (std::getline(file, line)) { // Si la linea contiene el texto [actor] se realiza el proceso de carga de los actores 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()); } } 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()); } } 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()); } } } // 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()); return false; } return true; } // Lee la matriz de tiles desde un fichero tmx a un vector std::vector Map::readTilesFromFile(std::ifstream &file) { std::vector tilemap; std::string line; // Salta una linea y lee la siguiente std::getline(file, line); std::getline(file, line); while (line != "") { // Procesa lineas mientras haya std::stringstream ss(line); std::string tmp; while (getline(ss, tmp, ',')) { tilemap.push_back(std::stoi(tmp)); } // Lee la siguiente linea std::getline(file, line); } return tilemap; } // Carga las variables y texturas desde un fichero de mapa de tiles bool Map::loadMapTileFile(std::string file_path) { std::vector tilemap; 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 printf("Reading file %s\n\n", filename.c_str()); while (std::getline(file, line)) { // Lee el fichero linea a linea if (line.find("name=\"estatico\"") != std::string::npos) { tilemap = readTilesFromFile(file); fillGradientTexture(*mapLayerBG); fillMapTexture(*mapLayerBG, tilemap, false); tilemap.clear(); } else if (line.find("name=\"fondo\"") != std::string::npos) { tilemap = readTilesFromFile(file); fillMapTexture(*mapLayer0, tilemap, true); tilemap.clear(); } else if (line.find("name=\"mapa\"") != std::string::npos) { tilemap = readTilesFromFile(file); fillMapTexture(*mapLayer1, tilemap, true); tilemap.clear(); } else if (line.find("name=\"colisiones\"") != std::string::npos) { collisionmap = readTilesFromFile(file); } } // Cierra el fichero printf("Closing file %s\n\n", filename.c_str()); file.close(); } else { // El fichero no se puede abrir printf("Warning: Unable to open %s file\n", filename.c_str()); return false; } return true; } // Asigna variables a partir de dos cadenas bool Map::setVars(std::string var, std::string value) { if (var == "tilemap") { tileMapFile = value; } else if (var == "tileset") { tileset = 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 == "bgScroll") { bgScroll = stringToBool(value); } else if (var == "roomUp") { roomUp = value; } else if (var == "roomDown") { roomDown = value; } else if (var == "roomLeft") { roomLeft = value; } else if (var == "roomRight") { roomRight = value; } else if (var == "enemyFile") { enemyFile = value; } else if (var == "") { } else { return false; } return true; } // 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) { 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) * tileSize; } else if (var == "y") { actor->y = std::stof(value) * tileSize; } 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) * tileSize; } else if (var == "x2") { p2->x = std::stoi(value) * tileSize; } else if (var == "y1") { p1->y = std::stoi(value) * tileSize; } else if (var == "y2") { p2->y = std::stoi(value) * tileSize; } else if ((var == "[/moving platform]") || (var == "[/diamond]")) { } else { return false; } return true; } // Pinta el degradado en la textura void Map::fillGradientTexture(SDL_Texture &layer) { // Cambia el puntero del renderizador a la textura SDL_SetRenderTarget(renderer, &layer); // Dibuja el degradado de fondo const float numLines = PLAY_AREA_BOTTOM - PLAY_AREA_TOP; for (int i = PLAY_AREA_TOP; i < PLAY_AREA_BOTTOM; ++i) { float step = ((float)i / numLines); 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, PLAY_AREA_LEFT, i, PLAY_AREA_RIGHT, i); } // Vuelve a colocar el renderizador apuntando a la pantalla SDL_SetRenderTarget(renderer, nullptr); } // Crea la textura con el mapeado de la habitación void Map::fillMapTexture(SDL_Texture &layer, std::vector tilemap, bool clean) { // Crea variables SDL_Rect clip = {0, 0, tileSize, tileSize}; // Cambia el puntero del renderizador a la textura SDL_SetRenderTarget(renderer, &layer); // Limpia la textura if (clean) { SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00); SDL_RenderClear(renderer); } // Dibuja el mapeado de tiles for (int y = 0; y < mapHeight; ++y) for (int x = 0; x < mapWidth; ++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[(y * mapWidth) + x] - 1) % tilesetWidth) * tileSize; clip.y = ((tilemap[(y * mapWidth) + x] - 1) / tilesetWidth) * tileSize; textureTile->render(renderer, x * tileSize, y * tileSize, &clip); } // Vuelve a colocar el renderizador apuntando a la pantalla SDL_SetRenderTarget(renderer, nullptr); } // Dibuja todos los elementos del mapa void Map::render() { renderLayerBG(); renderLayer0(); renderLayer1(); renderActors(); } // Dibuja la capa BG void Map::renderLayerBG() { // Dibuja la textura con el mapa en pantalla SDL_RenderCopy(renderer, mapLayerBG, nullptr, nullptr); } // Dibuja la capa 0 void Map::renderLayer0() { // Dibuja la textura con el mapa en pantalla if (bgScroll) { const int offset = PLAY_AREA_WIDTH - ((counter / 20) % PLAY_AREA_WIDTH); SDL_Rect src1 = {PLAY_AREA_X, PLAY_AREA_Y, offset, PLAY_AREA_HEIGHT}; SDL_Rect dst1 = {PLAY_AREA_X + PLAY_AREA_WIDTH - offset, PLAY_AREA_Y, offset, PLAY_AREA_HEIGHT}; SDL_Rect src2 = {PLAY_AREA_X + offset, PLAY_AREA_Y, PLAY_AREA_WIDTH - offset, PLAY_AREA_HEIGHT}; SDL_Rect dst2 = {PLAY_AREA_X, PLAY_AREA_Y, PLAY_AREA_WIDTH - offset, PLAY_AREA_HEIGHT}; SDL_RenderCopy(renderer, mapLayer0, &src1, &dst1); SDL_RenderCopy(renderer, mapLayer0, &src2, &dst2); } else { SDL_RenderCopy(renderer, mapLayer0, nullptr, nullptr); } } // Dibuja la capa 1 void Map::renderLayer1() { // Dibuja la textura con el mapa en pantalla SDL_RenderCopy(renderer, mapLayer1, nullptr, nullptr); } // Dibuja los actores void Map::renderActors() { for (auto actor : actors) { actor->render(); } } // Actualiza todas las variables void Map::update() { counter++; 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 / tileSize) * mapWidth) + (x / tileSize)]; 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 tileSize; } // 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 tileSize * mapWidth; break; case b_bottom: return tileSize * mapHeight; 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 roomUp; break; case b_left: return roomLeft; break; case b_right: return roomRight; break; case b_bottom: return roomDown; 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 enemyFile; } // Recarga las texturas void Map::reLoadTextures() { textureTile->reLoad(); }