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