SUPER Space invader : Turbo edition DX - VS GOD 1.0.0
A simple space invader ripoff
configManagement.cpp
Go to the documentation of this file.
1
12#include <fstream>
13#include "game.h"
14#include "configManagement.h"
15#include "errors.h"
16
17
18void trimSpaces(string& str){
19 str.erase(0, str.find_first_not_of(' '));
20}
21
22void sanitizeValue(string& val) {
23 trimSpaces(val);
24 for (char c: {'\'', '"'}) {
25 if (val[0] == c && val[val.size() - 1] == c) {
26 val.erase(val.begin());
27 val.pop_back();
28 break;
29 }
30 }
31}
32
34 for(const auto& ite : internalValues){
35 cerr << ite.first << " -> " << ite.second << endl;
36 }
37}
38
39/* WARNING : This implementation of YAML is not meant to be complete, but to work with our specific needs
40 * It also can't detect and report errors in a non-YAML-compliant file*/
41
42void ConfigBuilder::parseFile(const string& fname) {
43 ifstream file(fname);
44 if(!file.is_open())throw config_error("Error while opening config.yml. Check file location ?");
45
46 vector<string> keyParts;
47 unsigned listIndex;
48 unsigned lineno = 0; // for error handling purposes
49 while (!file.eof()) {
50 string line;
51 getline(file, line);
52 ++lineno;
53 auto match = line.find('#');
54 if (match != string::npos)line.erase(match);
55 if (line.find_first_not_of(' ')==string::npos)continue;
56
57 unsigned currentIndent = 0;
58 while (line[currentIndent] == ' ')++currentIndent;
59
60 if(line[currentIndent]=='-'){
61 string value = line.substr(currentIndent+1);
62 sanitizeValue(value);
63
64 string fullKey;
65 for (unsigned i = 0; i < currentIndent; ++i) {
66 fullKey.append(keyParts[i]);
67 fullKey.append(".");
68 }
69 // lists are just treated as sections with key 0,1,2...
70 fullKey.append(to_string((listIndex)));
71 ++listIndex;
72 internalValues[fullKey] = value;
73
74 }else{
75 match = line.find(':');
76 if (match == string::npos)throw config_error("Line "+ to_string(lineno)+" invalid : |"+line+"|");
77 string key = line.substr(0, match);
78 string value = line.substr(match + 1);
79 trimSpaces(key);
80 sanitizeValue(value);
81 if (value.empty()) {
82 keyParts.resize(currentIndent);
83 keyParts.push_back(key);
84 listIndex = 0;
85 } else {
86 string fullKey;
87 for (unsigned i = 0; i < currentIndent; ++i) {
88 fullKey.append(keyParts[i]);
89 fullKey.append(".");
90 }
91 fullKey.append(key);
92 internalValues[fullKey] = value;
93 }
94 }
95 }
96
97 file.close();
98}
99
100void ConfigBuilder::readGrid(const configKey& baseKey) {
101 vector<string> tmp;
102 getList("grid", tmp);
103
104 // we are essentially going to translate a line-oriented config to a column-oriented grid
105
106 unsigned maxSize = 0;
107 for(string& s : tmp){
108 if(s.size()>maxSize)maxSize = s.size();
109 }
110 collectedData.grid.resize(maxSize);
111
112 for(string& s : tmp){
113 unsigned i=0;
114 for(;i<s.size();++i){
115 switch(toupper(s[i])){
116 case 'A':{
118 break;
119 }
120 case 'B':{
122 break;
123 }
124 case 'C':{
126 break;
127 }
128 case ' ':{
130 break;
131 }
132 default:{
133 throw config_error("Invalid invader ID in grid definition : "+ to_string(s[i]));
134 }
135 }
136 }
137 while(i<maxSize){
139 ++i;
140 }
141 }
142}
143
144void ConfigBuilder::readPlayer(const configKey& baseKey, PlayerDef& pdef) {
145 getColor(baseKey+".color", pdef.color);
146 pdef.keys.left = getChar(baseKey+".keys.left");
147 pdef.keys.right = getChar(baseKey+".keys.right");
148 pdef.keys.shoot = getChar(baseKey+".keys.shoot");
149}
150
151void ConfigBuilder::readInvaderType(const configKey& baseKey, InvaderTypeDef& invDef) {
152 invDef.points = getInt(baseKey+".points");
153 getColor(baseKey+".color", invDef.color);
154}
155
157
158 collectedData.theme = getString("general.theme", "bad");
159 collectedData.maxFPS = getInt("general.maxFPS", 30, 1, 60);
160
161 readGrid("grid");
162
163 if (collectedData.theme != "bad"){
168
169 }
170 else{
171 collectedData.playersWidth = getInt("players.width", 100, 50, 500);
172 collectedData.invadersSize = getInt("invaders.size",30,10,100);
173 collectedData.missilesWidth = getInt("projectiles.missiles.width",10,5,100);
174 collectedData.torpedosWidth = getInt("projectiles.torpedos.width",10,1,100);
175 }
176
177 // players
178
179 collectedData.startXPosition = getInt("players.startXPosition",600 ,0 ,1200);
180 collectedData.playersSpeed = getInt("players.speed",1,1,100);
181 collectedData.playersFireCooldown = getInt("players.fireCooldown",10,1,100);
182 collectedData.playersLives = getInt("players.lives",3,1,100);
183
184 // the scalability behind the vector of players is only an illusion, because we force player count to be 1 or 2
185 // It was done so the 2+ players implementation could be easier in the future, if wanted
186 collectedData.playerDefs.resize(2);
187 readPlayer("players.user1", collectedData.playerDefs[0]);
188 readPlayer("players.user2", collectedData.playerDefs[1]);
189
190 // invaders
191
192 collectedData.invadersSpeed = getInt("invaders.speed",7,1,100);
193 collectedData.invadersDistance = getInt("invaders.distance",15,5,100);
194 collectedData.invadersFireCooldown = getInt("invaders.fireCooldown",0,0,100);
195
196 readInvaderType("invaders.typeA", collectedData.invadersDef[InvaderType::TYPEA]);
197 readInvaderType("invaders.typeB", collectedData.invadersDef[InvaderType::TYPEB]);
198 readInvaderType("invaders.typeC", collectedData.invadersDef[InvaderType::TYPEC]);
199
200 // projectiles
202 collectedData.missilesSpeed = getInt("projectiles.missiles.speed",10,5,100);
203 getColor("projectiles.missiles.color", collectedData.missilesColor);
204
206 collectedData.torpedosSpeed = getInt("projectiles.torpedos.speed",10,1,100);
207 getColor("projectiles.torpedos.color", collectedData.torpedosColor);
208}
209
210const string& ConfigBuilder::getString(const configKey& key, const string& def) const {
211 try{
212 return getString(key);
213 }catch(config_error& e){
214 cerr << e.what() << " . Using default value" << endl;
215 return def;
216 }
217}
218
219const string& ConfigBuilder::getString(const configKey& key) const {
220 DEBUG_MSG("Querying config key " << key)
221 if(internalValues.contains(key)){
222 // We don't really care about querying the key two time since we are... well, in debug mode
223 DEBUG_MSG("Got config value " << internalValues.at(key))
224 return internalValues.at(key);
225 }else{
226 throw config_error("Non-existent key requested : "+key);
227 }
228}
229
230int ConfigBuilder::getInt(const configKey& key, int def, int min, int max) const {
231 try{
232 int val = getInt(key);
233 if(val < min || val > max){
234 throw config_error("Value for key " + key + " do not follow preconditions : " +
235 to_string(min) + "<=" + to_string(val) + "<=" + to_string(max));
236 }
237 return val;
238 }catch(config_error& e){
239 cerr << e.what() << " . Using default value" << endl;
240 return def;
241 }
242}
243
244int ConfigBuilder::getInt(const configKey& key) const {
245 try{
246 return stoi(getString(key));
247 }catch(invalid_argument& e){
248 throw config_error("Invalid int data for key "+key+" : |"+getString(key)+"|");
249 }
250}
251
252char ConfigBuilder::getChar(const configKey& key, char def) const {
253 try{
254 return getChar(key);
255 }catch(config_error& e){
256 cerr << e.what() << " . Using default value" << endl;
257 return def;
258 }
259}
260
261char ConfigBuilder::getChar(const configKey& key) const {
262 string s = getString(key);
263 if(s.size()!=1)throw config_error("Invalid char data for key "+key+" : |"+s+"|");
264 return s[0];
265}
266
267void ConfigBuilder::getColor(const configKey& key, nsGraphics::RGBAcolor& color, const nsGraphics::RGBAcolor& def) const {
268 try{
269 getColor(key, color);
270 }catch(config_error& e){
271 cerr << e.what() << " . Using default value" << endl;
272 color = def;
273 }
274}
275
276void ConfigBuilder::getColor(const configKey& key, nsGraphics::RGBAcolor& color) const {
277 // switch do not work with strings, and I don't want to implement a constexpr hash function
278 string colorStr = getString(key);
279 if (colorStr == "black")color = nsGraphics::KBlack;
280 else if (colorStr == "white")color = nsGraphics::KWhite;
281 else if (colorStr == "red")color = nsGraphics::KRed;
282 else if (colorStr == "lime")color = nsGraphics::KLime;
283 else if (colorStr == "blue")color = nsGraphics::KBlue;
284 else if (colorStr == "yellow")color = nsGraphics::KYellow;
285 else if (colorStr == "cyan")color = nsGraphics::KCyan;
286 else if (colorStr == "magenta")color = nsGraphics::KMagenta;
287 else if (colorStr == "silver")color = nsGraphics::KSilver;
288 else if (colorStr == "gray")color = nsGraphics::KGray;
289 else if (colorStr == "maroon")color = nsGraphics::KMaroon;
290 else if (colorStr == "olive")color = nsGraphics::KOlive;
291 else if (colorStr == "green")color = nsGraphics::KGreen;
292 else if (colorStr == "purple")color = nsGraphics::KPurple;
293 else if (colorStr == "teal")color = nsGraphics::KTeal;
294 else if (colorStr == "navy")color = nsGraphics::KNavy;
295 else throw config_error("Invalid color string : "+colorStr);
296}
297
298void ConfigBuilder::getList(const configKey& baseKey, vector<string>& toPopulate) const {
299 unsigned i=0;
300 string fullKey = baseKey + ".0";
301 if(!internalValues.contains(fullKey))throw config_error("Non-existent list baseKey requested : " + baseKey);
302
303 do{
304 toPopulate.push_back(internalValues.at(fullKey));
305 ++i;
306 fullKey = baseKey + "." + to_string(i);
307 }while(internalValues.contains(baseKey + "." + to_string(i)));
308}
309
310
312 map<string, string> strValues;
313 ConfigBuilder builder;
314 bool parsed = false;
315 try{
316 builder.parseFile("config.yml");
317 parsed = true;
318 builder.readConfig();
319 }catch(config_error& e){
320 if(parsed)cerr << "An error occured while reading the configuration :" << endl;
321 else cerr << "An error occured while parsing the configuration :" << endl;
322 cerr << e.what() << endl;
323 if(parsed){
324 cerr << "Parsed keys :" << endl;
325 builder.dumpInternalValues();
326 }
327 cerr << "(The old configuration was kept in memory)" << endl;
328 return false;
329 }
330 confData = move(builder.collectedData);
331 return true;
332}
temporary class used to populate a ConfigData object
void parseFile(const string &fname)
parse the file given, and put the keys/values in internalValues
ConfigData collectedData
Actual config data object.
void dumpInternalValues() const
print internalValues pairs in the console (in case in error reporting)
bool reloadConfig()
reload the configuration file (not dynamically used currently, but it was planned to be)
Simple clone of runtime_error to help us handle errors in config management.
Definition: errors.h:23
string configKey
Definition: configData.h:20
void trimSpaces(string &str)
void sanitizeValue(string &val)
config parser
full game logic and display management
unsigned startXPosition
players horizontal start position
Definition: configData.h:47
unsigned playersLives
player life points
Definition: configData.h:67
unsigned invadersFireCooldown
wait time between two invader missile
Definition: configData.h:92
unsigned playersSpeed
player movement speed
Definition: configData.h:52
InvadersGrid grid
Invader type matrix.
Definition: configData.h:42
string theme
theme to use. Valid values : good,bad
Definition: configData.h:32
unsigned missilesWidth
invaders missiles width in pixel
Definition: configData.h:102
unsigned torpedosLength
virtual value : players torpedos length in pixel - auto defined from width
Definition: configData.h:127
unsigned playersFireCooldown
player shooting wait time
Definition: configData.h:62
nsGraphics::RGBAcolor torpedosColor
players torpedos color
Definition: configData.h:137
vector< PlayerDef > playerDefs
players configuration
Definition: configData.h:72
unsigned torpedosSpeed
players topedos movement speed
Definition: configData.h:132
unsigned invadersDistance
distance in pixel between two invader
Definition: configData.h:87
nsGraphics::RGBAcolor missilesColor
invaders missiles color
Definition: configData.h:117
unsigned invadersSpeed
invader movement speed
Definition: configData.h:77
unsigned playersWidth
player horizontal size in pixel
Definition: configData.h:57
unsigned invadersSize
invader radius size in pixel
Definition: configData.h:82
unsigned maxFPS
maximum framerate at which the game will run
Definition: configData.h:37
unsigned missilesSpeed
invaders missiles movement speed
Definition: configData.h:112
unsigned missilesLength
virtual value : invaders missiles length in pixel - auto defined from width
Definition: configData.h:107
unsigned torpedosWidth
players torpedos width in pixel
Definition: configData.h:122
map< InvaderType, InvaderTypeDef > invadersDef
link between an invader type, and its data
Definition: configData.h:97
defines an invader type
Definition: invaderDef.h:21
unsigned points
points given to the player by defeating this invader type
Definition: invaderDef.h:30
nsGraphics::RGBAcolor color
color of the invader type
Definition: invaderDef.h:25
player data, contains colors and key configuration
Definition: playerDef.h:42
PlayerKeys keys
player key configuration
Definition: playerDef.h:52
nsGraphics::RGBAcolor color
player color
Definition: playerDef.h:47
char left
key to move left
Definition: playerDef.h:30
char shoot
key to shoot
Definition: playerDef.h:35
char right
key to move right
Definition: playerDef.h:25
#define PROJ_LENGTH_FACTOR
Definition: utils.h:20
#define DEBUG_MSG(X)
Definition: utils.h:32