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 
static std::string directory
Default directory where the level files are.
Definition: BoardParser.hpp:38
static std::vector< std::string > listLevels()
Lists all levels found by the game.
GameState::StateCode update()
Called every frame, where states calculate everything that can change.
void load(int stack=0)
Where every state initializes it's resources.
void draw()
Called every frame, where states draw stuff on screen.
int unload()
Gets called when we're leaving this menu.
StateCode
All possible transitions between states.
Definition: GameState.hpp:40
How we show the screen at GameStateMainMenu.
void draw(Menu *menu)
Shows the Main Menu screen, along with drawing #menu.
Menu on which its items are always sorted alphabetically.
void goRandom()
Selects a random item right AFTER the first blank one.
A list of selectable text.
List of selectable items.
Definition: Menu.hpp:29
bool getBool(int id)
Returns the bool internal value of item that has #id.
Definition: Menu.cpp:350
bool willQuit()
Tells if the user selected an item that quits the menu.
Definition: Menu.cpp:330
void handleInput()
Makes the menu react to input, as seen on the global InputManager.
Definition: Menu.cpp:183
int currentID()
Returns the user-specified id of the selected item.
Definition: Menu.cpp:343
int getInt(int id)
Returns the integer value of the item that has #id.
Definition: Menu.cpp:372
std::string getString(int id)
Returns the string value of the item that has #id.
Definition: Menu.cpp:394
MenuItem * current
Current item selected.
Definition: Menu.hpp:115
void reset()
Makes the menu able to be selected again.
Definition: Menu.cpp:424
static void eraseAll()
Erases all high score files.
Definition: ScoreFile.cpp:71
Specific Window that shows Help and other info during Game.
void run()
Updates and draws all tabs.
void setTitle(std::string title, WindowTitlePosition position=Window::TOP_LEFT)
Sets a text that will appear at the top of the Window.
Definition: Window.cpp:214
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
void show(std::string message, bool pressAnyKey=false)
Shows a message on the screen.
Definition: Dialog.cpp:12
int getInput(int delay_ms=-1)
Returns a pressed character within a timespan of delay_ms (milliseconds).
Definition: Ncurses.cpp:37
A little box that can be checked or not.
Shows a Menu Item with left and right labels.
Allows to select a number, kinda like a slider.
Simplest type of item possible, with a label and user-defined id.
Definition: MenuItem.hpp:12
std::string label
Text that will be shown on the screen.
Definition: MenuItem.hpp:54