MenuState.cxx 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. #include "MenuState.hxx"
  2. #include "PlayingState.hxx"
  3. #include "GameStateManager.hxx"
  4. #include "TranslationManager.hxx"
  5. #include "../SDLRenderer.hxx"
  6. #include <array>
  7. void MenuState::on_enter(GameStateManager& gsm)
  8. {
  9. active_button_ = 0;
  10. last_controller_direction_ = 0;
  11. auto const& tm = TranslationManager::instance();
  12. new_game_button_.set_title(tm.get_translation("New game"));
  13. continue_button_.set_title(tm.get_translation("Continue"));
  14. high_score_button_.set_title(tm.get_translation("High Scores"));
  15. credits_button_.set_title(tm.get_translation("Credits"));
  16. quit_button_.set_title(tm.get_translation("Quit"));
  17. game_.reset();
  18. if (auto const parent = gsm.parent(); parent!=nullptr) {
  19. continue_button_.set_visible(true);
  20. if (auto const game = dynamic_cast<PlayingState*>(parent); game!=nullptr) {
  21. game_ = game;
  22. active_button_ = 1;
  23. }
  24. }
  25. else {
  26. continue_button_.set_visible(false);
  27. }
  28. new_game_button_.set_on_click([&gsm] {
  29. if (gsm.parent()!=nullptr)
  30. gsm.pop_state();
  31. gsm.replace_state(GameStates::Game);
  32. });
  33. continue_button_.set_on_click([&gsm] {
  34. gsm.pop_state();
  35. });
  36. high_score_button_.set_on_click([&gsm] {
  37. gsm.push_state(GameStates::HighScores);
  38. });
  39. credits_button_.set_on_click([&gsm] {
  40. gsm.push_state(GameStates::Credits);
  41. });
  42. quit_button_.set_on_click([&gsm] {
  43. while (gsm.current()!=nullptr) {
  44. gsm.pop_state();
  45. }
  46. });
  47. SDL_ShowCursor(SDL_ENABLE);
  48. }
  49. void MenuState::on_event(GameStateManager& gsm, SDL_Event const& evt)
  50. {
  51. switch (evt.type) {
  52. default:
  53. break;
  54. case SDL_KEYUP:
  55. handle_key_up(gsm, evt.key.keysym.scancode);
  56. break;
  57. case SDL_KEYDOWN:
  58. handle_key_down(evt.key.keysym.scancode);
  59. break;
  60. case SDL_CONTROLLERBUTTONUP:
  61. handle_controller_button_up(gsm, evt.cbutton.button);
  62. break;
  63. case SDL_CONTROLLERBUTTONDOWN:
  64. handle_controller_button_down(evt.cbutton.button);
  65. break;
  66. case SDL_CONTROLLERAXISMOTION:
  67. handle_controller_axis_motion(evt.caxis.axis, evt.caxis.value);
  68. break;
  69. case SDL_MOUSEMOTION:
  70. handle_mouse_movement(evt.motion.x, evt.motion.y);
  71. break;
  72. }
  73. }
  74. void MenuState::update(GameStateManager& gsm, std::chrono::milliseconds const delta_time)
  75. {
  76. (void) delta_time;
  77. auto adjust_button_size = [this, idx = 0](Button& btn) mutable {
  78. if (idx++==active_button_) {
  79. btn.resize(BUTTON_WIDTH, BUTTON_HEIGHT);
  80. }
  81. else {
  82. btn.resize(BUTTON_WIDTH-ACTIVE_SIZE_DIFF, BUTTON_HEIGHT-ACTIVE_SIZE_DIFF);
  83. }
  84. };
  85. for (auto const btn: {&new_game_button_, &continue_button_, &high_score_button_, &credits_button_, &quit_button_}) {
  86. adjust_button_size(*btn);
  87. btn->update();
  88. }
  89. }
  90. void MenuState::render(SDLRenderer& renderer)
  91. {
  92. int screen_w, screen_h;
  93. SDL_GetRendererOutputSize(renderer, &screen_w, &screen_h);
  94. int const button_count = continue_button_.is_visible() ? 5 : 4;
  95. int const x = (screen_w-BUTTON_WIDTH)/2;
  96. int y = (screen_h-(button_count*BUTTON_HEIGHT+(button_count-1)*20))/2;
  97. auto const adjust_button_position = [](Button& btn, int x, int y) {
  98. auto const box = btn.get_bounding_box();
  99. auto const dw = BUTTON_WIDTH-box.w;
  100. auto const dh = BUTTON_HEIGHT-box.h;
  101. btn.move(x+dw/2, y+dh/2);
  102. };
  103. adjust_button_position(new_game_button_, x, y);
  104. if (continue_button_.is_visible()) {
  105. adjust_button_position(continue_button_, x, y += BUTTON_HEIGHT+20);
  106. }
  107. adjust_button_position(high_score_button_, x, y += BUTTON_HEIGHT+20);
  108. adjust_button_position(credits_button_, x, y += BUTTON_HEIGHT+20);
  109. adjust_button_position(quit_button_, x, y+BUTTON_HEIGHT+20);
  110. SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
  111. SDL_RenderClear(renderer);
  112. if (game_.has_value()) {
  113. game_.value()->render_game(renderer, false);
  114. }
  115. SDL_SetRenderDrawColor(renderer, 0, 0, 0, 200);
  116. SDL_RenderFillRect(renderer, nullptr);
  117. new_game_button_.render(renderer);
  118. continue_button_.render(renderer);
  119. high_score_button_.render(renderer);
  120. credits_button_.render(renderer);
  121. quit_button_.render(renderer);
  122. SDL_RenderPresent(renderer);
  123. }
  124. void MenuState::on_leave()
  125. {
  126. SDL_ShowCursor(SDL_DISABLE);
  127. }
  128. void MenuState::select_previous_button()
  129. {
  130. SDL_ShowCursor(SDL_DISABLE);
  131. --active_button_;
  132. if (active_button_<0)
  133. active_button_ = 4;
  134. else if (active_button_==1 && !continue_button_.is_visible())
  135. active_button_ = 0;
  136. }
  137. void MenuState::select_next_button()
  138. {
  139. SDL_ShowCursor(SDL_DISABLE);
  140. ++active_button_;
  141. if (active_button_>4)
  142. active_button_ = 0;
  143. else if (active_button_==1 && !continue_button_.is_visible())
  144. active_button_ = 2;
  145. }
  146. void MenuState::handle_key_up(GameStateManager& gsm, SDL_Scancode const scancode)
  147. {
  148. switch (scancode) {
  149. default:
  150. break;
  151. case SDL_SCANCODE_PAUSE:
  152. if (gsm.parent()==nullptr)
  153. break;
  154. [[fallthrough]];
  155. case SDL_SCANCODE_ESCAPE:
  156. gsm.pop_state();
  157. break;
  158. case SDL_SCANCODE_SPACE:
  159. [[fallthrough]];
  160. case SDL_SCANCODE_RETURN:
  161. trigger_active_button();
  162. break;
  163. }
  164. }
  165. void MenuState::handle_key_down(SDL_Scancode const scancode)
  166. {
  167. switch (scancode) {
  168. default:
  169. break;
  170. case SDL_SCANCODE_UP:
  171. select_previous_button();
  172. break;
  173. case SDL_SCANCODE_DOWN:
  174. select_next_button();
  175. break;
  176. }
  177. }
  178. void MenuState::trigger_active_button()
  179. {
  180. switch (active_button_) {
  181. case 0:
  182. new_game_button_.trigger();
  183. break;
  184. case 1:
  185. continue_button_.trigger();
  186. break;
  187. case 2:
  188. high_score_button_.trigger();
  189. break;
  190. case 3:
  191. credits_button_.trigger();
  192. break;
  193. case 4:
  194. quit_button_.trigger();
  195. break;
  196. }
  197. }
  198. void MenuState::handle_mouse_movement(int const x, int const y)
  199. {
  200. SDL_ShowCursor(SDL_ENABLE);
  201. std::array<Button*, 5> buttons{&new_game_button_, nullptr, &high_score_button_, &credits_button_, &quit_button_};
  202. if (continue_button_.is_visible())
  203. buttons[1] = &continue_button_;
  204. for (std::size_t n = 0; n<buttons.size(); ++n) {
  205. if (buttons[n]==nullptr)
  206. continue;
  207. auto const box = buttons[n]->get_bounding_box();
  208. if (x>=box.x && x<=box.x+box.w && y>=box.y && y<=box.y+box.h) {
  209. active_button_ = static_cast<int>(n);
  210. break;
  211. }
  212. }
  213. }
  214. void MenuState::handle_controller_button_up(GameStateManager& gsm, std::uint8_t const button)
  215. {
  216. switch (button) {
  217. default:
  218. break;
  219. case SDL_CONTROLLER_BUTTON_START:
  220. [[fallthrough]];
  221. case SDL_CONTROLLER_BUTTON_B:
  222. if (gsm.parent()==nullptr)
  223. break;
  224. gsm.pop_state();
  225. break;
  226. case SDL_CONTROLLER_BUTTON_A:
  227. trigger_active_button();
  228. break;
  229. }
  230. }
  231. void MenuState::handle_controller_button_down(std::uint8_t const button)
  232. {
  233. switch (button) {
  234. default:
  235. break;
  236. case SDL_CONTROLLER_BUTTON_DPAD_UP:
  237. select_previous_button();
  238. break;
  239. case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
  240. select_next_button();
  241. break;
  242. }
  243. }
  244. void MenuState::handle_controller_axis_motion(std::uint8_t const axis, std::int16_t const value)
  245. {
  246. if (axis!=SDL_CONTROLLER_AXIS_LEFTY)
  247. return;
  248. if (value<-10'000) {
  249. if (last_controller_direction_==-1)
  250. return;
  251. last_controller_direction_ = -1;
  252. select_previous_button();
  253. }
  254. else if (value>10'000) {
  255. if (last_controller_direction_==1)
  256. return;
  257. last_controller_direction_ = 1;
  258. select_next_button();
  259. }
  260. else {
  261. last_controller_direction_ = 0;
  262. }
  263. }