💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

318 lines
9.3 KiB

  1. /*
  2. * Copyright (C) 2021 Christopher J. Howard
  3. *
  4. * This file is part of Antkeeper source code.
  5. *
  6. * Antkeeper source code is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Antkeeper source code is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "game/states/keyboard-config-menu.hpp"
  20. #include "game/states/controls-menu.hpp"
  21. #include "application.hpp"
  22. #include "scene/text.hpp"
  23. #include "debug/logger.hpp"
  24. #include "resources/resource-manager.hpp"
  25. #include "game/menu.hpp"
  26. #include "game/controls.hpp"
  27. namespace game {
  28. namespace state {
  29. namespace keyboard_config_menu {
  30. static std::string get_binding_string(game::context* ctx, input::control* control)
  31. {
  32. std::string binding_string;
  33. auto mappings = ctx->input_event_router->get_mappings(control);
  34. for (input::mapping* mapping: *mappings)
  35. {
  36. std::string mapping_string;
  37. switch (mapping->get_type())
  38. {
  39. case input::mapping_type::key:
  40. {
  41. const input::key_mapping* key_mapping = static_cast<const input::key_mapping*>(mapping);
  42. const char* scancode_name = input::keyboard::get_scancode_name(key_mapping->scancode);
  43. mapping_string = scancode_name;
  44. break;
  45. }
  46. case input::mapping_type::mouse_wheel:
  47. {
  48. const input::mouse_wheel_mapping* wheel_mapping = static_cast<const input::mouse_wheel_mapping*>(mapping);
  49. switch (wheel_mapping->axis)
  50. {
  51. case input::mouse_wheel_axis::negative_x:
  52. mapping_string = (*ctx->strings)["mouse_wheel_left"];
  53. break;
  54. case input::mouse_wheel_axis::positive_x:
  55. mapping_string = (*ctx->strings)["mouse_wheel_right"];
  56. break;
  57. case input::mouse_wheel_axis::negative_y:
  58. mapping_string = (*ctx->strings)["mouse_wheel_down"];
  59. break;
  60. case input::mouse_wheel_axis::positive_y:
  61. mapping_string = (*ctx->strings)["mouse_wheel_up"];
  62. break;
  63. default:
  64. break;
  65. }
  66. break;
  67. }
  68. case input::mapping_type::mouse_button:
  69. {
  70. const input::mouse_button_mapping* button_mapping = static_cast<const input::mouse_button_mapping*>(mapping);
  71. if (button_mapping->button == 1)
  72. {
  73. mapping_string = (*ctx->strings)["mouse_button_left"];
  74. }
  75. else if (button_mapping->button == 2)
  76. {
  77. mapping_string = (*ctx->strings)["mouse_button_middle"];
  78. }
  79. else if (button_mapping->button == 3)
  80. {
  81. mapping_string = (*ctx->strings)["mouse_button_right"];
  82. }
  83. else
  84. {
  85. const std::string& format = (*ctx->strings)["mouse_button_n"];
  86. char buffer[64];
  87. std::snprintf(buffer, 64, format.c_str(), button_mapping->button);
  88. mapping_string = buffer;
  89. }
  90. break;
  91. }
  92. default:
  93. break;
  94. }
  95. if (!mapping_string.empty())
  96. {
  97. if (binding_string.empty())
  98. {
  99. binding_string = mapping_string;
  100. }
  101. else
  102. {
  103. binding_string += " " + mapping_string;
  104. }
  105. }
  106. }
  107. return binding_string;
  108. }
  109. static void add_control_item(game::context* ctx, const std::string& control_name)
  110. {
  111. // Get pointer to control
  112. input::control* control = ctx->controls[control_name];
  113. // Construct texts
  114. scene::text* name_text = new scene::text();
  115. scene::text* value_text = new scene::text();
  116. // Add texts to list of menu item texts
  117. ctx->menu_item_texts.push_back({name_text, value_text});
  118. // Set content of name text
  119. std::string string_name = "control_" + control_name;
  120. if (auto it = ctx->strings->find(string_name); it != ctx->strings->end())
  121. name_text->set_content(it->second);
  122. else
  123. name_text->set_content(control_name);
  124. // Set content of value text
  125. value_text->set_content(get_binding_string(ctx, control));
  126. auto select_callback = [ctx, control, value_text]()
  127. {
  128. // Clear binding string from value text
  129. value_text->set_content((*ctx->strings)["ellipsis"]);
  130. game::menu::align_text(ctx);
  131. game::menu::update_text_tweens(ctx);
  132. // Disable controls
  133. game::menu::clear_controls(ctx);
  134. // Remove keyboard and mouse event mappings from control
  135. ctx->input_event_router->remove_mappings(control, input::mapping_type::key);
  136. ctx->input_event_router->remove_mappings(control, input::mapping_type::mouse_motion);
  137. ctx->input_event_router->remove_mappings(control, input::mapping_type::mouse_wheel);
  138. ctx->input_event_router->remove_mappings(control, input::mapping_type::mouse_button);
  139. // Setup input binding listener
  140. ctx->input_listener->set_callback
  141. (
  142. [ctx, control, value_text](const event_base& event)
  143. {
  144. auto id = event.get_event_type_id();
  145. if (id == key_pressed_event::event_type_id)
  146. {
  147. // Map key pressed event to control
  148. const key_pressed_event& key_event = static_cast<const key_pressed_event&>(event);
  149. if (key_event.scancode != input::scancode::escape && key_event.scancode != input::scancode::backspace)
  150. ctx->input_event_router->add_mapping(input::key_mapping(control, key_event.keyboard, key_event.scancode));
  151. }
  152. else if (id == mouse_wheel_scrolled_event::event_type_id)
  153. {
  154. // Map mouse wheel scrolled event to control
  155. const mouse_wheel_scrolled_event& wheel_event = static_cast<const mouse_wheel_scrolled_event&>(event);
  156. input::mouse_wheel_axis axis;
  157. if (wheel_event.x < 0)
  158. axis = input::mouse_wheel_axis::negative_x;
  159. else if (wheel_event.x > 0)
  160. axis = input::mouse_wheel_axis::positive_x;
  161. else if (wheel_event.y < 0)
  162. axis = input::mouse_wheel_axis::negative_y;
  163. else if (wheel_event.y > 0)
  164. axis = input::mouse_wheel_axis::positive_y;
  165. else
  166. return;
  167. ctx->input_event_router->add_mapping(input::mouse_wheel_mapping(control, wheel_event.mouse, axis));
  168. }
  169. else if (id == mouse_button_pressed_event::event_type_id)
  170. {
  171. // Map mouse button pressed event to control
  172. const mouse_button_pressed_event& button_event = static_cast<const mouse_button_pressed_event&>(event);
  173. ctx->input_event_router->add_mapping(input::mouse_button_mapping(control, button_event.mouse, button_event.button));
  174. }
  175. else
  176. {
  177. return;
  178. }
  179. // Update menu text
  180. value_text->set_content(get_binding_string(ctx, control));
  181. game::menu::align_text(ctx);
  182. game::menu::update_text_tweens(ctx);
  183. // Disable input listener
  184. ctx->input_listener->set_enabled(false);
  185. ctx->input_listener->set_callback(nullptr);
  186. // Queue menu control setup
  187. ctx->function_queue.push(std::bind(game::menu::setup_controls, ctx));
  188. }
  189. );
  190. ctx->input_listener->set_enabled(true);
  191. };
  192. // Register menu item callbacks
  193. ctx->menu_select_callbacks.push_back(select_callback);
  194. ctx->menu_left_callbacks.push_back(nullptr);
  195. ctx->menu_right_callbacks.push_back(nullptr);
  196. }
  197. void enter(game::context* ctx)
  198. {
  199. // Add camera control menu items
  200. add_control_item(ctx, "move_forward");
  201. add_control_item(ctx, "move_back");
  202. add_control_item(ctx, "move_left");
  203. add_control_item(ctx, "move_right");
  204. add_control_item(ctx, "move_up");
  205. add_control_item(ctx, "move_down");
  206. // Add application control menu items
  207. add_control_item(ctx, "toggle_fullscreen");
  208. add_control_item(ctx, "screenshot");
  209. // Construct menu item texts
  210. scene::text* back_text = new scene::text();
  211. // Build list of menu item texts
  212. ctx->menu_item_texts.push_back({back_text, nullptr});
  213. // Set content of menu item texts
  214. back_text->set_content((*ctx->strings)["back"]);
  215. // Init menu item index
  216. game::menu::init_menu_item_index(ctx, "keyboard_config");
  217. game::menu::update_text_color(ctx);
  218. game::menu::update_text_font(ctx);
  219. game::menu::align_text(ctx);
  220. game::menu::update_text_tweens(ctx);
  221. game::menu::add_text_to_ui(ctx);
  222. game::menu::setup_animations(ctx);
  223. // Construct menu item callbacks
  224. auto select_back_callback = [ctx]()
  225. {
  226. // Disable controls
  227. game::menu::clear_controls(ctx);
  228. game::menu::fade_out
  229. (
  230. ctx,
  231. [ctx]()
  232. {
  233. application::state next_state;
  234. next_state.name = "controls_menu";
  235. next_state.enter = std::bind(game::state::controls_menu::enter, ctx);
  236. next_state.exit = std::bind(game::state::controls_menu::exit, ctx);
  237. ctx->app->queue_state(next_state);
  238. }
  239. );
  240. };
  241. // Build list of menu select callbacks
  242. ctx->menu_select_callbacks.push_back(select_back_callback);
  243. // Build list of menu left callbacks
  244. ctx->menu_left_callbacks.push_back(nullptr);
  245. // Build list of menu right callbacks
  246. ctx->menu_right_callbacks.push_back(nullptr);
  247. // Set menu back callback
  248. ctx->menu_back_callback = select_back_callback;
  249. // Queue menu control setup
  250. ctx->function_queue.push(std::bind(game::menu::setup_controls, ctx));
  251. // Fade in menu
  252. game::menu::fade_in(ctx, nullptr);
  253. }
  254. void exit(game::context* ctx)
  255. {
  256. // Destruct menu
  257. game::menu::clear_controls(ctx);
  258. game::menu::clear_callbacks(ctx);
  259. game::menu::delete_animations(ctx);
  260. game::menu::remove_text_from_ui(ctx);
  261. game::menu::delete_text(ctx);
  262. // Save control profile
  263. game::save_control_profile(ctx);
  264. }
  265. } // namespace keyboard_config_menu
  266. } // namespace state
  267. } // namespace game