nsnake
Classic snake game for the terminal
GameStateMainMenu.cpp
1 #include <States/GameStateMainMenu.hpp>
2 #include <Engine/InputManager.hpp>
3 #include <Engine/Graphics/Ncurses.hpp>
4 #include <Engine/Graphics/Widgets/Dialog.hpp>
5 #include <Engine/Helpers/Utils.hpp>
6 #include <Engine/EngineGlobals.hpp>
7 #include <Config/Globals.hpp>
8 #include <Entities/BoardParser.hpp>
9 #include <Entities/ScoreFile.hpp>
10 
11 #include <Engine/Flow/StateManager.hpp>
12 #include <States/GameStateGame.hpp>
13 
14 enum NamesToEasilyIdentifyTheMenuItemsInsteadOfRawNumbers
15 {
16  // Main Menu
17  ARCADE=1337,
18  LEVELS,
19  GAME_SETTINGS,
20  HELP,
21  GUI_OPTIONS,
22  CONTROLS,
23  QUIT_GAME,
24 
25  // Level select Submenu
26  RANDOM,
27 
28  // Game Settings Submenu
29  GO_BACK,
30  STARTING_SPEED,
31  TELEPORT,
32  FRUITS,
33  RANDOM_WALLS,
34  BOARD_SIZE,
35  SCROLL_DELAY,
36  SCROLL_RIGHT,
37  SCROLL_LEFT,
38  SCROLL_UP,
39  SCROLL_DOWN,
40  ERASE_HIGH_SCORES,
41 
42  // GUI Submenu
43  SHOW_BORDERS,
44  FANCY_BORDERS,
45  OUTER_BORDER,
46  USE_COLORS,
47  CENTER_HORIZONTAL,
48  CENTER_VERTICAL,
49 
50  // Controls Submenu
51  CONTROLS_KEY_LEFT,
52  CONTROLS_KEY_RIGHT,
53  CONTROLS_KEY_UP,
54  CONTROLS_KEY_DOWN,
55  CONTROLS_KEY_PAUSE,
56  CONTROLS_KEY_HELP,
57  CONTROLS_KEY_QUIT,
58  CONTROLS_DEFAULT
59 };
60 
61 GameStateMainMenu::GameStateMainMenu():
62  layout(NULL),
63  menu(NULL),
64  menuLevels(NULL),
65  menuLevelsActivated(false),
66  menuGameSettings(NULL),
67  menuGameSettingsActivated(false),
68  menuGUIOptions(NULL),
69  menuGUIOptionsActivated(false),
70  menuControls(NULL),
71  menuControlsActivated(false),
72  helpWindows(NULL)
73 { }
74 
75 void GameStateMainMenu::load()
76 {
77  this->layout = new LayoutMainMenu(80, 24, this);
78 
79  createMainMenu();
80  createLevelsMenu();
81  createGameSettingsMenu();
82  createGUIOptionsMenu();
83  createControlsMenu();
84 
85  this->helpWindows = new WindowGameHelp();
86 }
87 
88 void GameStateMainMenu::unload()
89 {
90  saveSettingsMenuGameSettings();
91  saveSettingsMenuGUIOptions();
92 
93  SAFE_DELETE(this->layout);
94  SAFE_DELETE(this->menuLevels);
95  SAFE_DELETE(this->menuGameSettings);
96  SAFE_DELETE(this->menuGUIOptions);
97  SAFE_DELETE(this->menuControls);
98  SAFE_DELETE(this->menu);
99 }
100 
101 void GameStateMainMenu::update()
102 {
103  if (InputManager::isPressed("quit"))
104  StateManager::quit();
105 
106  if (this->menuLevelsActivated)
107  {
108  this->menuLevels->handleInput();
109 
110  if (this->menuLevels->willQuit())
111  {
112  switch (this->menuLevels->currentID())
113  {
114  case GO_BACK:
115  this->layout->menu->setTitle("Main Menu");
116  this->menuLevelsActivated = false;
117  break;
118 
119  case RANDOM:
120  {
121  this->menuLevels->goRandom();
122 
123  Globals::Game::current_level = this->menuLevels->current->label;
124  StateManager::change(new GameStateGame());
125  break;
126  }
127 
128  default:
129  // Selected a level name!
130  Globals::Game::current_level = this->menuLevels->current->label;
131  StateManager::change(new GameStateGame());
132  break;
133  }
134  this->menuLevels->reset();
135  }
136  }
137  else if (this->menuGameSettingsActivated)
138  {
139  this->menuGameSettings->handleInput();
140 
141  if (this->menuGameSettings->willQuit())
142  {
143  saveSettingsMenuGameSettings();
144 
145  // And then exit based on the selected option.
146  switch (this->menuGameSettings->currentID())
147  {
148  case ERASE_HIGH_SCORES:
149  {
150  bool answer = Dialog::askBool("Are you sure?");
151 
152  if (answer)
153  {
155  Dialog::show("All high scores erased!", true);
156  }
157  }
158  break;
159 
160  case GO_BACK:
161  this->layout->menu->setTitle("Main Menu");
162  this->menuGameSettingsActivated = false;
163  break;
164  }
165  this->menuGameSettings->reset();
166  }
167  }
168  else if (this->menuGUIOptionsActivated)
169  {
170  this->menuGUIOptions->handleInput();
171 
172  if (this->menuGUIOptions->willQuit())
173  {
174  switch(this->menuGUIOptions->currentID())
175  {
176  case GO_BACK:
177  this->layout->menu->setTitle("Main Menu");
178  this->menuGUIOptionsActivated = false;
179 
180  // Redrawing the screen to refresh settings
181  saveSettingsMenuGUIOptions();
182  this->layout->windowsExit();
183  this->layout->windowsInit();
184  break;
185  }
186  this->menuGUIOptions->reset();
187  }
188  }
189  else if (this->menuControlsActivated)
190  {
191  this->menuControls->handleInput();
192 
193  if (this->menuControls->willQuit())
194  {
195  std::string key(""); // for key binding
196 
197  switch(this->menuControls->currentID())
198  {
199  case GO_BACK:
200  this->layout->menu->setTitle("Main Menu");
201  this->menuControlsActivated = false;
202  break;
203 
204  case CONTROLS_KEY_LEFT: key = "left"; break;
205  case CONTROLS_KEY_RIGHT: key = "right"; break;
206  case CONTROLS_KEY_UP: key = "up"; break;
207  case CONTROLS_KEY_DOWN: key = "down"; break;
208  case CONTROLS_KEY_PAUSE: key = "pause"; break;
209  case CONTROLS_KEY_HELP: key = "help"; break;
210  case CONTROLS_KEY_QUIT: key = "quit"; break;
211 
212  case CONTROLS_DEFAULT:
213  {
214  // Reset all keybindings to default
215  InputManager::bind("left", KEY_LEFT);
216  InputManager::bind("right", KEY_RIGHT);
217  InputManager::bind("up", KEY_UP);
218  InputManager::bind("down", KEY_DOWN);
219  InputManager::bind("pause", 'p');
220  InputManager::bind("help", 'h');
221  InputManager::bind("quit", 'q');
222 
223  // Resetting the menu to show the new labels
224  createControlsMenu();
225  menuControls->goLast();
226  break;
227  }
228  }
229 
230  // If we'll change a key binding
231  if (! key.empty())
232  {
233  Dialog::show("Press any key, Enter to Cancel");
234  int tmp = Ncurses::getInput(-1);
235 
236  if ((tmp != KEY_ENTER) &&
237  (tmp != '\n') &&
238  (tmp != ERR))
239  {
240  InputManager::bind(key, tmp);
241 
242  MenuItemLabel* label;
243  label = (MenuItemLabel*)menuControls->current;
244 
245  label->set(InputManager::keyToString(tmp));
246  }
247  }
248  this->menuControls->reset();
249  }
250  }
251  else
252  {
253  // We're still at the Main Menu
254  this->menu->handleInput();
255 
256  if (this->menu->willQuit())
257  {
258  switch(this->menu->currentID())
259  {
260  case ARCADE:
261  // Starting game on the default level
262  Globals::Game::current_level = "";
263  StateManager::change(new GameStateGame());
264  break;
265 
266  case LEVELS:
267  // Before going to the Levels menu, we must check if
268  // the user has any levels on the level directory.
269  // If not, we should stay at the main menu.
270  if (BoardParser::listLevels().size() == 0)
271  {
272  Dialog::show("Sorry, it seems you have no levels.\n"
273  "\n"
274  "Please copy the default level files from\n"
275  "`" SYSTEM_LEVEL_DIR "/`\n"
276  "to\n"
277  "`" + BoardParser::directory + "`\n" +
278  "\n"
279  "You can also download more levels from the website:\n"
280  "http://nsnake.alexdantas.net/", true);
281  }
282  else
283  {
284  this->layout->menu->setTitle("Level Select");
285  this->menuLevelsActivated = true;
286  }
287  break;
288 
289  case GAME_SETTINGS:
290  this->layout->menu->setTitle("Game Settings");
291  this->menuGameSettingsActivated = true;
292  break;
293 
294  case GUI_OPTIONS:
295  this->layout->menu->setTitle("GUI Options");
296  this->menuGUIOptionsActivated = true;
297  break;
298 
299  case CONTROLS:
300  this->layout->menu->setTitle("Controls");
301  this->menuControlsActivated = true;
302  break;
303 
304  case HELP:
305  this->helpWindows->run();
306  break;
307 
308  case QUIT_GAME:
309  StateManager::quit();
310  break;
311  }
312  this->menu->reset();
313  }
314  }
315 }
316 
317 void GameStateMainMenu::draw()
318 {
319  if (this->menuLevelsActivated)
320  this->layout->draw(this->menuLevels);
321 
322  else if (this->menuGameSettingsActivated)
323  this->layout->draw(this->menuGameSettings);
324 
325  else if (this->menuGUIOptionsActivated)
326  this->layout->draw(this->menuGUIOptions);
327 
328  else if (this->menuControlsActivated)
329  this->layout->draw(this->menuControls);
330 
331  else
332  this->layout->draw(this->menu);
333 }
334 
335 void GameStateMainMenu::createMainMenu()
336 {
337  SAFE_DELETE(this->menu);
338 
339  // Creating the Menu and Items.
340  // Their default ids will be based on current's
341  // settings.
342  this->menu = new Menu(1,
343  1,
344  this->layout->menu->getW() - 2,
345  this->layout->menu->getH() - 2);
346 
347  MenuItem* item;
348 
349  item = new MenuItem("Arcade Mode", ARCADE);
350  menu->add(item);
351 
352  item = new MenuItem("Level Select", LEVELS);
353  menu->add(item);
354 
355  item = new MenuItem("Game Settings", GAME_SETTINGS);
356  menu->add(item);
357 
358  item = new MenuItem("GUI Options", GUI_OPTIONS);
359  menu->add(item);
360 
361  item = new MenuItem("Controls", CONTROLS);
362  menu->add(item);
363 
364  item = new MenuItem("Help", HELP);
365  menu->add(item);
366 
367  item = new MenuItem("Quit", QUIT_GAME);
368  menu->add(item);
369 }
370 void GameStateMainMenu::createLevelsMenu()
371 {
372  SAFE_DELETE(this->menuLevels);
373 
374  this->menuLevels = new MenuAlphabetic(1,
375  1,
376  this->layout->menu->getW() - 2,
377  this->layout->menu->getH() - 2);
378 
379  MenuItem* item;
380 
381  std::vector<std::string> levels = BoardParser::listLevels();
382 
383  item = new MenuItem("Back", GO_BACK);
384  menuLevels->add(item);
385 
386  item = new MenuItem("Random", RANDOM);
387  menuLevels->add(item);
388 
389  menuLevels->addBlank();
390 
391  for (size_t i = 0; i < levels.size(); i++)
392  {
393  item = new MenuItem(levels[i], i);
394  menuLevels->add(item);
395  }
396 }
397 void GameStateMainMenu::createGameSettingsMenu()
398 {
399  SAFE_DELETE(this->menuGameSettings);
400 
401  this->menuGameSettings = new Menu(1,
402  1,
403  this->layout->menu->getW() - 2,
404  this->layout->menu->getH() - 2);
405 
406  MenuItem* item;
407 
408  item = new MenuItem("Back", GO_BACK);
409  menuGameSettings->add(item);
410 
411  menuGameSettings->addBlank();
412 
413  MenuItemNumberbox* number;
414 
415  number = new MenuItemNumberbox("Starting Speed", STARTING_SPEED, 1, 10, Globals::Game::starting_speed);
416  menuGameSettings->add(number);
417 
418  number = new MenuItemNumberbox("Fruits", FRUITS, 1, 99, Globals::Game::fruits_at_once);
419  menuGameSettings->add(number);
420 
421  MenuItemCheckbox* check;
422 
423  check = new MenuItemCheckbox("Teleport", TELEPORT, Globals::Game::teleport);
424  menuGameSettings->add(check);
425 
426  check = new MenuItemCheckbox("Random Walls", RANDOM_WALLS, Globals::Game::random_walls);
427  menuGameSettings->add(check);
428 
429  // The board size
430  std::vector<std::string> options;
431  options.push_back("Small");
432  options.push_back("Medium");
433  options.push_back("Large");
434 
435  MenuItemTextlist* list;
436 
437  // the default board size
438  std::string defaullt;
439 
440  switch (Globals::Game::board_size)
441  {
442  case Globals::Game::SMALL: defaullt = "Small"; break;
443  case Globals::Game::MEDIUM: defaullt = "Medium"; break;
444  default: defaullt = "Large"; break;
445  }
446 
447  list = new MenuItemTextlist("Maze size",
448  BOARD_SIZE,
449  options,
450  defaullt);
451 
452  menuGameSettings->add(list);
453 
454  menuGameSettings->addBlank();
455 
456  number = new MenuItemNumberbox("Scroll Delay(ms)", SCROLL_DELAY, 100, 5000, Globals::Game::board_scroll_delay, 100);
457  menuGameSettings->add(number);
458 
459  check = new MenuItemCheckbox("Scroll Up", SCROLL_UP, Globals::Game::board_scroll_up);
460  menuGameSettings->add(check);
461 
462  check = new MenuItemCheckbox("Scroll Down", SCROLL_DOWN, Globals::Game::board_scroll_down);
463  menuGameSettings->add(check);
464 
465  check = new MenuItemCheckbox("Scroll Left", SCROLL_LEFT, Globals::Game::board_scroll_left);
466  menuGameSettings->add(check);
467 
468  check = new MenuItemCheckbox("Scroll Right", SCROLL_RIGHT, Globals::Game::board_scroll_right);
469  menuGameSettings->add(check);
470 
471  menuGameSettings->addBlank();
472 
473  item = new MenuItem("Erase High Scores",
474  ERASE_HIGH_SCORES);
475  menuGameSettings->add(item);
476 }
477 void GameStateMainMenu::createGUIOptionsMenu()
478 {
479  SAFE_DELETE(this->menuGUIOptions);
480 
481  this->menuGUIOptions = new Menu(1,
482  1,
483  this->layout->menu->getW() - 2,
484  this->layout->menu->getH() - 2);
485 
486  MenuItem* item;
487 
488  item = new MenuItem("Back", GO_BACK);
489  menuGUIOptions->add(item);
490 
491  menuGUIOptions->addBlank();
492 
493  MenuItemCheckbox* check;
494 
495  check = new MenuItemCheckbox("Show Borders",
496  SHOW_BORDERS,
497  EngineGlobals::Screen::show_borders);
498  menuGUIOptions->add(check);
499 
500  check = new MenuItemCheckbox("Fancy Borders",
501  FANCY_BORDERS,
502  EngineGlobals::Screen::fancy_borders);
503  menuGUIOptions->add(check);
504 
505  check = new MenuItemCheckbox("Outer Border",
506  OUTER_BORDER,
507  EngineGlobals::Screen::outer_border);
508  menuGUIOptions->add(check);
509 
510  check = new MenuItemCheckbox("Center Horizontal",
511  CENTER_HORIZONTAL,
512  EngineGlobals::Screen::center_horizontally);
513  menuGUIOptions->add(check);
514 
515  check = new MenuItemCheckbox("Center Vertical",
516  CENTER_VERTICAL,
517  EngineGlobals::Screen::center_vertically);
518  menuGUIOptions->add(check);
519 }
520 void GameStateMainMenu::createControlsMenu()
521 {
522  SAFE_DELETE(this->menuControls);
523 
524  this->menuControls = new Menu(1,
525  1,
526  this->layout->menu->getW() - 2,
527  this->layout->menu->getH() - 2);
528 
529  MenuItem* item;
530 
531  item = new MenuItem("Back", GO_BACK);
532  menuControls->add(item);
533 
534  menuControls->addBlank();
535 
536  MenuItemLabel* label;
537  std::string str;
538 
539  str = InputManager::keyToString(InputManager::getBind("up"));
540  label = new MenuItemLabel("Key up", CONTROLS_KEY_UP, str);
541  menuControls->add(label);
542 
543  str = InputManager::keyToString(InputManager::getBind("down"));
544  label = new MenuItemLabel("Key down", CONTROLS_KEY_DOWN, str);
545  menuControls->add(label);
546 
547  str = InputManager::keyToString(InputManager::getBind("left"));
548  label = new MenuItemLabel("Key left", CONTROLS_KEY_LEFT, str);
549  menuControls->add(label);
550 
551  str = InputManager::keyToString(InputManager::getBind("right"));
552  label = new MenuItemLabel("Key right", CONTROLS_KEY_RIGHT, str);
553  menuControls->add(label);
554 
555  str = InputManager::keyToString(InputManager::getBind("pause"));
556  label = new MenuItemLabel("Key pause", CONTROLS_KEY_PAUSE, str);
557  menuControls->add(label);
558 
559  str = InputManager::keyToString(InputManager::getBind("help"));
560  label = new MenuItemLabel("Key help", CONTROLS_KEY_HELP, str);
561  menuControls->add(label);
562 
563  str = InputManager::keyToString(InputManager::getBind("quit"));
564  label = new MenuItemLabel("Key quit", CONTROLS_KEY_QUIT, str);
565  menuControls->add(label);
566 
567  menuControls->addBlank();
568 
569  item = new MenuItem("Reset to Defaults", CONTROLS_DEFAULT);
570  menuControls->add(item);
571 }
572 void GameStateMainMenu::saveSettingsMenuGUIOptions()
573 {
574  if (!this->menuGUIOptions)
575  return;
576 
577  // User selected an option
578  // Let's get ids from menu items
579  EngineGlobals::Screen::show_borders = this->menuGUIOptions->getBool(SHOW_BORDERS);
580  EngineGlobals::Screen::fancy_borders = this->menuGUIOptions->getBool(FANCY_BORDERS);
581  EngineGlobals::Screen::outer_border = this->menuGUIOptions->getBool(OUTER_BORDER);
582  EngineGlobals::Screen::center_horizontally = this->menuGUIOptions->getBool(CENTER_HORIZONTAL);
583  EngineGlobals::Screen::center_vertically = this->menuGUIOptions->getBool(CENTER_VERTICAL);
584 }
585 void GameStateMainMenu::saveSettingsMenuGameSettings()
586 {
587  if (!this->menuGameSettings)
588  return;
589 
590  // User selected an option
591  // Let's get ids from menu items
592  Globals::Game::starting_speed = (unsigned int)this->menuGameSettings->getInt(STARTING_SPEED);
593  Globals::Game::fruits_at_once = this->menuGameSettings->getInt(FRUITS);
594  Globals::Game::random_walls = this->menuGameSettings->getBool(RANDOM_WALLS);
595  Globals::Game::teleport = this->menuGameSettings->getBool(TELEPORT);
596 
597  std::string tmp = this->menuGameSettings->getString(BOARD_SIZE);
598  if (tmp == "Small")
599  Globals::Game::board_size = Globals::Game::SMALL;
600 
601  else if (tmp == "Medium")
602  Globals::Game::board_size = Globals::Game::MEDIUM;
603 
604  else
605  Globals::Game::board_size = Globals::Game::LARGE;
606 
607  Globals::Game::board_scroll_delay = this->menuGameSettings->getInt(SCROLL_DELAY);
608 
609  Globals::Game::board_scroll_left = this->menuGameSettings->getBool(SCROLL_LEFT);
610  Globals::Game::board_scroll_right = this->menuGameSettings->getBool(SCROLL_RIGHT);
611  Globals::Game::board_scroll_up = this->menuGameSettings->getBool(SCROLL_UP);
612  Globals::Game::board_scroll_down = this->menuGameSettings->getBool(SCROLL_DOWN);
613 }
This represents the actual game taking place.
Specific Window that shows Help and other info during Game.
static std::vector< std::string > listLevels()
Lists all levels found by the game.
static std::string directory
Default directory where the level files are.
Definition: BoardParser.hpp:38
static void eraseAll()
Erases all high score files.
Definition: ScoreFile.cpp:73
How we show the screen at GameStateMainMenu.
void draw(Menu *menu)
Shows the Main Menu screen, along with drawing #menu.
void run()
Updates and draws all tabs.