#include "Map.hpp" #include bool operator==(uint32_t lhs, TileType rhs) { return lhs == static_cast(rhs); } bool operator&(uint8_t lhs, MapFlags rhs) { return lhs & static_cast(rhs); } bool operator==(int lhs, TileType rhs) { return lhs == static_cast(rhs); } auto operator<=>(int lhs, TileType rhs) { return lhs <=> static_cast(rhs); } MapError Map::populate_vertex_array() { if (!tiles) return {"Map: [Loading] Tilemap is not loaded", 2}; atlas.vertices.setPrimitiveType(sf::PrimitiveType::Triangles); atlas.vertices.resize(map_size.x * map_size.y * 6); const auto xw = static_cast(atlas.tile_size.x); const auto yw = static_cast(atlas.tile_size.y); const auto tex_w = static_cast(atlas.tile_size.x); const auto tex_h = static_cast(atlas.tile_size.y); for (uint32_t x = 0; x < map_size.y; x++) { for (uint32_t y = 0; y < map_size.z; y++) { const int32_t tile = tiles[x + y * map_size.y]; if (tile < -atlas.wall_tiles || tile > atlas.other_tiles) return {"Map: [Loading] Invalid tile! Tile type does not have a texture; ID: " + std::to_string(tile), 3}; // map tile type to atlas /* * Atlas: * +++++ * +++-- * ---## * * + = wall * # = special * - = other * * wall_tiles = 8 * other_tiles = 5 * special_tiles = 2 * * Tile: * -1 <= wall * 0 == special @ .type == tile_type * 1 >= other */ if (tile == 0) return {"Map: [Loading] [Not Implemented!] Tile is a special tile", 3}; // atlas tile index const uint32_t atlas_tile = atlas.wall_tiles + (tile - 1); if (atlas_tile >= atlas.wall_tiles + atlas.other_tiles) return {"Map: [Loading] Invalid tile! Tile type does not have a texture", 3}; // add the vertices const auto xf = static_cast(x * atlas.tile_size.x); const auto yf = static_cast(y * atlas.tile_size.y); const uint32_t tile_x = atlas_tile % atlas.tile_count.x; const uint32_t tile_y = atlas_tile / atlas.tile_count.x; const auto tex_x = static_cast(tile_x * atlas.tile_size.x); const auto tex_y = static_cast(tile_y * atlas.tile_size.y); sf::Vertex tl = {{xf, yf}, sf::Color::White, {tex_x, tex_y}}; sf::Vertex tr = {{xf + xw, yf}, sf::Color::White, {tex_x + tex_w, tex_y}}; sf::Vertex bl = {{xf, yf + yw}, sf::Color::White, {tex_x, tex_y + tex_h}}; sf::Vertex br = {{xf + xw, yf + yw}, sf::Color::White, {tex_x + tex_w, tex_y + tex_h}}; atlas.vertices.append(tl); atlas.vertices.append(tr); atlas.vertices.append(bl); atlas.vertices.append(bl); atlas.vertices.append(tr); atlas.vertices.append(br); } } return {"Map: [Loading] Success", 0}; } void Map::LoadDefaultTilemap() { atlas.wall_tiles = 0; atlas.other_tiles = 1; atlas.special_tiles = 0; atlas.tile_count = {1, 1}; atlas.tile_size = {16, 16}; if (!atlas.texture.loadFromFile(ASSETS_PATH "/default_tilemap.png")) { throw std::runtime_error("Map: [Loading] Could not load default tilemap -> Fatal"); } } Map::Map(): atlas() { } #include #include #include MapError Map::load(const std::filesystem::path& path) { // opening the file and checking the magic number std::ifstream file(path, std::ios::binary | std::ios::in); if (!file.is_open()) { return {"Map: [Loading] Could not open file: " + path.string(), 1}; } char magic[4]; file.read(magic, 4); if (std::memcmp(magic, "CYMF", 4) != 0) { return {"Map: [Loading] Invalid magic number: " + std::string(magic, 4) + " (expected CYMF)", 1}; } // reading the version uint8_t version[3]; file.read(reinterpret_cast(version), 3); if (version[0] != MAP_VERSION_MAJOR || version[1] > MAP_VERSION_MINOR) { return {"Map: [Loading] Invalid version; Current: " MAP_VERSION_STRING, 1}; } // reading the flags uint8_t flags; file.read(reinterpret_cast(&flags), 1); // flag CustomTilemap -> load the tilemap if (flags & MapFlags::CustomTilemap) { uint32_t filepath_buffer_size; file.read(reinterpret_cast(&filepath_buffer_size), 4); char* filepath_buffer = new char[filepath_buffer_size]; file.read(filepath_buffer, filepath_buffer_size); return {"Map: [Loading] [Not Implemented] Custom Tilemap is not implemented. Tilemap file: " + std::string(filepath_buffer, filepath_buffer_size), 1}; delete[] filepath_buffer; } else { LoadDefaultTilemap(); } // load the map info uint32_t map_info[3]; file.read(reinterpret_cast(map_info), 12); map_size = sf::Vector3(static_cast(map_info[0])*map_info[1], map_info[0], map_info[1]); // load the tiles tiles = new int[map_info[0] * map_info[1]]; for (int i = 0; i < map_info[0] * map_info[1]; i++) { // read the special if needed file.read(reinterpret_cast(&tiles[i]), 4); if (tiles[i] == TileType::Special) { MapSpecial special{}; file.read(reinterpret_cast(&special), sizeof(MapSpecial)); // get position uint32_t tile_x = i % map_info[0]; uint32_t tile_y = i / map_info[0]; specials.emplace_back(std::pair({tile_x, tile_y}, special)); } } // load the player spawn uint32_t t_player_spawn[2]; file.read(reinterpret_cast(t_player_spawn), 8); player_spawn = sf::Vector2u{t_player_spawn[0], t_player_spawn[1]}; if (player_spawn.x >= map_size.y || player_spawn.y >= map_size.z) { return {"Map: [Loading] Player: Spawn out of bounds", 2}; } int player_tile = tiles[player_spawn.x + player_spawn.y * map_size.x]; if (player_tile <= TileType::Special) { return {"Map: [Loading] Player: Spawn is not a valid tile (invalid: wall, special; valid: Floor)", 2}; } // load all the enemy spawns uint32_t number_enemy_spawns; file.read(reinterpret_cast(&number_enemy_spawns), 4); if (number_enemy_spawns > map_size.x) { return {"Map: [Loading] Enemies: Too many enemy spawns (More than tile in the map)", 3}; } for (int i = 0; i < number_enemy_spawns; i++) { uint32_t t_enemy_spawn[2]; file.read(reinterpret_cast(t_enemy_spawn), 8); if (t_enemy_spawn[0] >= map_size.x || t_enemy_spawn[1] >= map_size.y) { return {"Map: [Loading] Enemy: Spawn out of bounds", 4}; } if (tiles[t_enemy_spawn[0] + t_enemy_spawn[1] * map_size.y] <= TileType::Special) { return {"Map: [Loading] Enemy: Spawn is not a valid tile (invalid: wall, special; valid: Floor)", 4}; } if (t_enemy_spawn[0] == player_spawn.x && t_enemy_spawn[1] == player_spawn.y) { return {"Map: [Loading] Enemy: Spawn is the same as the player spawn", 4}; } enemy_spawns.emplace_back(t_enemy_spawn[0], t_enemy_spawn[1]); } // Everything went well return populate_vertex_array(); } sf::Vector2u Map::getSize() const { return {static_cast(map_size.y), static_cast(map_size.z)}; } void Map::draw(sf::RenderTarget& target, sf::RenderStates states) const { if (!tiles) return; // apply the transform states.transform *= getTransform(); // apply the tileset texture states.texture = &atlas.texture; // draw the vertex array target.draw(atlas.vertices, states); } #ifdef _DEBUG void Map::debug() { std::cout << "Map Debug" << std::endl; std::cout << "\tSize : " << map_size.x << ", " << map_size.y << ", " << map_size.z << std::endl; std::cout << "\tTiles : " << tiles << std::endl; for (auto& special : specials) { std::cout << "\t\tSpecial : " << special.first.x << ", " << special.first.y << std::endl; std::cout << "\t\t\tType : " << special.second.type << std::endl; std::cout << "\t\t\tPortal : " << special.second.portal_end[0] << ", " << special.second.portal_end[1] << std::endl; } std::cout << "\t\tEnemy Spawns : " << enemy_spawns.size() << std::endl; for (auto& spawn : enemy_spawns) { std::cout << "\t\t\tSpawn : " << spawn.x << ", " << spawn.y << std::endl; } std::cout << "\t\tPlayer Spawn : " << player_spawn.x << ", " << player_spawn.y << std::endl; } #endif