2D_Engine/TileMap.cpp

267 lines
9.5 KiB
C++

/********************\
| Copyright 2024, |
| Ulysse Cura |
\********************/
////////////////////////////////////////
// //
// Definition de la classe TileMap. //
// //
////////////////////////////////////////
#include <iostream>
#include <stdexcept>
#include "Camera.hpp"
#include "Game.hpp"
#include "MapManager.hpp"
#include "TextureManager.hpp"
#include "TileMap.hpp"
#include "Vector2D.hpp"
#include "ECS/Components.hpp"
using std::string, std::exception, std::vector, std::cerr, std::runtime_error;
using json = nlohmann::json;
// Load a map from memory
void TileMap::LoadTileMap(const string &path)
{
// Load the map data from memory
json *mapData = Game::mapManager.LoadMap(path);
// Get tilemap width and height same for world
tileMapWidth = mapData->at("TileMapWidth").get<int>();
tileMapHeight = mapData->at("TileMapHeight").get<int>();
worldWidth = tileMapWidth * TILE_SIZE * TILEMAP_SCALE;
worldHeight = tileMapHeight * TILE_SIZE * TILEMAP_SCALE;
// Clear layers and hitboxes and resize them
m_tilesLayer1.clear();
m_tilesLayer2.clear();
m_tilesLayer3.clear();
hitboxes.clear();
m_tilesLayer1.resize(tileMapWidth * tileMapHeight);
m_tilesLayer2.resize(tileMapWidth * tileMapHeight);
m_tilesLayer3.resize(tileMapWidth * tileMapHeight);
hitboxes.resize(tileMapWidth * tileMapHeight);
// Load layers and hitboxes from map data
string layers[] {"Layer 1","Layer 2","Layer 3", "Hitboxes"};
for(const string &layer : layers)
{
for(int y {0}; y < tileMapHeight; y++)
{
for(int x {0}; x < tileMapWidth; x++)
{
TileID tileID {mapData->at(layer).at(y).at(x)};
if(layer == "Layer 1")
{
m_tilesLayer1[x + y * tileMapWidth] = tileID;
}
else if(layer == "Layer 2")
{
m_tilesLayer2[x + y * tileMapWidth] = tileID;
}
else if(layer == "Layer 3")
{
m_tilesLayer3[x + y * tileMapWidth] = tileID;
}
else if(layer == "Hitboxes")
{
hitboxes[x + y * tileMapWidth] = tileID;
}
}
}
}
LoadEntities(mapData);
if(mapData->contains("PlayerInitPos"))
{
m_playerInitPos.x = mapData->at("PlayerInitPos").at("x").get<float>() * TILE_SIZE * TILEMAP_SCALE;
m_playerInitPos.y = mapData->at("PlayerInitPos").at("y").get<float>() * TILE_SIZE * TILEMAP_SCALE;
}
m_nextMaps.clear();
for(auto it {mapData->at("NextMaps").begin()}; it < mapData->at("NextMaps").end(); it++)
{
NextMap nextMap;
nextMap.path = it->at("Path").get<string>();
nextMap.playerInitPos.x = it->at("PlayerInitPos").at("x").get<float>();
nextMap.playerInitPos.y = it->at("PlayerInitPos").at("y").get<float>();
m_nextMaps.emplace(it->at("Number").get<int>(), nextMap);
}
// Texture renderer creating
if(Game::textureRenderer != NULL) SDL_DestroyTexture(Game::textureRenderer);
Game::textureRenderer = SDL_CreateTexture(Game::renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, worldWidth, worldHeight);
if(Game::textureRenderer == NULL)
{
cerr << "Erreur SDL_CreateTexture : " << SDL_GetError() << '\n';
throw runtime_error("Impossible de creer la texture de rendu.\n");
}
}
void TileMap::LoadEntities(json *mapData)
{
// Erase lasts entities and load news if there are some
std::size_t numberOfEntity {Game::entityManager.getNumberOfEntity()};
for(std::size_t i {1}; i < numberOfEntity; i++) Game::entityManager.destroy(i);
Game::entityManager.refresh();
if(mapData->contains("Entities"))
{
for(auto it {mapData->at("Entities").begin()}; it < mapData->at("Entities").end(); it++)
{
Entity &e(Game::entityManager.addEntity());
for(auto component {it->begin()}; component < it->end(); component++)
{
if(component->at("Type").get<string>() == "TransformComponent")
{
Vector2D<float> pos {Vector2D<float>(component->at("Position").at("x").get<float>(), component->at("Position").at("y").get<float>())};
Vector2D<float> dim {Vector2D<float>(component->at("Dimension").at("x").get<float>(), component->at("Dimension").at("y").get<float>())};
pos = pos * TILE_SIZE * TILEMAP_SCALE;
e.addComponent<TransformComponent>(pos, dim, component->at("Scale").get<int>(), component->at("Speed").get<float>());
}
else if(component->at("Type").get<string>() == "SpriteComponent")
{
e.addComponent<SpriteComponent>(component->at("Path").get<string>());
}
else if(component->at("Type").get<string>() == "AnimationSystem")
{
int nbFrames {component->at("NbFrames").get<int>()};
int currentFrame {component->at("CurrentFrame").get<int>()};
int frameDelay {component->at("FrameDelay").get<int>()};
bool playAnimation {component->at("PlayAnimation").get<bool>()};
e.addComponent<AnimationSystem>(nbFrames, currentFrame, frameDelay, playAnimation);
}
else if(component->at("Type").get<string>() == "HitboxComponent")
{
Vector2D<float> sc {Vector2D<float>(component->at("Scale").at("x").get<float>(), component->at("Scale").at("y").get<float>())};
Vector2D<float> pos {Vector2D<float>(component->at("Position").at("x").get<float>(), component->at("Position").at("y").get<float>())};
e.addComponent<HitboxComponent>(sc, pos);
}
else if(component->at("Type").get<string>() == "Lever")
{
e.addComponent<Lever>(component->at("Channel").get<int>());
}
else if(component->at("Type").get<string>() == "Lock")
{
e.addComponent<Lock>(component->at("Channel").get<int>());
}
else if(component->at("Type").get<string>() == "Door")
{
unordered_map<int, bool> states {};
for(auto state {component->at("States").begin()}; state < component->at("States").end(); state++)
states[state->at("Channel").get<int>()] = state->at("State").get<bool>();
e.addComponent<Door>(states);
}
}
}
}
Game::entityManager.update();
// Texture renderer creating
if(Game::textureRenderer != NULL) SDL_DestroyTexture(Game::textureRenderer);
Game::textureRenderer = SDL_CreateTexture(Game::renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, worldWidth, worldHeight);
if(Game::textureRenderer == NULL)
{
cerr << "Erreur SDL_CreateTexture : " << SDL_GetError() << '\n';
throw runtime_error("Impossible de creer la texture de rendu.\n");
}
}
// Load the corresponding next map from memory
void TileMap::LoadNextMap(int nextMapNumber)
{
NextMap nextMap = m_nextMaps.at(nextMapNumber);
LoadTileMap(nextMap.path);
m_playerInitPos = nextMap.playerInitPos * TILE_SIZE * TILEMAP_SCALE;
}
// Load a tileset
void TileMap::LoadTileset(const string &path)
{
m_tileset = Game::textureManager.LoadTexture(path);
SDL_QueryTexture(m_tileset, nullptr, nullptr, &m_tilesetWidth, &m_tilesetHeight);
m_tilesetWidth /= TILE_SIZE;
m_tilesetHeight /= TILE_SIZE;
}
// Draw corresponding layer
void TileMap::draw(int layer)
{
if(layer == 1)
{
m_draw(m_tilesLayer1);
}
else if(layer == 2)
{
m_draw(m_tilesLayer2);
}
else if(layer == 3)
{
m_draw(m_tilesLayer3);
}
else
{
cerr << "Erreur : La tileMap " << layer << " n'est pas prise en charge.\n";
throw runtime_error("Impossible de dessiner la tileMap.\n");
}
}
// Draw the layer given
void TileMap::m_draw(const vector<TileID> &tiles)
{
SDL_Rect visibleTilesR;
visibleTilesR.x = Game::camera.camR.x / (TILE_SIZE * TILEMAP_SCALE);
visibleTilesR.y = Game::camera.camR.y / (TILE_SIZE * TILEMAP_SCALE);
visibleTilesR.w = (Game::camera.camR.w + Game::camera.camR.x) / (TILE_SIZE * TILEMAP_SCALE);
visibleTilesR.h = (Game::camera.camR.h + Game::camera.camR.y) / (TILE_SIZE * TILEMAP_SCALE);
for(int tileY {visibleTilesR.y}; tileY <= visibleTilesR.h; tileY++)
{
for(int tileX {visibleTilesR.x}; tileX <= visibleTilesR.w; tileX++)
{
const TileID tile = tiles[tileX + tileY * tileMapWidth] - 1;
if(tile)
{
SDL_Rect srcR {(tile % m_tilesetWidth) * TILE_SIZE, (tile / m_tilesetWidth) * TILE_SIZE, TILE_SIZE, TILE_SIZE};
SDL_Rect dstR {tileX * TILE_SIZE * TILEMAP_SCALE, tileY * TILE_SIZE * TILEMAP_SCALE, TILE_SIZE * TILEMAP_SCALE, TILE_SIZE * TILEMAP_SCALE};
Game::textureManager.Draw(m_tileset, srcR, dstR, false);
}
}
}
}
// Get loaded player inital position
Vector2D<float> TileMap::getPlayerInitPos()
{
return m_playerInitPos;
}
// Destroy texture renderer
TileMap::~TileMap()
{
if(Game::textureRenderer != NULL) SDL_DestroyTexture(Game::textureRenderer);
}