← index
2D.TileMap/level.cpp
Source: 2D.TileMap/level.cpp
#include"level.hpp"
#include<iostream>
#include<sstream>
#include<string>
#include<fstream>
#include<vector>

namespace game {  

    // tile copy constructor
    Tile::Tile(const Tile &tile) {
        set(tile);
    }

    // tile constructor
    Tile::Tile(uint8_t c, uint8_t s, uint8_t i) : color{c}, solid{s}, img{i} {
        layers[0] = 0;
        layers[1] = 0;
        layers[2] = 0;
    }

    // tile assignment operator
    Tile& Tile::operator=(const Tile &tile) {
        set(tile);
        return *this;
    }
    // set tile with another tile
    void Tile::set(const Tile &tile) { 
        color = tile.color;
        img = tile.img;
        solid = tile.solid;
        layers[0] = tile.layers[0];
        layers[1] = tile.layers[1];
        layers[2] = tile.layers[2];
    }
    // set tile
    void Tile::setTile(const Tile &tile) {
        color = tile.color;
        img = tile.img;
        solid = tile.solid;
    }

   // level default constructor
   Level::Level() : width{0}, height{0}, tiles{nullptr} {

   }

    // level destructor
    Level::~Level() {
        releaseTiles();
    }

    // load level
    bool Level::loadLevel(const std::string &filename) {
        std::fstream file;
        file.open(filename, std::ios::in | std::ios::binary);
        if(!file.is_open()) 
            return false;
        return unserialize(file);
    }

    bool Level::loadLevelText(const std::string &filename) {
        std::fstream file;
        file.open(filename, std::ios::in);
        if(!file.is_open()) 
            return false;
        return unserialize_text(file);
    }

    bool Level::loadLevelByName(const std::string &filename) {
        if(filename.find(".txt") == std::string::npos)
            return loadLevel(filename);
        
        return loadLevelText(filename);
    }

    bool Level::saveLevelByName(const std::string &filename) {
        if(filename.find(".txt") == std::string::npos)
            return saveLevel(filename);

        return saveLevelText(filename);
    }

    // save level
    bool Level::saveLevel(const std::string &filename) {
        std::fstream file;
        file.open(filename, std::ios::out | std::ios::binary);
        if(!file.is_open()) 
            return false;
        return serialize(file);
    }

    bool Level::saveLevelText(const std::string &filename) {
        std::fstream file;
        file.open(filename, std::ios::out);
        if(!file.is_open())
            return false;
        serialize_text(file);
        return true;
    }

    // create new map
    void Level::create(int32_t w, int32_t h, const Tile &init_tile) {
        resizeTiles(w, h);
        for(int i = 0; i < w; ++i) {
            for(int z = 0; z < h; ++z) {
                tiles[i][z] = init_tile;
            }
        }
    }

    // tile at location x,y
    Tile *Level::at(int32_t x, int32_t y) {
        if(x >= 0 && x < width && y >= 0 && y < height) {
            return &tiles[x][y];
        }
        return nullptr;
    }
    // output map to stdout
    void Level::output_map(std::ostream &out) {
        if(width == 0 || height == 0 || tiles == nullptr) {
            out << "[empty map]\n";
            return;
        }
        for(int x = 0; x < width; ++x) {
            for(int y = 0; y < height; ++y) {
                Tile *t = at(x,y);
                if(t != nullptr) {
                     out << "x: " << x << ", y: " << y << " " << t->color << "\n";
                }
            }
        }
    }

    // resize tiles
    bool Level::resizeTiles(int32_t w, int32_t h) {
        releaseTiles();
        width = w;
        height = h;
        tiles = new Tile*[width];
        for(int i = 0; i < width; ++i) {
            tiles[i] = new Tile[height];
        }
        return true;
    }

    // release the tiles
    void Level::releaseTiles() {
        if(tiles != nullptr && width != 0) {
            for(int i = 0; i < width; ++i) {
                delete [] tiles[i];
            }
            delete [] tiles;
            tiles = nullptr;
        }
    }

    // serialize - save to stream
   bool Level::serialize(std::ostream &out) {
        if(tiles == nullptr) return false;
        uint32_t type = 0x420;
        out.write(reinterpret_cast<char*>(&type), sizeof(type));
        uint32_t name_len = static_cast<uint32_t>(level_name.length());
        out.write(reinterpret_cast<char*>(&name_len), sizeof(name_len));
        out.write(reinterpret_cast<const char*>(level_name.c_str()), name_len);
        out.write(reinterpret_cast<char*>(&width), sizeof(width));
        out.write(reinterpret_cast<char*>(&height), sizeof(height));
        for(int i = 0; i < width; ++i) {
            for(int z = 0; z < height; ++z) {
                out.write(reinterpret_cast<char*>(&tiles[i][z]), sizeof(tiles[i][z]));
            }
        }
        return true;
   }

   void Level::serialize_text(std::ostream &out) {
        out << "map " << "\"" << level_name << "\"" << " \"" << width << "x" << height << "\" {\n";
        for(int i = 0; i < width; ++i) {
            for(int z = 0; z < height; ++z) {
                Tile *tile = at(i, z);
                if(tile != nullptr) {
                    out << "{ " << static_cast<int>(tile->color) << " " << static_cast<int>(tile->img) << " " << static_cast<int>(tile->solid) << " " << static_cast<int>(tile->layers[0]) << " " << static_cast<int>(tile->layers[1]) << " " << static_cast<int>(tile->layers[2]) << " }\n";
                }
            }
        }
        out << "};\n";
   }


   // unserialize - construct from stream
   bool Level::unserialize(std::istream &in) {
        uint32_t type = 0;
        in.read(reinterpret_cast<char*>(&type), sizeof(type));
        if(type != 0x420) return false;
        char *tmp;
        uint32_t name_len = 0;
        in.read(reinterpret_cast<char*>(&name_len), sizeof(name_len));
        tmp = new char [ name_len + 1 ];
        in.read(reinterpret_cast<char*>(tmp), name_len);
        tmp[name_len] = 0;
        level_name = tmp;
        delete [] tmp;
        releaseTiles();
        in.read(reinterpret_cast<char*>(&width), sizeof(width));
        in.read(reinterpret_cast<char*>(&height), sizeof(height));
        create(width, height, game::Tile{});
        for(int i = 0; i < width; ++i) {
            for(int z = 0; z < height; ++z) {
                in.read(reinterpret_cast<char*>(&tiles[i][z]), sizeof(tiles[i][z]));
            }
        }
        return true;
   }

   bool Level::unserialize_text(std::istream &in) {
        std::string input;
        std::getline(in, input);
        if(in) {
            auto start_pos = input.find("\"");
            auto stop_pos = input.find("\"", start_pos+1);            
            std::string text = input.substr(start_pos+1, stop_pos-start_pos-1);
            level_name = text;
            start_pos = input.find("\"", stop_pos+1);
            stop_pos = input.find("\"", start_pos+1);
            text = input.substr(start_pos+1, stop_pos-start_pos-1);
            auto x = text.find("x");
            std::string left = text.substr(0, x);
            std::string right = text.substr(x+1, text.length()-x);
            width = atoi(left.c_str());
            height = atoi(right.c_str());
            std::cout << "Map: " << level_name << " size " << width << "X" << height << "\n";
            // todo construct level structure
            releaseTiles();
            create(width, height, game::Tile{});
            for (int x = 0; x < width; ++x) {
                for (int y = 0; y < height; ++y) {
                    std::string line;
                    std::getline(in, line);
                    if(in) {
                        std::string text_line = line.substr(1, line.length()-2);
                        std::istringstream stream(text_line);
                        int color = 0, img = 0, solid = 0, layer1 = 0, layer2 = 0, layer3 = 0;
                        game::Tile *t = at(x, y);
                        if(t != nullptr) {
                            stream >> color >> img >> solid >> layer1 >> layer2 >> layer3;
                            t->color = color;
                            t->img = img;
                            t->solid = solid;
                            t->layers[0] = layer1;
                            t->layers[1] = layer2;
                            t->layers[2] = layer3;
                        }
                    }
                }
            } 
        } else {
            return false;
        }

        return true;
   }

    // check Type
    bool Level::checkType(const Point &p, int type) {
        Tile *tile = at(p.x, p.y);
        if(tile != nullptr) {
            if(tile->solid == type)
                return true;
        }
        return false;
    }
    
    // check multiple types
    bool Level::checkTypes(const std::vector<Point> &p, int type) {
        for(auto &i : p) {
            if(checkType(i, type) == false)
                return false;
        }
        return true;
    }

    // check tile solid
    bool Level::checkTileSolid(int x, int y) {
        Tile *tile = at(x, y);
        if(tile != nullptr) {
            if(tile->solid == 0)
                return false;
        }
        return true;
    }

    // check solid from Point
    bool Level::checkSolid(const Point &p) {
        return checkTileSolid(p.x, p.y);
    }

    // check solid from multiple points
    bool Level::check(const std::vector<Point> &p) {
        for(auto &i : p) {
            if(checkTileSolid(i.x, i.y))
                return true;
        }
        return false;
    }    

    // check if rect contains a solid
    bool Level::checkRect(const Rect &r) {
        for(int i = r.x; i <= r.x+r.w; ++i) {
            for(int z = r.y; z <= r.y+r.h; ++z) {
                Tile *tile = at(i, z);
                if(tile != nullptr) {
                    if(tile->solid == 1)
                        return false;
                } else return false;
            }
        }
        return true;
    }

    // check if rect contains a type
    bool Level::checkRectForType(const Rect &r, int type) {
        for(int i = r.x; i <= r.x+r.w; ++i) {
            for(int z = r.y; z <= r.y+r.h; ++z) {
                Tile *tile = at(i, z);
                if(tile != nullptr) {
                    if(tile->solid == type)
                        return true;
                }
            }
        }
        return false;
    }

    // check multiple rects for type
    bool Level::checkRectsForType(const std::vector<Rect> &v, int type) {
        for(auto &i : v) {
            if(!checkRectForType(i, type))
                return false;
        }
        return true;
    }

    // set level name
    void Level::setLevelName(const std::string &name) {
         level_name = name;
    }
  
    // get level name
    std::string Level::getLevelName() const {
        return level_name;  
    }

    // at Point
    bool atPoint(int x1, int y1, int w, int h, int &x, int &y) {
       x = x1/w;
       y = y1/h;
       if(x >= 0 && y >= 0)
            return true;
        return false;
    }

    // is Point in Rect?
    bool PointInRect(const Point &p, const Rect &r) {
        if(p.x >= r.x && p.x <= r.x+r.w && p.y >= r.y && p.y <= r.y+r.h)
            return true;
        return false;
    }

}