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 // players
164 collectedData.playersWidth = getInt("players.width", 100, 50, 500);
165 collectedData.startXPosition = getInt("players.startXPosition",600 ,0 ,1200);
166 collectedData.playersSpeed = getInt("players.speed",1,1,100);
167 collectedData.playersFireCooldown = getInt("players.fireCooldown",10,1,100);
168 collectedData.playersLives = getInt("players.lives",3,1,100);
169
170 // the scalability behind the vector of players is only an illusion, because we force player count to be 1 or 2
171 // It was done so the 2+ players implementation could be easier in the future, if wanted
172 collectedData.playerDefs.resize(2);
173 readPlayer("players.user1", collectedData.playerDefs[0]);
174 readPlayer("players.user2", collectedData.playerDefs[1]);
175
176 // invaders
177 collectedData.invadersSize = getInt("invaders.size",30,10,100);
178 collectedData.invadersSpeed = getInt("invaders.speed",7,1,100);
179 collectedData.invadersDistance = getInt("invaders.distance",15,5,100);
180 collectedData.invadersFireCooldown = getInt("invaders.fireCooldown",0,0,100);
181
182 readInvaderType("invaders.typeA", collectedData.invadersDef[InvaderType::TYPEA]);
183 readInvaderType("invaders.typeB", collectedData.invadersDef[InvaderType::TYPEB]);
184 readInvaderType("invaders.typeC", collectedData.invadersDef[InvaderType::TYPEC]);
185
186 // projectiles
187 collectedData.missilesWidth = getInt("projectiles.missiles.width",10,5,100);
189 collectedData.missilesSpeed = getInt("projectiles.missiles.speed",10,5,100);
190 getColor("projectiles.missiles.color", collectedData.missilesColor);
191
192 collectedData.torpedosWidth = getInt("projectiles.torpedos.width",10,1,100);
194 collectedData.torpedosSpeed = getInt("projectiles.torpedos.speed",10,1,100);
195 getColor("projectiles.torpedos.color", collectedData.torpedosColor);
196}
197
198const string& ConfigBuilder::getString(const configKey& key, const string& def) const {
199 try{
200 return getString(key);
201 }catch(config_error& e){
202 cerr << e.what() << " . Using default value" << endl;
203 return def;
204 }
205}
206
207const string& ConfigBuilder::getString(const configKey& key) const {
208 DEBUG_MSG("Querying config key " << key)
209 if(internalValues.contains(key)){
210 // We don't really care about querying the key two time since we are... well, in debug mode
211 DEBUG_MSG("Got config value " << internalValues.at(key))
212 return internalValues.at(key);
213 }else{
214 throw config_error("Non-existent key requested : "+key);
215 }
216}
217
218int ConfigBuilder::getInt(const configKey& key, int def, int min, int max) const {
219 try{
220 int val = getInt(key);
221 if(val < min || val > max){
222 throw config_error("Value for key " + key + " do not follow preconditions : " +
223 to_string(min) + "<=" + to_string(val) + "<=" + to_string(max));
224 }
225 return val;
226 }catch(config_error& e){
227 cerr << e.what() << " . Using default value" << endl;
228 return def;
229 }
230}
231
232int ConfigBuilder::getInt(const configKey& key) const {
233 try{
234 return stoi(getString(key));
235 }catch(invalid_argument& e){
236 throw config_error("Invalid int data for key "+key+" : |"+getString(key)+"|");
237 }
238}
239
240char ConfigBuilder::getChar(const configKey& key, char def) const {
241 try{
242 return getChar(key);
243 }catch(config_error& e){
244 cerr << e.what() << " . Using default value" << endl;
245 return def;
246 }
247}
248
249char ConfigBuilder::getChar(const configKey& key) const {
250 string s = getString(key);
251 if(s.size()!=1)throw config_error("Invalid char data for key "+key+" : |"+s+"|");
252 return s[0];
253}
254
255void ConfigBuilder::getColor(const configKey& key, nsGraphics::RGBAcolor& color, const nsGraphics::RGBAcolor& def) const {
256 try{
257 getColor(key, color);
258 }catch(config_error& e){
259 cerr << e.what() << " . Using default value" << endl;
260 color = def;
261 }
262}
263
264void ConfigBuilder::getColor(const configKey& key, nsGraphics::RGBAcolor& color) const {
265 // switch do not work with strings, and I don't want to implement a constexpr hash function
266 string colorStr = getString(key);
267 if (colorStr == "black")color = nsGraphics::KBlack;
268 else if (colorStr == "white")color = nsGraphics::KWhite;
269 else if (colorStr == "red")color = nsGraphics::KRed;
270 else if (colorStr == "lime")color = nsGraphics::KLime;
271 else if (colorStr == "blue")color = nsGraphics::KBlue;
272 else if (colorStr == "yellow")color = nsGraphics::KYellow;
273 else if (colorStr == "cyan")color = nsGraphics::KCyan;
274 else if (colorStr == "magenta")color = nsGraphics::KMagenta;
275 else if (colorStr == "silver")color = nsGraphics::KSilver;
276 else if (colorStr == "gray")color = nsGraphics::KGray;
277 else if (colorStr == "maroon")color = nsGraphics::KMaroon;
278 else if (colorStr == "olive")color = nsGraphics::KOlive;
279 else if (colorStr == "green")color = nsGraphics::KGreen;
280 else if (colorStr == "purple")color = nsGraphics::KPurple;
281 else if (colorStr == "teal")color = nsGraphics::KTeal;
282 else if (colorStr == "navy")color = nsGraphics::KNavy;
283 else throw config_error("Invalid color string : "+colorStr);
284}
285
286void ConfigBuilder::getList(const configKey& key, vector<string>& toPopulate) const {
287 unsigned i=0;
288 string fullKey = key+".0";
289 if(!internalValues.contains(fullKey))throw config_error("Non-existent list key requested : "+key);
290
291 do{
292 toPopulate.push_back(internalValues.at(fullKey));
293 ++i;
294 fullKey = key+"."+to_string(i);
295 }while(internalValues.contains(key+"."+to_string(i)));
296}
297
298
300 map<string, string> strValues;
301 ConfigBuilder builder;
302 bool parsed = false;
303 try{
304 builder.parseFile("config.yml");
305 parsed = true;
306 builder.readConfig();
307 }catch(config_error& e){
308 if(parsed)cerr << "An error occured while reading the configuration :" << endl;
309 else cerr << "An error occured while parsing the configuration :" << endl;
310 cerr << e.what() << endl;
311 if(parsed){
312 cerr << "Parsed keys :" << endl;
313 builder.dumpInternalValues();
314 }
315 cerr << "(The old configuration was kept in memory)" << endl;
316 return false;
317 }
318 confData = move(builder.collectedData);
319 return true;
320}
void parseFile(const string &fname)
ConfigData collectedData
void dumpInternalValues() const
bool reloadConfig()
reload the configuration file for a new game
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
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
player key 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
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:19
#define DEBUG_MSG(X)
Definition: utils.h:34