So i have written this code to save and load data for my C++ game to a file, this way I don't have to hard code every single room and item for the game. Essentially here's how the game works: So you have a Map, and a map consists of rooms. A room contains tiles and items. there are 32x18 tiles in a room, and a tile is just a 40x40 image basically, and each tile has a unique numeric ID. An item works similarly, however, it can be at any coordinate in the room and isn't necessarily 40x40 pixels, it has a sprite, and a unique numeric ID, and of course data for it's position in the room (the tiles don't need this because their index in the tiles matrix act as their coordinates, just multiply it's indexes by 40 and you have it's coordinates for screen). There is a RoomBuilder class which my saving and loading feature is in, and this allows me as the developer to click on the screen and place tiles/items which will be added to the current room i'm in. If you need further clarification on how a room system works, think original Legend of Zelda on the NES, how you are in a room then when you go off the screen you are in another room pretty much.
What I do is I Save the data as a string to the output file like this: A line will have Tiles: at the beginning to signify that it is the tiles for that room, there are 576 numbers on that line each separated by a space (32x18 tiles per room = 576). It starts at the top left and saves them row by row, left to right.
When I read the line with Tiles, i save each number into a vector, then loop through that vector at the beginning and set each tile ID in the room based on that vector, so it will start at index 0 in the vector, get that number, go all the way to 32 and save those as the first row of tiles, then go to 64 from there and save those as the second row of tiles, and so on.
For the items, each item in the room has it's own line, it starts with ID: to signify that it's an item, and there are 3 numbers on an item's line in the text file; the first one is it's numeric ID, the second one is it's X position, the third one is it's Y position. I read that, and add an item to the room's vector of items based on the item's ID and position.
The code works, but i'm not happy with it because it's ugly, very finicky, could have bugs I don't know about, and there are parts that I don't fully understand about it (mainly the string stream in the loading method where i have a temporary string and then parse each integer out, I found that code online and tested it, it worked great for my purposes but the point of this project is to learn, and i'm not learning from that since I don't understand it). If I change anything slightly that could break the whole loading/saving system and it will cause a segfault error if that breaks because now I am in a room that doesn't exist since I wasn't able to load any of the rooms.
Feedback is greatly appreciated, I'm looking for things I did wrong with this code, things that could cause it to break, and some tips on how I could improve it, specifically formatting the output and reading it easier.
Saving Method:
bool RoomBuilder::saveRooms(){
//open the output file to save the rooms to:
std::ofstream out("GameData\\DefaultMapRoomBuilder.klvl");
//loop through each room in the rooms vector
for(int i = 0; i < rooms.size(); i++){
//write the tiles matrix for this room to the file:
out << "Tiles: ";
for(int j = 0; j < 32; j++){
for(int k = 0; k < 18; k++){
out << rooms.at(i).getTile(j, k) << " ";
}
}
out << "\n";
//save the items and their positions to the room:
for(int j = 0; j < rooms.at(i).items.size(); j++){
out << "Items: \n";
int itemID = rooms.at(i).items.at(j).getItemID();
float xPos = rooms.at(i).items.at(j).getPosition().x;
float yPos = rooms.at(i).items.at(j).getPosition().y;
out << "ID: " << itemID << " " << xPos << " " << yPos << std::endl;
}
//text to signify end of current room:
out << "END_OF_ROOM" << "\n";
}
return true;
}
loading method:
bool RoomBuilder::loadRooms(){
std::ifstream in("GameData\\DefaultMapRoomBuilder.klvl");
std::vector<std::string> lines;
//only happens if input stream is open:
if(in.is_open()){
//string to store the current line we're on:
std::string line;
//while loop to loop through each line:
while(std::getline(in, line)){
//add each line to the lines vector:
lines.push_back(line);
}
//close the file now that we no longer need it:
in.close();
}
rooms.push_back(Room());
int roomIndex = 0;
for(int i = 0; i < lines.size(); i++){
std::cout << lines.at(i) << std::endl;
if(lines.at(i).find("END_OF_ROOM") != std::string::npos){
if(i != lines.size()-1)
rooms.push_back(Room());
roomIndex++;
std::cout << "loaded room at iteration: " << i << std::endl;
}
else if(lines.at(i).find("Tiles:") != std::string::npos){
std::vector<int> tiles;
std::stringstream ss;
ss << lines.at(i);
std::string temp;
int found;
while(!ss.eof()){
ss >> temp;
if(std::stringstream(temp) >> found)
tiles.push_back(found);
temp = "";
}
int tIndex = 0;
for(int x = 0; x < 32; x++){
for(int y = 0; y < 18; y++){
rooms.at(roomIndex).setTile(x, y, tiles[tIndex]);
tIndex++;
}
}
}
else if(lines.at(i).find("ID:") != std::string::npos){
std::vector<int> itemInfo;
std::stringstream ss;
ss << lines.at(i);
std::string temp;
int found;
while(!ss.eof()){
ss >> temp;
if(std::stringstream(temp) >> found)
itemInfo.push_back(found);
temp = "";
}
Item I = *Materials::getItem(itemInfo[0]);
I.setPosition(sf::Vector2f(itemInfo[1], itemInfo[2]));
rooms.at(roomIndex).addItem(I);
std::cout << "added item at iteration: " << i << std::endl;
}
}
return true;
}
```