#include #include "game.h" class ConfigBuilder{ public: ConfigData collectedData; void parseFile(const string& fname); void readConfig(); void dumpInternalValues(); private: map internalValues; string& getString(const configKey& key); char getChar(const configKey& key); int getInt(const configKey& key); nsGraphics::RGBAcolor getColor(const configKey& key); }; void ConfigBuilder::dumpInternalValues(){ for(const auto& ite : internalValues){ cerr << ite.first << " -> " << ite.second << endl; } } void trimSpaces(string& str){ str.erase(0, str.find_first_not_of(' ')); } void ConfigBuilder::parseFile(const string& fname) { ifstream file(fname); if(!file.is_open())throw runtime_error("Error while opening config.yml. Check file location ?"); vector keyParts; while (!file.eof()) { string line; getline(file, line); auto match = line.find('#'); if (match != string::npos)line.erase(match); if (line.empty())continue; unsigned currentIndent = 0; while (line[currentIndent] == ' ')++currentIndent; match = line.find(':'); if (match == string::npos)throw runtime_error("Invalid line : " + line); string key = line.substr(0, match); string value = line.substr(match + 1); trimSpaces(key); trimSpaces(value); if (value.empty()) { keyParts.resize(currentIndent); keyParts.push_back(key); } else { string fullKey; for (unsigned i = 0; i < currentIndent; ++i) { fullKey.append(keyParts[i]); fullKey.append("."); } fullKey.append(key); internalValues[fullKey] = value; } } file.close(); } void ConfigBuilder::readConfig() { // TODO PUT THAT IN CONFIG ?? #define S 48 collectedData.grid.resize(S); for(unsigned i=0;i(j); } // players collectedData.playersWidth = getInt("players.width"); collectedData.startXPosition = getInt("players.startXPosition"); collectedData.playersSpeed = getInt("players.speed"); collectedData.playersFireCooldown = getInt("players.fireCooldown"); // the scalability behind the vector of players is only an illusion, because we force player count to be 1 or 2 // It was done so the 2+ players implementation could be easier in the future, if wanted collectedData.playerDefs.resize(2); collectedData.playerDefs[0].color = getColor("players.user1.color"); collectedData.playerDefs[0].keys.left = getChar("players.user1.keys.left"); collectedData.playerDefs[0].keys.right = getChar("players.user1.keys.right"); collectedData.playerDefs[0].keys.shoot = getChar("players.user1.keys.shoot"); collectedData.playerDefs[1].color = getColor("players.user2.color"); collectedData.playerDefs[1].keys.left = getChar("players.user2.keys.left"); collectedData.playerDefs[1].keys.right = getChar("players.user2.keys.right"); collectedData.playerDefs[1].keys.shoot = getChar("players.user2.keys.shoot"); // invaders collectedData.invadersSize = getInt("invaders.size"); collectedData.invadersSpeed = getInt("invaders.speed"); collectedData.invadersDistance = getInt("invaders.distance"); collectedData.invadersFireCooldown = getInt("invaders.fireCooldown"); // projectiles collectedData.missilesWidth = getInt("projectiles.missiles.width"); collectedData.missilesLength = collectedData.missilesWidth*PROJ_LENGTH_FACTOR; collectedData.missilesSpeed = getInt("projectiles.missiles.speed"); collectedData.missilesColor = getColor("projectiles.missiles.color"); collectedData.torpedosWidth = getInt("projectiles.torpedos.width"); collectedData.torpedosLength = collectedData.torpedosWidth*PROJ_LENGTH_FACTOR; collectedData.torpedosSpeed = getInt("projectiles.missiles.speed"); collectedData.torpedosColor = getColor("projectiles.torpedos.color"); } int ConfigBuilder::getInt(const configKey& key) { return stoi(getString(key)); } char ConfigBuilder::getChar(const configKey& key) { return getString(key)[0]; } string& ConfigBuilder::getString(const configKey& key) { if(internalValues.contains(key)){ return internalValues.at(key); }else{ throw runtime_error("Non-existent key requested : "+key); } } nsGraphics::RGBAcolor ConfigBuilder::getColor(const configKey& key) { // switch do not work with strings, and I don't want to implement a constexpr hash function string colorStr = getString(key); if (colorStr == "black")return nsGraphics::KBlack; else if (colorStr == "white")return nsGraphics::KWhite; else if (colorStr == "red")return nsGraphics::KRed; else if (colorStr == "lime")return nsGraphics::KLime; else if (colorStr == "blue")return nsGraphics::KBlue; else if (colorStr == "yellow")return nsGraphics::KYellow; else if (colorStr == "cyan")return nsGraphics::KCyan; else if (colorStr == "magenta")return nsGraphics::KMagenta; else if (colorStr == "silver")return nsGraphics::KSilver; else if (colorStr == "gray")return nsGraphics::KGray; else if (colorStr == "maroon")return nsGraphics::KMaroon; else if (colorStr == "olive")return nsGraphics::KOlive; else if (colorStr == "green")return nsGraphics::KGreen; else if (colorStr == "purple")return nsGraphics::KPurple; else if (colorStr == "teal")return nsGraphics::KTeal; else if (colorStr == "navy")return nsGraphics::KNavy; else throw runtime_error("Invalid color string : "+colorStr); } /** * * @return false if there was an error */ bool Game::reloadConfig() { map strValues; ConfigBuilder builder; bool parsed = false; try{ builder.parseFile("config.yml"); parsed = true; builder.readConfig(); }catch(runtime_error& e){ if(parsed)cerr << "An error occured while reading the configuration :" << endl; else cerr << "An error occured while parsing the configuration :" << endl; cerr << e.what() << endl; if(parsed){ cerr << "Parsed keys :" << endl; builder.dumpInternalValues(); } cerr << "(The old configuration was kept in memory)" << endl; return false; } confData = builder.collectedData; return true; }