178 lines
5.9 KiB
C++
178 lines
5.9 KiB
C++
#include <fstream>
|
|
#include "game.h"
|
|
|
|
class ConfigBuilder{
|
|
public:
|
|
ConfigData collectedData;
|
|
void parseFile(const string& fname);
|
|
void readConfig();
|
|
void dumpInternalValues();
|
|
private:
|
|
map<string, string> 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<string> 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<S;++i){
|
|
collectedData.grid[i].resize(3);
|
|
for(unsigned j=0;j<3;++j)collectedData.grid[i][j] = static_cast<Invader>(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<string, string> 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;
|
|
}
|