nsnake
Classic snake game for the terminal
GameStateMainMenu.cpp
1 #include <Flow/GameStateMainMenu.hpp>
2 #include <Flow/InputManager.hpp>
3 #include <Interface/Ncurses.hpp>
4 #include <Interface/Dialog.hpp>
5 #include <Misc/Utils.hpp>
6 #include <Config/Globals.hpp>
7 #include <Game/BoardParser.hpp>
8 #include <Game/ScoreFile.hpp>
9 
10 enum NamesToEasilyIdentifyTheMenuItemsInsteadOfRawNumbers
11 {
12  // Main Menu
13  ARCADE=1337,
14  LEVELS,
15  GAME_SETTINGS,
16  HELP,
17  GUI_OPTIONS,
18  CONTROLS,
19  QUIT_GAME,
20 
21  // Level select Submenu
22  RANDOM,
23 
24  // Game Settings Submenu
25  GO_BACK,
26  STARTING_SPEED,
27  TELEPORT,
28  FRUITS,
29  RANDOM_WALLS,
30  BOARD_SIZE,
31  SCROLL_DELAY,
32  SCROLL_RIGHT,
33  SCROLL_LEFT,
34  SCROLL_UP,
35  SCROLL_DOWN,
36  ERASE_HIGH_SCORES,
37 
38  // GUI Submenu
39  SHOW_BORDERS,
40  FANCY_BORDERS,
41  OUTER_BORDER,
42  USE_COLORS,
43  CENTER_HORIZONTAL,
44  CENTER_VERTICAL,
45 
46  // Controls Submenu
47  CONTROLS_KEY_LEFT,
48  CONTROLS_KEY_RIGHT,
49  CONTROLS_KEY_UP,
50  CONTROLS_KEY_DOWN,
51  CONTROLS_KEY_PAUSE,
52  CONTROLS_KEY_HELP,
53  CONTROLS_KEY_QUIT,
54  CONTROLS_DEFAULT
55 };
56 
57 GameStateMainMenu::GameStateMainMenu():
58  layout(NULL),
59  menu(NULL),
60  menuLevels(NULL),
61  menuLevelsActivated(false),
62  menuGameSettings(NULL),
63  menuGameSettingsActivated(false),
64  menuGUIOptions(NULL),
65  menuGUIOptionsActivated(false),
66  menuControls(NULL),
67  menuControlsActivated(false),
68  helpWindows(NULL)
69 { }
70 
71 void GameStateMainMenu::load(int stack)
72 {
73  UNUSED(stack);
74 
75  this->layout = new LayoutMainMenu(80, 24, this);
76 
77  createMainMenu();
78  createLevelsMenu();
79  createGameSettingsMenu();
80  createGUIOptionsMenu();
81  createControlsMenu();
82 
83  this->helpWindows = new WindowGameHelp();
84 }
85 
87 {
88  saveSettingsMenuGameSettings();
89  saveSettingsMenuGUIOptions();
90 
91  SAFE_DELETE(this->layout);
92  SAFE_DELETE(this->menuLevels);
93  SAFE_DELETE(this->menuGameSettings);
94  SAFE_DELETE(this->menuGUIOptions);
95  SAFE_DELETE(this->menuControls);
96  SAFE_DELETE(this->menu);
97 
98  return 0;
99 }
100 
102 {
103  if (InputManager::isPressed("quit"))
104  return GameState::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  return GameState::GAME_START;
125  break;
126  }
127 
128  default:
129  // Selected a level name!
130  Globals::Game::current_level = this->menuLevels->current->label;
131  return GameState::GAME_START;
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  return GameState::GAME_START;
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  return GameState::QUIT;
310  break;
311  }
312  this->menu->reset();
313  }
314  }
315 
316  // Otherwise, continuing things...
317  return GameState::CONTINUE;
318 }
319 
321 {
322  if (this->menuLevelsActivated)
323  this->layout->draw(this->menuLevels);
324 
325  else if (this->menuGameSettingsActivated)
326  this->layout->draw(this->menuGameSettings);
327 
328  else if (this->menuGUIOptionsActivated)
329  this->layout->draw(this->menuGUIOptions);
330 
331  else if (this->menuControlsActivated)
332  this->layout->draw(this->menuControls);
333 
334  else
335  this->layout->draw(this->menu);
336 }
337 
338 void GameStateMainMenu::createMainMenu()
339 {
340  SAFE_DELETE(this->menu);
341 
342  // Creating the Menu and Items.
343  // Their default ids will be based on current's
344  // settings.
345  this->menu = new Menu(1,
346  1,
347  this->layout->menu->getW() - 2,
348  this->layout->menu->getH() - 2);
349 
350  MenuItem* item;
351 
352  item = new MenuItem("Arcade Mode", ARCADE);
353  menu->add(item);
354 
355  item = new MenuItem("Level Select", LEVELS);
356  menu->add(item);
357 
358  item = new MenuItem("Game Settings", GAME_SETTINGS);
359  menu->add(item);
360 
361  item = new MenuItem("GUI Options", GUI_OPTIONS);
362  menu->add(item);
363 
364  item = new MenuItem("Controls", CONTROLS);
365  menu->add(item);
366 
367  item = new MenuItem("Help", HELP);
368  menu->add(item);
369 
370  item = new MenuItem("Quit", QUIT_GAME);
371  menu->add(item);
372 }
373 void GameStateMainMenu::createLevelsMenu()
374 {
375  SAFE_DELETE(this->menuLevels);
376 
377  this->menuLevels = new MenuAlphabetic(1,
378  1,
379  this->layout->menu->getW() - 2,
380  this->layout->menu->getH() - 2);
381 
382  MenuItem* item;
383 
384  std::vector<std::string> levels = BoardParser::listLevels();
385 
386  item = new MenuItem("Back", GO_BACK);
387  menuLevels->add(item);
388 
389  item = new MenuItem("Random", RANDOM);
390  menuLevels->add(item);
391 
392  menuLevels->addBlank();
393 
394  for (size_t i = 0; i < levels.size(); i++)
395  {
396  item = new MenuItem(levels[i], i);
397  menuLevels->add(item);
398  }
399 }
400 void GameStateMainMenu::createGameSettingsMenu()
401 {
402  SAFE_DELETE(this->menuGameSettings);
403 
404  this->menuGameSettings = new Menu(1,
405  1,
406  this->layout->menu->getW() - 2,
407  this->layout->menu->getH() - 2);
408 
409  MenuItem* item;
410 
411  item = new MenuItem("Back", GO_BACK);
412  menuGameSettings->add(item);
413 
414  menuGameSettings->addBlank();
415 
416  MenuItemNumberbox* number;
417 
418  number = new MenuItemNumberbox("Starting Speed", STARTING_SPEED, 1, 10, Globals::Game::starting_speed);
419  menuGameSettings->add(number);
420 
421  number = new MenuItemNumberbox("Fruits", FRUITS, 1, 99, Globals::Game::fruits_at_once);
422  menuGameSettings->add(number);
423 
424  MenuItemCheckbox* check;
425 
426  check = new MenuItemCheckbox("Teleport", TELEPORT, Globals::Game::teleport);
427  menuGameSettings->add(check);
428 
429  check = new MenuItemCheckbox("Random Walls", RANDOM_WALLS, Globals::Game::random_walls);
430  menuGameSettings->add(check);
431 
432  // The board size
433  std::vector<std::string> options;
434  options.push_back("Small");
435  options.push_back("Medium");
436  options.push_back("Large");
437 
438  MenuItemTextlist* list;
439 
440  // the default board size
441  std::string defaullt;
442 
443  switch (Globals::Game::board_size)
444  {
445  case Globals::Game::SMALL: defaullt = "Small"; break;
446  case Globals::Game::MEDIUM: defaullt = "Medium"; break;
447  default: defaullt = "Large"; break;
448  }
449 
450  list = new MenuItemTextlist("Maze size",
451  BOARD_SIZE,
452  options,
453  defaullt);
454 
455  menuGameSettings->add(list);
456 
457  menuGameSettings->addBlank();
458 
459  number = new MenuItemNumberbox("Scroll Delay(ms)", SCROLL_DELAY, 100, 5000, Globals::Game::board_scroll_delay, 100);
460  menuGameSettings->add(number);
461 
462  check = new MenuItemCheckbox("Scroll Up", SCROLL_UP, Globals::Game::board_scroll_up);
463  menuGameSettings->add(check);
464 
465  check = new MenuItemCheckbox("Scroll Down", SCROLL_DOWN, Globals::Game::board_scroll_down);
466  menuGameSettings->add(check);
467 
468  check = new MenuItemCheckbox("Scroll Left", SCROLL_LEFT, Globals::Game::board_scroll_left);
469  menuGameSettings->add(check);
470 
471  check = new MenuItemCheckbox("Scroll Right", SCROLL_RIGHT, Globals::Game::board_scroll_right);
472  menuGameSettings->add(check);
473 
474  menuGameSettings->addBlank();
475 
476  item = new MenuItem("Erase High Scores",
477  ERASE_HIGH_SCORES);
478  menuGameSettings->add(item);
479 }
480 void GameStateMainMenu::createGUIOptionsMenu()
481 {
482  SAFE_DELETE(this->menuGUIOptions);
483 
484  this->menuGUIOptions = new Menu(1,
485  1,
486  this->layout->menu->getW() - 2,
487  this->layout->menu->getH() - 2);
488 
489  MenuItem* item;
490 
491  item = new MenuItem("Back", GO_BACK);
492  menuGUIOptions->add(item);
493 
494  menuGUIOptions->addBlank();
495 
496  MenuItemCheckbox* check;
497 
498  check = new MenuItemCheckbox("Show Borders",
499  SHOW_BORDERS,
500  Globals::Screen::show_borders);
501  menuGUIOptions->add(check);
502 
503  check = new MenuItemCheckbox("Fancy Borders",
504  FANCY_BORDERS,
505  Globals::Screen::fancy_borders);
506  menuGUIOptions->add(check);
507 
508  check = new MenuItemCheckbox("Outer Border",
509  OUTER_BORDER,
510  Globals::Screen::outer_border);
511  menuGUIOptions->add(check);
512 
513  check = new MenuItemCheckbox("Center Horizontal",
514  CENTER_HORIZONTAL,
515  Globals::Screen::center_horizontally);
516  menuGUIOptions->add(check);
517 
518  check = new MenuItemCheckbox("Center Vertical",
519  CENTER_VERTICAL,
520  Globals::Screen::center_vertically);
521  menuGUIOptions->add(check);
522 }
523 void GameStateMainMenu::createControlsMenu()
524 {
525  SAFE_DELETE(this->menuControls);
526 
527  this->menuControls = new Menu(1,
528  1,
529  this->layout->menu->getW() - 2,
530  this->layout->menu->getH() - 2);
531 
532  MenuItem* item;
533 
534  item = new MenuItem("Back", GO_BACK);
535  menuControls->add(item);
536 
537  menuControls->addBlank();
538 
539  MenuItemLabel* label;
540  std::string str;
541 
542  str = InputManager::keyToString(InputManager::getBind("up"));
543  label = new MenuItemLabel("Key up", CONTROLS_KEY_UP, str);
544  menuControls->add(label);
545 
546  str = InputManager::keyToString(InputManager::getBind("down"));
547  label = new MenuItemLabel("Key down", CONTROLS_KEY_DOWN, str);
548  menuControls->add(label);
549 
550  str = InputManager::keyToString(InputManager::getBind("left"));
551  label = new MenuItemLabel("Key left", CONTROLS_KEY_LEFT, str);
552  menuControls->add(label);
553 
554  str = InputManager::keyToString(InputManager::getBind("right"));
555  label = new MenuItemLabel("Key right", CONTROLS_KEY_RIGHT, str);
556  menuControls->add(label);
557 
558  str = InputManager::keyToString(InputManager::getBind("pause"));
559  label = new MenuItemLabel("Key pause", CONTROLS_KEY_PAUSE, str);
560  menuControls->add(label);
561 
562  str = InputManager::keyToString(InputManager::getBind("help"));
563  label = new MenuItemLabel("Key help", CONTROLS_KEY_HELP, str);
564  menuControls->add(label);
565 
566  str = InputManager::keyToString(InputManager::getBind("quit"));
567  label = new MenuItemLabel("Key quit", CONTROLS_KEY_QUIT, str);
568  menuControls->add(label);
569 
570  menuControls->addBlank();
571 
572  item = new MenuItem("Reset to Defaults", CONTROLS_DEFAULT);
573  menuControls->add(item);
574 }
575 void GameStateMainMenu::saveSettingsMenuGUIOptions()
576 {
577  if (!this->menuGUIOptions)
578  return;
579 
580  // User selected an option
581  // Let's get ids from menu items
582  Globals::Screen::show_borders = this->menuGUIOptions->getBool(SHOW_BORDERS);
583  Globals::Screen::fancy_borders = this->menuGUIOptions->getBool(FANCY_BORDERS);
584  Globals::Screen::outer_border = this->menuGUIOptions->getBool(OUTER_BORDER);
585  Globals::Screen::center_horizontally = this->menuGUIOptions->getBool(CENTER_HORIZONTAL);
586  Globals::Screen::center_vertically = this->menuGUIOptions->getBool(CENTER_VERTICAL);
587 }
588 void GameStateMainMenu::saveSettingsMenuGameSettings()
589 {
590  if (!this->menuGameSettings)
591  return;
592 
593  // User selected an option
594  // Let's get ids from menu items
595  Globals::Game::starting_speed = (unsigned int)this->menuGameSettings->getInt(STARTING_SPEED);
596  Globals::Game::fruits_at_once = this->menuGameSettings->getInt(FRUITS);
597  Globals::Game::random_walls = this->menuGameSettings->getBool(RANDOM_WALLS);
598  Globals::Game::teleport = this->menuGameSettings->getBool(TELEPORT);
599 
600  std::string tmp = this->menuGameSettings->getString(BOARD_SIZE);
601  if (tmp == "Small")
602  Globals::Game::board_size = Globals::Game::SMALL;
603 
604  else if (tmp == "Medium")
605  Globals::Game::board_size = Globals::Game::MEDIUM;
606 
607  else
608  Globals::Game::board_size = Globals::Game::LARGE;
609 
610  Globals::Game::board_scroll_delay = this->menuGameSettings->getInt(SCROLL_DELAY);
611 
612  Globals::Game::board_scroll_left = this->menuGameSettings->getBool(SCROLL_LEFT);
613  Globals::Game::board_scroll_right = this->menuGameSettings->getBool(SCROLL_RIGHT);
614  Globals::Game::board_scroll_up = this->menuGameSettings->getBool(SCROLL_UP);
615  Globals::Game::board_scroll_down = this->menuGameSettings->getBool(SCROLL_DOWN);
616 }
617 
void load(int stack=0)
Where every state initializes it&#39;s resources.
Specific Window that shows Help and other info during Game.
static std::vector< std::string > listLevels()
Lists all levels found by the game.
int unload()
Gets called when we&#39;re leaving this menu.
static std::string directory
Default directory where the level files are.
Definition: BoardParser.hpp:38
GameState::StateCode update()
Called every frame, where states calculate everything that can change.
Shows a Menu Item with left and right labels.
int getInput(int delay_ms=-1)
Returns a pressed character within a timespan of delay_ms (milliseconds).
Definition: Ncurses.cpp:37
static void eraseAll()
Erases all high score files.
Definition: ScoreFile.cpp:71
void draw()
Called every frame, where states draw stuff on screen.
A list of selectable text.
Simplest type of item possible, with a label and user-defined id.
Definition: MenuItem.hpp:11
List of selectable items.
Definition: Menu.hpp:28
How we show the screen at GameStateMainMenu.
bool askBool(std::string question, std::string title="", bool default_value=false)
Spawns a Dialog box asking for a yes-or-no #question.
Definition: Dialog.cpp:58
Allows to select a number, kinda like a slider.
void show(std::string message, bool pressAnyKey=false)
Shows a message on the screen.
Definition: Dialog.cpp:12
StateCode
All possible transitions between states.
Definition: GameState.hpp:39
A little box that can be checked or not.
Menu on which its items are always sorted alphabetically.