Files
volcano_2022/source/map.cpp

748 lines
18 KiB
C++

#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 = 30;
name = file.substr(file.find_last_of("\\/") + 1);
enemy_file = "";
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
map_layerBG = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, PLAY_AREA_WIDTH, PLAY_AREA_HEIGHT);
if (map_layerBG == NULL)
{
printf("Error: map_layer0 could not be created!\nSDL Error: %s\n", SDL_GetError());
}
SDL_SetTextureBlendMode(map_layerBG, SDL_BLENDMODE_BLEND);
map_layer0 = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, PLAY_AREA_WIDTH, PLAY_AREA_HEIGHT);
if (map_layer0 == NULL)
{
printf("Error: map_layer0 could not be created!\nSDL Error: %s\n", SDL_GetError());
}
SDL_SetTextureBlendMode(map_layer0, SDL_BLENDMODE_BLEND);
// SDL_SetTextureAlphaMod(map_layer0, 128);
map_layer1 = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, PLAY_AREA_WIDTH, PLAY_AREA_HEIGHT);
if (map_layer1 == NULL)
{
printf("Error: map_layer1 could not be created!\nSDL Error: %s\n", SDL_GetError());
}
SDL_SetTextureBlendMode(map_layer1, SDL_BLENDMODE_BLEND);
// Crea los objetos
loadMapFile(file);
texture_tile = new LTexture(renderer, asset->get(tileset_img));
tileset_width = texture_tile->getWidth() / tile_size;
loadMapTileFile(asset->get(tileMapFile));
// Pinta el mapa en las texturas
// 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 de mapa
bool Map::loadMapFile(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 [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());
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;
}
// Lee la matriz de tiles desde un fichero tmx a un vector
std::vector<int> Map::readTilesFromFile(std::ifstream &file)
{
std::vector<int> tilemap;
std::string line;
// Salta una linea y lee la siguiente
std::getline(file, line);
std::getline(file, line);
while (line != "</data>")
{ // 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<int> 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(*map_layerBG);
fillMapTexture(*map_layerBG, tilemap, false);
tilemap.clear();
}
else if (line.find("name=\"fondo\"") != std::string::npos)
{
tilemap = readTilesFromFile(file);
fillMapTexture(*map_layer0, tilemap, true);
tilemap.clear();
}
else if (line.find("name=\"mapa\"") != std::string::npos)
{
tilemap = readTilesFromFile(file);
fillMapTexture(*map_layer1, 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)
{
// Indicador de éxito en la asignación
bool success = true;
if (var == "tilemap")
{
tileMapFile = value;
}
else 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 == "bgScroll")
{
bgScroll = stringToBool(value);
}
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;
}
// 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 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, 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<int> tilemap, bool clean)
{
// Crea variables
SDL_Rect clip = {0, 0, tile_size, tile_size};
// 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 < 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[(y * map_width) + x] - 1) % tileset_width) * tile_size;
clip.y = ((tilemap[(y * map_width) + x] - 1) / tileset_width) * tile_size;
texture_tile->render(renderer, x * tile_size, y * tile_size, &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, map_layerBG, 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, map_layer0, &src1, &dst1);
SDL_RenderCopy(renderer, map_layer0, &src2, &dst2);
}
else
{
SDL_RenderCopy(renderer, map_layer0, nullptr, nullptr);
}
}
// Dibuja la capa 1
void Map::renderLayer1()
{
// Dibuja la textura con el mapa en pantalla
SDL_RenderCopy(renderer, map_layer1, 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 / tile_size) * map_width) + (x / tile_size)];
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;
}
// Recarga las texturas
void Map::reLoadTextures()
{
texture_tile->reLoad();
// fillMapTexture();
}