nsnake
Classic snake game for the terminal
Globals.cpp
1 #include <Config/Globals.hpp>
2 #include <Engine/EngineGlobals.hpp>
3 #include <Engine/Helpers/INI.hpp>
4 #include <Engine/Helpers/File.hpp>
5 #include <Engine/Helpers/String.hpp>
6 #include <Engine/InputManager.hpp>
7 #include <Entities/BoardParser.hpp>
8 #include <Entities/ScoreFile.hpp>
9 
10 #include <ncurses.h>
11 #include <iostream>
12 #include <fstream>
13 
14 // VERSION is formatted like "0.0.1" - i'm skipping the dots
15 char Globals::version[3] = { VERSION[0],
16  VERSION[2],
17  VERSION[4] };
18 
19 // __ ___ _ ____ _ __
20 // / /` / / \ | |\ | | |_ | | / /`_
21 // \_\_, \_\_/ |_| \| |_| |_| \_\_/
22 
23 // real initialization at init()
24 std::string Globals::Config::directory = "";
25 std::string Globals::Config::file = "";
26 std::string Globals::Config::scoresFile = "";
27 
28 unsigned int Globals::Game::starting_speed = 1;
29 int Globals::Game::fruits_at_once = 1;
30 bool Globals::Game::random_walls = false;
31 bool Globals::Game::teleport = false;
32 std::string Globals::Game::current_level = "";
33 
34 ColorPair Globals::Theme::player_head;
35 ColorPair Globals::Theme::player_head_dead;
36 ColorPair Globals::Theme::player_body;
37 ColorPair Globals::Theme::fruit;
38 
39 Globals::Game::BoardSize Globals::Game::board_size = LARGE;
40 
41 Globals::Game::BoardSize Globals::Game::intToBoardSize(int val)
42 {
43  if (val == 0)
44  return Globals::Game::SMALL;
45 
46  if (val == 1)
47  return Globals::Game::MEDIUM;
48 
49  return Globals::Game::LARGE;
50 }
51 int Globals::Game::boardSizeToInt(Globals::Game::BoardSize size)
52 {
53  if (size == Globals::Game::SMALL)
54  return 0;
55 
56  if (size == Globals::Game::MEDIUM)
57  return 1;
58 
59  return 2;
60 }
61 
62 int Globals::Game::board_scroll_delay = 1000;
63 
64 bool Globals::Game::board_scroll_up = false;
65 bool Globals::Game::board_scroll_down = false;
66 bool Globals::Game::board_scroll_left = false;
67 bool Globals::Game::board_scroll_right = false;
68 
69 bool Globals::Error::has_config_file = true;
70 bool Globals::Error::has_score_file = true;
71 bool Globals::Error::old_version_score_file = false;
72 bool Globals::Error::strange_score_file = false;
73 
74 // _ _ _ _____
75 // | | | |\ | | | | |
76 // |_| |_| \| |_| |_|
77 
79 {
80  // Making sure default config directory exists
81  // By default it's `~/.local/share/nsnake/`
82 
83  Globals::Config::directory = (Utils::File::getHome() +
84  ".local/share/" +
85  PACKAGE + "/");
86 
87  if (Utils::String::front(Globals::Config::directory) != '/')
88  {
89  // We couldn't get user's home directory,
90  // so let's fallback to `/tmp/.local/share...`
91  Globals::Config::directory = ("/tmp/" +
92  Globals::Config::directory);
93  }
94 
95  Globals::Config::file = (Globals::Config::directory +
96  "settings.ini");
97 
98  Globals::Config::scoresFile = (Globals::Config::directory +
99  "arcade.nsnakescores");
100 
101  if (! Utils::File::isDirectory(Globals::Config::directory))
102  Utils::File::mkdir_p(Globals::Config::directory);
103 
104  if (! Utils::File::isDirectory(Globals::Config::directory))
105  {
106  // We REALLY can't access the disk by any means.
107  // Let's throw everything away and give up.
108  Globals::Config::directory = "/dev/";
109  Globals::Config::file = "/dev/null";
110  return;
111  }
112 
113  // Default Input configurationa
114  InputManager::bind("left", KEY_LEFT);
115  InputManager::bind("right", KEY_RIGHT);
116  InputManager::bind("up", KEY_UP);
117  InputManager::bind("down", KEY_DOWN);
118  InputManager::bind("pause", 'p');
119  InputManager::bind("help", 'h');
120  InputManager::bind("quit", 'q');
121 
122 
123  // Aww yeah, rev up dem colors
124  Globals::Theme::player_head = Colors::pair("green", "default", true);
125  Globals::Theme::player_head_dead = Colors::pair("red", "default", true);
126  Globals::Theme::player_body = Colors::pair("green", "default", true);
127 
128  Globals::Theme::fruit = Colors::pair("red", "default", true);
129 
134  BoardParser::directory = Globals::Config::directory + "levels/";
136 
138  if (! Utils::File::isDirectory(BoardParser::directory))
139  Utils::File::mkdir_p(BoardParser::directory);
140 }
142 {
143  // Now, back on track
144  if (! Utils::File::exists(Globals::Config::file))
145  return;
146 
147  INI::Parser* ini = NULL;
148 
149  try {
150  ini = new INI::Parser(Globals::Config::file);
151  }
152  catch(std::runtime_error& e)
153  {
154  // File doesn't exist (or we couldn't access it)
155  // Either way, ignore it silently
156  SAFE_DELETE(ini);
157  return;
158  }
159 
160  // Will be used on this macro below
161  std::string buffer = "";
162 
163 // Small macro to avoid unnecessary typing.
164 //
165 // To get something from the ini file we send the
166 // text (to identify some value) and the default
167 // value in case it doesn't exist.
168 //
169 // For the last one I send the variable itself,
170 // so we fallback to the default values.
171 #define INI_GET(var, out, in) \
172  { \
173  buffer = (* ini)(out)[in]; \
174  if (! buffer.empty()) \
175  { \
176  Utils::String::convert(buffer, var); \
177  } \
178  }
179 
180  INI_GET(EngineGlobals::Screen::center_horizontally, "screen", "center_horizontal");
181  INI_GET(EngineGlobals::Screen::center_vertically, "screen", "center_vertical");
182 
183  INI_GET(EngineGlobals::Screen::show_borders, "screen", "borders");
184  INI_GET(EngineGlobals::Screen::fancy_borders, "screen", "fancy_borders");
185  INI_GET(EngineGlobals::Screen::outer_border, "screen", "outer_border");
186 
187  INI_GET(Globals::Game::random_walls, "game", "random_walls");
188  INI_GET(Globals::Game::fruits_at_once, "game", "fruits_at_once");
189  INI_GET(Globals::Game::teleport, "game", "teleport");
190  INI_GET(Globals::Game::board_scroll_delay, "game", "board_scroll_delay");
191 
192  INI_GET(Globals::Game::board_scroll_up, "game", "board_scroll_up");
193  INI_GET(Globals::Game::board_scroll_down, "game", "board_scroll_down");
194  INI_GET(Globals::Game::board_scroll_left, "game", "board_scroll_left");
195  INI_GET(Globals::Game::board_scroll_right, "game", "board_scroll_right");
196 
197  // unsigned ints are the exception - their overloading
198  // is ambiguous... I should consider removing them altogether
199  buffer = (* ini)("game")["starting_speed"];
200  if (! buffer.empty())
201  {
202  int starting_speed = Globals::Game::starting_speed;
203  Utils::String::convert(buffer, starting_speed);
204  Globals::Game::starting_speed = starting_speed;
205  }
206 
207  // Special Cases
208 
209  // Getting input keys
210  std::string tmp;
211 
212  INI_GET(tmp, "input", "left");
213  InputManager::bind("left", InputManager::stringToKey(tmp));
214 
215  INI_GET(tmp, "input", "right");
216  InputManager::bind("right", InputManager::stringToKey(tmp));
217 
218  INI_GET(tmp, "input", "up");
219  InputManager::bind("up", InputManager::stringToKey(tmp));
220 
221  INI_GET(tmp, "input", "down");
222  InputManager::bind("down", InputManager::stringToKey(tmp));
223 
224  INI_GET(tmp, "input", "pause");
225  InputManager::bind("pause", InputManager::stringToKey(tmp));
226 
227  INI_GET(tmp, "input", "help");
228  InputManager::bind("help", InputManager::stringToKey(tmp));
229 
230  INI_GET(tmp, "input", "quit");
231  InputManager::bind("quit", InputManager::stringToKey(tmp));
232 
233  // Board Size
234  int board_size = 2;
235  INI_GET(board_size, "game", "board_size");
236  Globals::Game::board_size = Globals::Game::intToBoardSize(board_size);
237 
238  // Getting the colors from their strings
239  INI_GET(tmp, "gui_colors", "text");
240  EngineGlobals::Theme::text = ColorPair::fromString(tmp);
241 
242  INI_GET(tmp, "gui_colors", "hilite_text");
243  EngineGlobals::Theme::hilite_text = ColorPair::fromString(tmp);
244 
245  INI_GET(tmp, "gui_colors", "textbox");
246  EngineGlobals::Theme::textbox = ColorPair::fromString(tmp);
247 
248  INI_GET(tmp, "game_colors", "player_head");
249  Globals::Theme::player_head = ColorPair::fromString(tmp);
250 
251  INI_GET(tmp, "game_colors", "player_head_dead");
252  Globals::Theme::player_head_dead = ColorPair::fromString(tmp);
253 
254  INI_GET(tmp, "game_colors", "player_body");
255  Globals::Theme::player_body = ColorPair::fromString(tmp);
256 
257  INI_GET(tmp, "game_colors", "fruit");
258  Globals::Theme::fruit = ColorPair::fromString(tmp);
259 
260  SAFE_DELETE(ini);
261 }
263 {
264  // Even if the file doesn't exist, we'll create it.
265  INI::Parser* ini;
266 
267  try
268  {
269  ini = new INI::Parser(Globals::Config::file);
270  }
271  catch(std::runtime_error& e)
272  {
273  ini = new INI::Parser();
274  ini->create();
275  }
276 
277  // Will be used on this macro below
278  std::string buffer;
279 
280 // Other macro to avoid typing, similar to the one
281 // at loadFile()
282 #define INI_SET(out, in, var) \
283  { \
284  buffer = Utils::String::toString(var); \
285  ini->top().addGroup(out); \
286  (* ini)(out).addKey(in, buffer); \
287  }
288 
289  INI_SET("screen", "center_horizontal", EngineGlobals::Screen::center_horizontally);
290  INI_SET("screen", "center_vertical", EngineGlobals::Screen::center_vertically);
291 
292  INI_SET("screen", "borders", EngineGlobals::Screen::show_borders);
293  INI_SET("screen", "fancy_borders", EngineGlobals::Screen::fancy_borders);
294  INI_SET("screen", "outer_border", EngineGlobals::Screen::outer_border);
295 
296  INI_SET("game", "random_walls", Globals::Game::random_walls);
297  INI_SET("game", "fruits_at_once", Globals::Game::fruits_at_once);
298  INI_SET("game", "teleport", Globals::Game::teleport);
299 
300  INI_SET("game", "board_scroll_delay", Globals::Game::board_scroll_delay);
301 
302  INI_SET("game", "board_scroll_up", Globals::Game::board_scroll_up);
303  INI_SET("game", "board_scroll_down", Globals::Game::board_scroll_down);
304  INI_SET("game", "board_scroll_left", Globals::Game::board_scroll_left);
305  INI_SET("game", "board_scroll_right", Globals::Game::board_scroll_right);
306 
307  // unsigned ints are the exception - their overloading
308  // is ambiguous... I should consider removing them altogether
309  int starting_speed = Globals::Game::starting_speed;
310  buffer = Utils::String::toString(starting_speed);
311  ini->top().addGroup("game");
312  (* ini)("game").addKey("starting_speed", buffer);
313 
314  // Special Cases
315 
316  // Input Keys
317  std::string key;
318 
319  key = InputManager::keyToString(InputManager::getBind("left"));
320  INI_SET("input", "left", key);
321 
322  key = InputManager::keyToString(InputManager::getBind("right"));
323  INI_SET("input", "right", key);
324 
325  key = InputManager::keyToString(InputManager::getBind("up"));
326  INI_SET("input", "up", key);
327 
328  key = InputManager::keyToString(InputManager::getBind("down"));
329  INI_SET("input", "down", key);
330 
331  key = InputManager::keyToString(InputManager::getBind("pause"));
332  INI_SET("input", "pause", key);
333 
334  key = InputManager::keyToString(InputManager::getBind("help"));
335  INI_SET("input", "help", key);
336 
337  key = InputManager::keyToString(InputManager::getBind("quit"));
338  INI_SET("input", "quit", key);
339 
340  // Board size
341  int board_size = Globals::Game::boardSizeToInt(Globals::Game::board_size);
342  INI_SET("game", "board_size", board_size);
343 
344  // Saving the colors from their strings
345  INI_SET("gui_colors", "text", EngineGlobals::Theme::text.toString());
346 
347  INI_SET("gui_colors", "hilite_text", EngineGlobals::Theme::hilite_text.toString());
348 
349  INI_SET("gui_colors", "textbox", EngineGlobals::Theme::textbox.toString());
350 
351 
352  INI_SET("game_colors", "player_head", Globals::Theme::player_head.toString());
353 
354  INI_SET("game_colors", "player_head_dead", Globals::Theme::player_head_dead.toString());
355 
356  INI_SET("game_colors", "player_body", Globals::Theme::player_body.toString());
357 
358  INI_SET("game_colors", "fruit", Globals::Theme::fruit.toString());
359 
360  try
361  {
362  ini->saveAs(Globals::Config::file);
363  }
364  catch(std::runtime_error& e)
365  {
366  // Couldn't save the file...
367  // ... do nothing
368  }
369  SAFE_DELETE(ini);
370 }
372 {
373  if (! Globals::Error::has_config_file)
374  {
375  std::cout << "Warning: We could not create the configuration file.\n"
376  << " Please check permissions to the path:\n"
377  << " " + Globals::Config::file
378  << std::endl;
379  }
380  if (! Globals::Error::has_score_file)
381  {
382  std::cout << "Warning: We could not create the score file.\n"
383  << " Please check permissions to the path:\n"
384  << " " + Globals::Config::scoresFile
385  << std::endl;
386  }
387  if (Globals::Error::old_version_score_file)
388  {
389  std::cout << "Warning: Your high score file is from an old nsnake version."
390  << std::endl;
391  }
392  if (Globals::Error::strange_score_file)
393  {
394  // Erasing high scores...
395  Utils::File::create(Globals::Config::scoresFile);
396 
397  std::cout << "Error: Corrupted high score file!\n"
398  << " We're sorry, but we had to erase it"
399  << std::endl;
400  }
401 }
402 
void saveFile()
Loads global variables to the default file.
Definition: Globals.cpp:262
void loadFile()
Loads global variables from the default file.
Definition: Globals.cpp:141
char version[3]
Game version (format MMP - Major Minor Patch).
Definition: Globals.cpp:15
static std::string directory
Default directory where the level files are.
Definition: BoardParser.hpp:38
void init()
Initializes global variables with default values.
Definition: Globals.cpp:78
void warnErrors()
Warns the user about any errors and warnings found during the program's execution.
Definition: Globals.cpp:371
static std::string directory
Default directory where we store the game score files.
Definition: ScoreFile.hpp:97