💿🐜 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.

530 lines
17 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/graphics-menu.hpp"
  20. #include "game/states/options-menu.hpp"
  21. #include "application.hpp"
  22. #include "scene/text.hpp"
  23. #include "render/passes/clear-pass.hpp"
  24. #include "debug/logger.hpp"
  25. #include "game/fonts.hpp"
  26. namespace game {
  27. namespace state {
  28. namespace graphics_menu {
  29. static void update_text_color(game::context* ctx)
  30. {
  31. float4 inactive_color = {1.0f, 1.0f, 1.0f, 0.5f};
  32. float4 active_color = {1.0f, 1.0f, 1.0f, 1.0f};
  33. for (std::size_t i = 0; i < ctx->graphics_menu_label_texts.size(); ++i)
  34. {
  35. scene::text* label_text = ctx->graphics_menu_label_texts[i];
  36. scene::text* value_text = ctx->graphics_menu_value_texts[i];
  37. if (i == ctx->graphics_menu_index)
  38. {
  39. label_text->set_color(active_color);
  40. if (value_text)
  41. value_text->set_color(active_color);
  42. }
  43. else
  44. {
  45. label_text->set_color(inactive_color);
  46. if (value_text)
  47. value_text->set_color(inactive_color);
  48. }
  49. }
  50. }
  51. static void align_texts(game::context* ctx)
  52. {
  53. // Calculate menu width
  54. float menu_width = 0.0f;
  55. float menu_spacing = ctx->menu_font.get_glyph_metrics(U'M').width;
  56. for (std::size_t i = 0; i < ctx->graphics_menu_label_texts.size(); ++i)
  57. {
  58. scene::text* label_text = ctx->graphics_menu_label_texts[i];
  59. scene::text* value_text = ctx->graphics_menu_value_texts[i];
  60. float row_width = 0.0f;
  61. // Add label width to width
  62. const auto& label_bounds = static_cast<const geom::aabb<float>&>(label_text->get_local_bounds());
  63. row_width += label_bounds.max_point.x - label_bounds.min_point.x;
  64. if (value_text != nullptr)
  65. {
  66. // Add value width to width
  67. //const auto& value_bounds = static_cast<const geom::aabb<float>&>(value_text->get_local_bounds());
  68. //row_width += value_bounds.max_point.x - value_bounds.min_point.x;
  69. // Add spacing to row width
  70. row_width += menu_spacing * 8.0f;
  71. }
  72. menu_width = std::max<float>(menu_width, row_width);
  73. }
  74. // Align texts
  75. float menu_height = ctx->graphics_menu_label_texts.size() * ctx->menu_font.get_font_metrics().linespace;
  76. float menu_x = -menu_width * 0.5f;
  77. float menu_y = menu_height * 0.5f - ctx->menu_font.get_font_metrics().linespace;
  78. for (std::size_t i = 0; i < ctx->graphics_menu_label_texts.size(); ++i)
  79. {
  80. scene::text* label_text = ctx->graphics_menu_label_texts[i];
  81. float x = menu_x;
  82. float y = menu_y - ctx->menu_font.get_font_metrics().linespace * i;
  83. label_text->set_translation({std::round(x), std::round(y), 0.0f});
  84. scene::text* value_text = ctx->graphics_menu_value_texts[i];
  85. if (value_text)
  86. {
  87. const auto& value_bounds = static_cast<const geom::aabb<float>&>(value_text->get_local_bounds());
  88. const float value_width = value_bounds.max_point.x - value_bounds.min_point.x;
  89. x = menu_x + menu_width - value_width;
  90. value_text->set_translation({std::round(x), std::round(y), 0.0f});
  91. }
  92. }
  93. }
  94. static void update_text_tweens(game::context* ctx)
  95. {
  96. for (std::size_t i = 0; i < ctx->graphics_menu_label_texts.size(); ++i)
  97. {
  98. scene::text* label_text = ctx->graphics_menu_label_texts[i];
  99. scene::text* value_text = ctx->graphics_menu_value_texts[i];
  100. label_text->update_tweens();
  101. if (value_text)
  102. value_text->update_tweens();
  103. }
  104. }
  105. static void refresh_texts(game::context* ctx)
  106. {
  107. for (std::size_t i = 0; i < ctx->graphics_menu_label_texts.size(); ++i)
  108. {
  109. scene::text* label_text = ctx->graphics_menu_label_texts[i];
  110. label_text->refresh();
  111. scene::text* value_text = ctx->graphics_menu_value_texts[i];
  112. if (value_text)
  113. value_text->refresh();
  114. }
  115. }
  116. void enter(game::context* ctx)
  117. {
  118. ctx->ui_clear_pass->set_cleared_buffers(true, true, false);
  119. // Construct graphics menu texts
  120. ctx->graphics_menu_display_mode_label_text = new scene::text();
  121. ctx->graphics_menu_display_mode_value_text = new scene::text();
  122. ctx->graphics_menu_render_resolution_label_text = new scene::text();
  123. ctx->graphics_menu_render_resolution_value_text = new scene::text();
  124. ctx->graphics_menu_v_sync_label_text = new scene::text();
  125. ctx->graphics_menu_v_sync_value_text = new scene::text();
  126. ctx->graphics_menu_font_size_label_text = new scene::text();
  127. ctx->graphics_menu_font_size_value_text = new scene::text();
  128. ctx->graphics_menu_dyslexia_font_label_text = new scene::text();
  129. ctx->graphics_menu_dyslexia_font_value_text = new scene::text();
  130. ctx->graphics_menu_back_label_text = new scene::text();
  131. bool fullscreen = ctx->app->is_fullscreen();
  132. float render_resolution = ctx->render_resolution_scale;
  133. bool vsync = ctx->app->get_vsync();
  134. float font_size = ctx->font_size;
  135. bool dyslexia_font = ctx->dyslexia_font;
  136. const std::string string_fullscreen = (*ctx->strings)["graphics_menu_display_mode_fullscreen"];
  137. const std::string string_window = (*ctx->strings)["graphics_menu_display_mode_window"];
  138. const std::string string_on = (*ctx->strings)["on"];
  139. const std::string string_off = (*ctx->strings)["off"];
  140. // Set text content
  141. ctx->graphics_menu_display_mode_label_text->set_content((*ctx->strings)["graphics_menu_display_mode"]);
  142. ctx->graphics_menu_display_mode_value_text->set_content((fullscreen) ? string_fullscreen : string_window);
  143. ctx->graphics_menu_render_resolution_label_text->set_content((*ctx->strings)["graphics_menu_render_resolution"]);
  144. ctx->graphics_menu_render_resolution_value_text->set_content(std::to_string(static_cast<int>(std::round(render_resolution * 100.0f))) + "%");
  145. ctx->graphics_menu_v_sync_label_text->set_content((*ctx->strings)["graphics_menu_v_sync"]);
  146. ctx->graphics_menu_v_sync_value_text->set_content((vsync) ? string_on : string_off);
  147. ctx->graphics_menu_font_size_label_text->set_content((*ctx->strings)["graphics_menu_font_size"]);
  148. ctx->graphics_menu_font_size_value_text->set_content(std::to_string(static_cast<int>(std::round(font_size * 100.0f))) + "%");
  149. ctx->graphics_menu_dyslexia_font_label_text->set_content((*ctx->strings)["graphics_menu_dyslexia_font"]);
  150. ctx->graphics_menu_dyslexia_font_value_text->set_content((dyslexia_font) ? string_on : string_off);
  151. ctx->graphics_menu_back_label_text->set_content((*ctx->strings)["back"]);
  152. // Build lists of graphics menu texts
  153. ctx->graphics_menu_label_texts.push_back(ctx->graphics_menu_display_mode_label_text);
  154. ctx->graphics_menu_label_texts.push_back(ctx->graphics_menu_render_resolution_label_text);
  155. ctx->graphics_menu_label_texts.push_back(ctx->graphics_menu_v_sync_label_text);
  156. ctx->graphics_menu_label_texts.push_back(ctx->graphics_menu_font_size_label_text);
  157. ctx->graphics_menu_label_texts.push_back(ctx->graphics_menu_dyslexia_font_label_text);
  158. ctx->graphics_menu_label_texts.push_back(ctx->graphics_menu_back_label_text);
  159. ctx->graphics_menu_value_texts.push_back(ctx->graphics_menu_display_mode_value_text);
  160. ctx->graphics_menu_value_texts.push_back(ctx->graphics_menu_render_resolution_value_text);
  161. ctx->graphics_menu_value_texts.push_back(ctx->graphics_menu_v_sync_value_text);
  162. ctx->graphics_menu_value_texts.push_back(ctx->graphics_menu_font_size_value_text);
  163. ctx->graphics_menu_value_texts.push_back(ctx->graphics_menu_dyslexia_font_value_text);
  164. ctx->graphics_menu_value_texts.push_back(nullptr);
  165. // Set text fonts
  166. for (std::size_t i = 0; i < ctx->graphics_menu_label_texts.size(); ++i)
  167. {
  168. scene::text* label_text = ctx->graphics_menu_label_texts[i];
  169. label_text->set_material(&ctx->menu_font_material);
  170. label_text->set_font(&ctx->menu_font);
  171. scene::text* value_text = ctx->graphics_menu_value_texts[i];
  172. if (value_text)
  173. {
  174. value_text->set_material(&ctx->menu_font_material);
  175. value_text->set_font(&ctx->menu_font);
  176. }
  177. }
  178. // Align texts
  179. align_texts(ctx);
  180. // Construct graphics menu callbacks
  181. auto menu_back_callback = [ctx]()
  182. {
  183. application::state next_state;
  184. next_state.name = "options_menu";
  185. next_state.enter = std::bind(game::state::options_menu::enter, ctx);
  186. next_state.exit = std::bind(game::state::options_menu::exit, ctx);
  187. ctx->app->change_state(next_state);
  188. };
  189. ctx->controls["menu_down"]->set_activated_callback
  190. (
  191. [ctx]()
  192. {
  193. ++ctx->graphics_menu_index;
  194. if (ctx->graphics_menu_index >= ctx->graphics_menu_label_texts.size())
  195. ctx->graphics_menu_index = 0;
  196. update_text_color(ctx);
  197. }
  198. );
  199. ctx->controls["menu_up"]->set_activated_callback
  200. (
  201. [ctx]()
  202. {
  203. --ctx->graphics_menu_index;
  204. if (ctx->graphics_menu_index < 0)
  205. ctx->graphics_menu_index = ctx->graphics_menu_label_texts.size() - 1;
  206. update_text_color(ctx);
  207. }
  208. );
  209. ctx->controls["menu_left"]->set_activated_callback
  210. (
  211. [ctx]()
  212. {
  213. if (ctx->graphics_menu_left_callbacks[ctx->graphics_menu_index])
  214. ctx->graphics_menu_left_callbacks[ctx->graphics_menu_index]();
  215. }
  216. );
  217. ctx->controls["menu_right"]->set_activated_callback
  218. (
  219. [ctx]()
  220. {
  221. if (ctx->graphics_menu_right_callbacks[ctx->graphics_menu_index])
  222. ctx->graphics_menu_right_callbacks[ctx->graphics_menu_index]();
  223. }
  224. );
  225. ctx->controls["menu_select"]->set_activated_callback
  226. (
  227. [ctx]()
  228. {
  229. if (ctx->graphics_menu_select_callbacks[ctx->graphics_menu_index])
  230. ctx->graphics_menu_select_callbacks[ctx->graphics_menu_index]();
  231. }
  232. );
  233. ctx->controls["menu_back"]->set_activated_callback(menu_back_callback);
  234. auto toggle_fullscreen_callback = [ctx]()
  235. {
  236. bool fullscreen = !ctx->app->is_fullscreen();
  237. ctx->app->set_fullscreen(fullscreen);
  238. if (!fullscreen)
  239. {
  240. int2 resolution;
  241. resolution.x = (*ctx->config)["windowed_resolution"][0].get<int>();
  242. resolution.y = (*ctx->config)["windowed_resolution"][1].get<int>();
  243. ctx->app->resize_window(resolution.x, resolution.y);
  244. }
  245. const std::string string_fullscreen = (*ctx->strings)["graphics_menu_display_mode_fullscreen"];
  246. const std::string string_window = (*ctx->strings)["graphics_menu_display_mode_window"];;
  247. ctx->graphics_menu_display_mode_value_text->set_content((fullscreen) ? string_fullscreen : string_window);
  248. // Save display mode config
  249. (*ctx->config)["fullscreen"] = fullscreen;
  250. };
  251. auto increase_render_resolution_callback = [ctx]()
  252. {
  253. // Increase resolution
  254. if (ctx->controls["menu_modifier"]->is_active())
  255. ctx->render_resolution_scale += 0.01f;
  256. else
  257. ctx->render_resolution_scale += 0.1f;
  258. // Limit resolution
  259. if (ctx->render_resolution_scale > 2.0f)
  260. ctx->render_resolution_scale = 2.0f;
  261. // Update value text
  262. ctx->graphics_menu_render_resolution_value_text->set_content(std::to_string(static_cast<int>(std::round(ctx->render_resolution_scale * 100.0f))) + "%");
  263. // Update config
  264. (*ctx->config)["render_resolution"] = ctx->render_resolution_scale;
  265. };
  266. auto decrease_render_resolution_callback = [ctx]()
  267. {
  268. // Increase resolution
  269. if (ctx->controls["menu_modifier"]->is_active())
  270. ctx->render_resolution_scale -= 0.01f;
  271. else
  272. ctx->render_resolution_scale -= 0.1f;
  273. // Limit resolution
  274. if (ctx->render_resolution_scale < 0.1f)
  275. ctx->render_resolution_scale = 0.1f;
  276. // Update value text
  277. ctx->graphics_menu_render_resolution_value_text->set_content(std::to_string(static_cast<int>(std::round(ctx->render_resolution_scale * 100.0f))) + "%");
  278. // Update config
  279. (*ctx->config)["render_resolution"] = ctx->render_resolution_scale;
  280. };
  281. auto increase_font_size_callback = [ctx]()
  282. {
  283. // Increase font size
  284. if (ctx->controls["menu_modifier"]->is_active())
  285. ctx->font_size += 0.01f;
  286. else
  287. ctx->font_size += 0.1f;
  288. // Limit font size
  289. if (ctx->font_size > 2.0f)
  290. ctx->font_size = 2.0f;
  291. // Update value text
  292. ctx->graphics_menu_font_size_value_text->set_content(std::to_string(static_cast<int>(std::round(ctx->font_size * 100.0f))) + "%");
  293. // Update config
  294. (*ctx->config)["font_size"] = ctx->font_size;
  295. // Reload fonts
  296. ctx->logger->push_task("Reloading fonts");
  297. try
  298. {
  299. game::load_fonts(ctx);
  300. }
  301. catch (...)
  302. {
  303. ctx->logger->pop_task(EXIT_FAILURE);
  304. }
  305. ctx->logger->pop_task(EXIT_SUCCESS);
  306. // Refresh text
  307. refresh_texts(ctx);
  308. // Realign texts
  309. align_texts(ctx);
  310. update_text_tweens(ctx);
  311. };
  312. auto decrease_font_size_callback = [ctx]()
  313. {
  314. // Decrease font size
  315. if (ctx->controls["menu_modifier"]->is_active())
  316. ctx->font_size -= 0.01f;
  317. else
  318. ctx->font_size -= 0.1f;
  319. // Limit font size
  320. if (ctx->font_size < 0.1f)
  321. ctx->font_size = 0.1f;
  322. // Update value text
  323. ctx->graphics_menu_font_size_value_text->set_content(std::to_string(static_cast<int>(std::round(ctx->font_size * 100.0f))) + "%");
  324. // Update config
  325. (*ctx->config)["font_size"] = ctx->font_size;
  326. // Reload fonts
  327. ctx->logger->push_task("Reloading fonts");
  328. try
  329. {
  330. game::load_fonts(ctx);
  331. }
  332. catch (...)
  333. {
  334. ctx->logger->pop_task(EXIT_FAILURE);
  335. }
  336. ctx->logger->pop_task(EXIT_SUCCESS);
  337. // Refresh text
  338. refresh_texts(ctx);
  339. // Realign texts
  340. align_texts(ctx);
  341. update_text_tweens(ctx);
  342. };
  343. auto toggle_vsync_callback = [ctx]()
  344. {
  345. bool vsync = !ctx->app->get_vsync();
  346. ctx->app->set_vsync(vsync);
  347. std::string string_on = (*ctx->strings)["on"];
  348. std::string string_off = (*ctx->strings)["off"];
  349. ctx->graphics_menu_v_sync_value_text->set_content((vsync) ? string_on : string_off);
  350. // Save v-sync config
  351. (*ctx->config)["vsync"] = vsync;
  352. };
  353. auto toggle_dyslexia_font_callback = [ctx]()
  354. {
  355. ctx->dyslexia_font = !ctx->dyslexia_font;
  356. std::string string_on = (*ctx->strings)["on"];
  357. std::string string_off = (*ctx->strings)["off"];
  358. ctx->graphics_menu_dyslexia_font_value_text->set_content((ctx->dyslexia_font) ? string_on : string_off);
  359. // Save dyslexia font config
  360. (*ctx->config)["dyslexia_font"] = ctx->dyslexia_font;
  361. // Reload fonts
  362. ctx->logger->push_task("Reloading fonts");
  363. try
  364. {
  365. game::load_fonts(ctx);
  366. }
  367. catch (...)
  368. {
  369. ctx->logger->pop_task(EXIT_FAILURE);
  370. }
  371. ctx->logger->pop_task(EXIT_SUCCESS);
  372. // Refresh text
  373. refresh_texts(ctx);
  374. // Realign texts
  375. align_texts(ctx);
  376. update_text_tweens(ctx);
  377. };
  378. // Build list of graphics menu callbacks
  379. ctx->graphics_menu_select_callbacks.push_back(toggle_fullscreen_callback);
  380. ctx->graphics_menu_select_callbacks.push_back(increase_render_resolution_callback);
  381. ctx->graphics_menu_select_callbacks.push_back(toggle_vsync_callback);
  382. ctx->graphics_menu_select_callbacks.push_back(increase_font_size_callback);
  383. ctx->graphics_menu_select_callbacks.push_back(toggle_dyslexia_font_callback);
  384. ctx->graphics_menu_select_callbacks.push_back(menu_back_callback);
  385. ctx->graphics_menu_left_callbacks.push_back(toggle_fullscreen_callback);
  386. ctx->graphics_menu_left_callbacks.push_back(decrease_render_resolution_callback);
  387. ctx->graphics_menu_left_callbacks.push_back(toggle_vsync_callback);
  388. ctx->graphics_menu_left_callbacks.push_back(decrease_font_size_callback);
  389. ctx->graphics_menu_left_callbacks.push_back(toggle_dyslexia_font_callback);
  390. ctx->graphics_menu_left_callbacks.push_back(nullptr);
  391. ctx->graphics_menu_right_callbacks.push_back(toggle_fullscreen_callback);
  392. ctx->graphics_menu_right_callbacks.push_back(increase_render_resolution_callback);
  393. ctx->graphics_menu_right_callbacks.push_back(toggle_vsync_callback);
  394. ctx->graphics_menu_right_callbacks.push_back(increase_font_size_callback);
  395. ctx->graphics_menu_right_callbacks.push_back(toggle_dyslexia_font_callback);
  396. ctx->graphics_menu_right_callbacks.push_back(nullptr);
  397. // Add text objects to UI
  398. for (std::size_t i = 0; i < ctx->graphics_menu_label_texts.size(); ++i)
  399. {
  400. scene::text* label_text = ctx->graphics_menu_label_texts[i];
  401. scene::text* value_text = ctx->graphics_menu_value_texts[i];
  402. ctx->ui_scene->add_object(label_text);
  403. if (value_text)
  404. ctx->ui_scene->add_object(value_text);
  405. }
  406. update_text_color(ctx);
  407. update_text_tweens(ctx);
  408. }
  409. void exit(game::context* ctx)
  410. {
  411. // Clear control callbacks
  412. ctx->controls["menu_down"]->set_activated_callback(nullptr);
  413. ctx->controls["menu_up"]->set_activated_callback(nullptr);
  414. ctx->controls["menu_left"]->set_activated_callback(nullptr);
  415. ctx->controls["menu_right"]->set_activated_callback(nullptr);
  416. ctx->controls["menu_select"]->set_activated_callback(nullptr);
  417. ctx->controls["menu_back"]->set_activated_callback(nullptr);
  418. // Clear menu callbacks
  419. ctx->graphics_menu_select_callbacks.clear();
  420. ctx->graphics_menu_left_callbacks.clear();
  421. ctx->graphics_menu_right_callbacks.clear();
  422. // Destruct graphics menu texts
  423. for (std::size_t i = 0; i < ctx->graphics_menu_label_texts.size(); ++i)
  424. {
  425. scene::text* label_text = ctx->graphics_menu_label_texts[i];
  426. ctx->ui_scene->remove_object(label_text);
  427. delete label_text;
  428. scene::text* value_text = ctx->graphics_menu_value_texts[i];
  429. if (value_text)
  430. {
  431. ctx->ui_scene->remove_object(value_text);
  432. delete value_text;
  433. }
  434. }
  435. ctx->graphics_menu_label_texts.clear();
  436. ctx->graphics_menu_value_texts.clear();
  437. // Update volumes in config
  438. //(*ctx->config)["master_volume"] = ctx->master_volume;
  439. //(*ctx->config)["ambience_volume"] = ctx->ambience_volume;
  440. //(*ctx->config)["effects_volume"] = ctx->effects_volume;
  441. ctx->ui_clear_pass->set_cleared_buffers(false, true, false);
  442. }
  443. } // namespace graphics_menu
  444. } // namespace state
  445. } // namespace game