MenuState.cxx 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  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. if (game_.has_value()) {
  111. game_.value()->render(renderer);
  112. }
  113. SDL_SetRenderDrawColor(renderer, 0, 0, 0, 200);
  114. SDL_RenderFillRect(renderer, nullptr);
  115. new_game_button_.render(renderer);
  116. continue_button_.render(renderer);
  117. high_score_button_.render(renderer);
  118. credits_button_.render(renderer);
  119. quit_button_.render(renderer);
  120. }
  121. void MenuState::on_leave()
  122. {
  123. SDL_ShowCursor(SDL_DISABLE);
  124. }
  125. void MenuState::select_previous_button()
  126. {
  127. SDL_ShowCursor(SDL_DISABLE);
  128. --active_button_;
  129. if (active_button_<0)
  130. active_button_ = 4;
  131. else if (active_button_==1 && !continue_button_.is_visible())
  132. active_button_ = 0;
  133. }
  134. void MenuState::select_next_button()
  135. {
  136. SDL_ShowCursor(SDL_DISABLE);
  137. ++active_button_;
  138. if (active_button_>4)
  139. active_button_ = 0;
  140. else if (active_button_==1 && !continue_button_.is_visible())
  141. active_button_ = 2;
  142. }
  143. void MenuState::handle_key_up(GameStateManager& gsm, SDL_Scancode const scancode)
  144. {
  145. switch (scancode) {
  146. default:
  147. break;
  148. case SDL_SCANCODE_PAUSE:
  149. if (gsm.parent()==nullptr)
  150. break;
  151. [[fallthrough]];
  152. case SDL_SCANCODE_ESCAPE:
  153. gsm.pop_state();
  154. break;
  155. case SDL_SCANCODE_SPACE:
  156. [[fallthrough]];
  157. case SDL_SCANCODE_RETURN:
  158. trigger_active_button();
  159. break;
  160. }
  161. }
  162. void MenuState::handle_key_down(SDL_Scancode const scancode)
  163. {
  164. switch (scancode) {
  165. default:
  166. break;
  167. case SDL_SCANCODE_UP:
  168. select_previous_button();
  169. break;
  170. case SDL_SCANCODE_DOWN:
  171. select_next_button();
  172. break;
  173. }
  174. }
  175. void MenuState::trigger_active_button()
  176. {
  177. switch (active_button_) {
  178. case 0:
  179. new_game_button_.trigger();
  180. break;
  181. case 1:
  182. continue_button_.trigger();
  183. break;
  184. case 2:
  185. high_score_button_.trigger();
  186. break;
  187. case 3:
  188. credits_button_.trigger();
  189. break;
  190. case 4:
  191. quit_button_.trigger();
  192. break;
  193. }
  194. }
  195. void MenuState::handle_mouse_movement(int const x, int const y)
  196. {
  197. SDL_ShowCursor(SDL_ENABLE);
  198. std::array<Button*, 5> buttons{&new_game_button_, nullptr, &high_score_button_, &credits_button_, &quit_button_};
  199. if (continue_button_.is_visible())
  200. buttons[1] = &continue_button_;
  201. for (std::size_t n = 0; n<buttons.size(); ++n) {
  202. if (buttons[n]==nullptr)
  203. continue;
  204. auto const box = buttons[n]->get_bounding_box();
  205. if (x>=box.x && x<=box.x+box.w && y>=box.y && y<=box.y+box.h) {
  206. active_button_ = static_cast<int>(n);
  207. break;
  208. }
  209. }
  210. }
  211. void MenuState::handle_controller_button_up(GameStateManager& gsm, std::uint8_t const button)
  212. {
  213. switch (button) {
  214. default:
  215. break;
  216. case SDL_CONTROLLER_BUTTON_START:
  217. [[fallthrough]];
  218. case SDL_CONTROLLER_BUTTON_B:
  219. if (gsm.parent()==nullptr)
  220. break;
  221. gsm.pop_state();
  222. break;
  223. case SDL_CONTROLLER_BUTTON_A:
  224. trigger_active_button();
  225. break;
  226. }
  227. }
  228. void MenuState::handle_controller_button_down(std::uint8_t const button)
  229. {
  230. switch (button) {
  231. default:
  232. break;
  233. case SDL_CONTROLLER_BUTTON_DPAD_UP:
  234. select_previous_button();
  235. break;
  236. case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
  237. select_next_button();
  238. break;
  239. }
  240. }
  241. void MenuState::handle_controller_axis_motion(std::uint8_t const axis, std::int16_t const value)
  242. {
  243. if (axis!=SDL_CONTROLLER_AXIS_LEFTY)
  244. return;
  245. if (value<-10'000) {
  246. if (last_controller_direction_==-1)
  247. return;
  248. last_controller_direction_ = -1;
  249. select_previous_button();
  250. }
  251. else if (value>10'000) {
  252. if (last_controller_direction_==1)
  253. return;
  254. last_controller_direction_ = 1;
  255. select_next_button();
  256. }
  257. else {
  258. last_controller_direction_ = 0;
  259. }
  260. }