gameconfig.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. // SuperTux - A Jump'n Run
  2. // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. #include "supertux/gameconfig.hpp"
  17. #include <ctime>
  18. #include "editor/overlay_widget.hpp"
  19. #include "supertux/colorscheme.hpp"
  20. #include "util/reader_collection.hpp"
  21. #include "util/reader_document.hpp"
  22. #include "util/reader_mapping.hpp"
  23. #include "util/writer.hpp"
  24. #include "util/log.hpp"
  25. #include "video/video_system.hpp"
  26. #include "video/viewport.hpp"
  27. #ifdef __EMSCRIPTEN__
  28. #include <emscripten.h>
  29. #include <emscripten/html5.h>
  30. #endif
  31. Config::Config() :
  32. profile(1),
  33. fullscreen_size(0, 0),
  34. fullscreen_refresh_rate(0),
  35. window_size(1280, 800),
  36. window_resizable(true),
  37. aspect_size(0, 0), // Auto detect.
  38. #ifdef __EMSCRIPTEN__
  39. fit_window(true),
  40. #endif
  41. magnification(0.0f),
  42. // Ubuntu Touch supports windowed apps.
  43. #ifdef __ANDROID__
  44. use_fullscreen(true),
  45. #else
  46. use_fullscreen(false),
  47. #endif
  48. video(VideoSystem::VIDEO_AUTO),
  49. vsync(1),
  50. show_fps(false),
  51. show_player_pos(false),
  52. show_controller(false),
  53. sound_enabled(true),
  54. music_enabled(true),
  55. sound_volume(100),
  56. music_volume(50),
  57. random_seed(0), // Set by time(), by default (unless in config).
  58. enable_script_debugger(false),
  59. tux_spawn_pos(),
  60. locale(),
  61. keyboard_config(),
  62. joystick_config(),
  63. mobile_controls(SDL_GetNumTouchDevices() > 0),
  64. m_mobile_controls_scale(1),
  65. addons(),
  66. developer_mode(false),
  67. christmas_mode(false),
  68. transitions_enabled(true),
  69. confirmation_dialog(false),
  70. pause_on_focusloss(true),
  71. custom_mouse_cursor(true),
  72. #ifdef __EMSCRIPTEN__
  73. do_release_check(false),
  74. #else
  75. do_release_check(true),
  76. #endif
  77. custom_title_levels(true),
  78. #ifdef ENABLE_DISCORD
  79. enable_discord(false),
  80. #endif
  81. hide_editor_levelnames(false),
  82. notifications(),
  83. menubackcolor(ColorScheme::Menu::back_color),
  84. menufrontcolor(ColorScheme::Menu::front_color),
  85. menuhelpbackcolor(ColorScheme::Menu::help_back_color),
  86. menuhelpfrontcolor(ColorScheme::Menu::help_front_color),
  87. labeltextcolor(ColorScheme::Menu::label_color),
  88. activetextcolor(ColorScheme::Menu::active_color),
  89. hlcolor(ColorScheme::Menu::hl_color),
  90. editorcolor(ColorScheme::Editor::default_color),
  91. editorhovercolor(ColorScheme::Editor::hover_color),
  92. editorgrabcolor(ColorScheme::Editor::grab_color),
  93. menuroundness(16.f),
  94. editor_selected_snap_grid_size(3),
  95. editor_render_grid(true),
  96. editor_snap_to_grid(true),
  97. editor_render_background(true),
  98. editor_render_lighting(false),
  99. editor_autotile_mode(false),
  100. editor_autotile_help(true),
  101. editor_autosave_frequency(5),
  102. editor_undo_tracking(true),
  103. editor_undo_stack_size(20),
  104. editor_show_deprecated_tiles(false),
  105. multiplayer_auto_manage_players(true),
  106. multiplayer_multibind(false),
  107. #if SDL_VERSION_ATLEAST(2, 0, 9)
  108. multiplayer_buzz_controllers(true),
  109. #else
  110. // Will be loaded and saved anyways, to retain the setting. This is helpful
  111. // for users who frequently switch between versions compiled with a newer SDL
  112. // and those with an older SDL; they won't have to check the setting each time.
  113. multiplayer_buzz_controllers(false),
  114. #endif
  115. repository_url()
  116. {
  117. }
  118. void
  119. Config::load()
  120. {
  121. #ifdef __EMSCRIPTEN__
  122. EM_ASM({
  123. supertux_loadFiles();
  124. }, 0); // EM_ASM is a variadic macro and Clang requires at least 1 value for the variadic argument.
  125. #endif
  126. auto doc = ReaderDocument::from_file("config");
  127. auto root = doc.get_root();
  128. if (root.get_name() != "supertux-config")
  129. {
  130. throw std::runtime_error("File is not a supertux-config file");
  131. }
  132. auto config_mapping = root.get_mapping();
  133. config_mapping.get("profile", profile);
  134. config_mapping.get("show_fps", show_fps);
  135. config_mapping.get("show_player_pos", show_player_pos);
  136. config_mapping.get("show_controller", show_controller);
  137. config_mapping.get("developer", developer_mode);
  138. config_mapping.get("confirmation_dialog", confirmation_dialog);
  139. config_mapping.get("pause_on_focusloss", pause_on_focusloss);
  140. config_mapping.get("custom_mouse_cursor", custom_mouse_cursor);
  141. config_mapping.get("do_release_check", do_release_check);
  142. config_mapping.get("custom_title_levels", custom_title_levels);
  143. std::optional<ReaderMapping> config_integrations_mapping;
  144. if (config_mapping.get("integrations", config_integrations_mapping))
  145. {
  146. config_integrations_mapping->get("hide_editor_levelnames", hide_editor_levelnames);
  147. #ifdef ENABLE_DISCORD
  148. config_integrations_mapping->get("enable_discord", enable_discord);
  149. #endif
  150. }
  151. std::optional<ReaderCollection> config_notifications_mapping;
  152. if (config_mapping.get("notifications", config_notifications_mapping))
  153. {
  154. for (auto const& notification_node : config_notifications_mapping->get_objects())
  155. {
  156. if (notification_node.get_name() == "notification")
  157. {
  158. auto notification = notification_node.get_mapping();
  159. std::string id;
  160. bool disabled = false;
  161. if (notification.get("id", id) &&
  162. notification.get("disabled", disabled))
  163. {
  164. notifications.push_back({id, disabled});
  165. }
  166. }
  167. else
  168. {
  169. log_warning << "Unknown token in config file: " << notification_node.get_name() << std::endl;
  170. }
  171. }
  172. }
  173. // Menu colors.
  174. std::vector<float> menubackcolor_, menufrontcolor_, menuhelpbackcolor_, menuhelpfrontcolor_,
  175. labeltextcolor_, activetextcolor_, hlcolor_, editorcolor_, editorhovercolor_, editorgrabcolor_;
  176. std::optional<ReaderMapping> interface_colors_mapping;
  177. if (config_mapping.get("interface_colors", interface_colors_mapping))
  178. {
  179. interface_colors_mapping->get("menubackcolor", menubackcolor_, ColorScheme::Menu::back_color.toVector());
  180. interface_colors_mapping->get("menufrontcolor", menufrontcolor_, ColorScheme::Menu::front_color.toVector());
  181. interface_colors_mapping->get("menuhelpbackcolor", menuhelpbackcolor_, ColorScheme::Menu::help_back_color.toVector());
  182. interface_colors_mapping->get("menuhelpfrontcolor", menuhelpfrontcolor_, ColorScheme::Menu::help_back_color.toVector());
  183. interface_colors_mapping->get("labeltextcolor", labeltextcolor_, ColorScheme::Menu::label_color.toVector());
  184. interface_colors_mapping->get("activetextkcolor", activetextcolor_, ColorScheme::Menu::active_color.toVector());
  185. interface_colors_mapping->get("hlcolor", hlcolor_, ColorScheme::Menu::hl_color.toVector());
  186. interface_colors_mapping->get("editorcolor", editorcolor_, ColorScheme::Editor::default_color.toVector());
  187. interface_colors_mapping->get("editorhovercolor", editorhovercolor_, ColorScheme::Editor::hover_color.toVector());
  188. interface_colors_mapping->get("editorgrabcolor", editorgrabcolor_, ColorScheme::Editor::grab_color.toVector());
  189. menubackcolor = Color(menubackcolor_);
  190. menufrontcolor = Color(menufrontcolor_);
  191. menuhelpbackcolor = Color(menuhelpbackcolor_);
  192. menuhelpfrontcolor = Color(menuhelpfrontcolor_);
  193. labeltextcolor = Color(labeltextcolor_);
  194. activetextcolor = Color(activetextcolor_);
  195. hlcolor = Color(hlcolor_);
  196. editorcolor = Color(editorcolor_);
  197. editorhovercolor = Color(editorhovercolor_);
  198. editorgrabcolor = Color(editorgrabcolor_);
  199. interface_colors_mapping->get("menuroundness", menuroundness, 16.f);
  200. }
  201. // Compatibility; will be overwritten by the "editor" category.
  202. config_mapping.get("editor_autosave_frequency", editor_autosave_frequency);
  203. editor_autotile_help = !developer_mode;
  204. std::optional<ReaderMapping> editor_mapping;
  205. if (config_mapping.get("editor", editor_mapping))
  206. {
  207. editor_mapping->get("autosave_frequency", editor_autosave_frequency);
  208. editor_mapping->get("autotile_help", editor_autotile_help);
  209. editor_mapping->get("autotile_mode", editor_autotile_mode);
  210. editor_mapping->get("render_background", editor_render_background);
  211. editor_mapping->get("render_grid", editor_render_grid);
  212. editor_mapping->get("render_lighting", editor_render_lighting);
  213. editor_mapping->get("selected_snap_grid_size", editor_selected_snap_grid_size);
  214. editor_mapping->get("snap_to_grid", editor_snap_to_grid);
  215. editor_mapping->get("undo_tracking", editor_undo_tracking);
  216. editor_mapping->get("undo_stack_size", editor_undo_stack_size);
  217. if (editor_undo_stack_size < 1)
  218. {
  219. log_warning << "Undo stack size could not be lower than 1. Setting to lowest possible value (1)." << std::endl;
  220. editor_undo_stack_size = 1;
  221. }
  222. editor_mapping->get("show_deprecated_tiles", editor_show_deprecated_tiles);
  223. }
  224. if (is_christmas()) {
  225. config_mapping.get("christmas", christmas_mode, true);
  226. }
  227. config_mapping.get("transitions_enabled", transitions_enabled);
  228. config_mapping.get("locale", locale);
  229. config_mapping.get("random_seed", random_seed);
  230. config_mapping.get("repository_url", repository_url);
  231. config_mapping.get("multiplayer_auto_manage_players", multiplayer_auto_manage_players);
  232. config_mapping.get("multiplayer_multibind", multiplayer_multibind);
  233. config_mapping.get("multiplayer_buzz_controllers", multiplayer_buzz_controllers);
  234. std::optional<ReaderMapping> config_video_mapping;
  235. if (config_mapping.get("video", config_video_mapping))
  236. {
  237. config_video_mapping->get("fullscreen", use_fullscreen);
  238. std::string video_string;
  239. config_video_mapping->get("video", video_string);
  240. video = VideoSystem::get_video_system(video_string);
  241. config_video_mapping->get("vsync", vsync);
  242. config_video_mapping->get("fullscreen_width", fullscreen_size.width);
  243. config_video_mapping->get("fullscreen_height", fullscreen_size.height);
  244. if (fullscreen_size.width < 0 || fullscreen_size.height < 0)
  245. {
  246. // Somehow, an invalid size got entered into the config file,
  247. // let's use the "auto" setting instead.
  248. fullscreen_size = Size(0, 0);
  249. }
  250. config_video_mapping->get("fullscreen_refresh_rate", fullscreen_refresh_rate);
  251. config_video_mapping->get("window_width", window_size.width);
  252. config_video_mapping->get("window_height", window_size.height);
  253. config_video_mapping->get("window_resizable", window_resizable);
  254. config_video_mapping->get("aspect_width", aspect_size.width);
  255. config_video_mapping->get("aspect_height", aspect_size.height);
  256. config_video_mapping->get("magnification", magnification);
  257. #ifdef __EMSCRIPTEN__
  258. // Forcibly set autofit to true.
  259. // TODO: Remove the autofit parameter entirely - it should always be true.
  260. //config_video_mapping->get("fit_window", fit_window);
  261. fit_window = true;
  262. #endif
  263. }
  264. std::optional<ReaderMapping> config_audio_mapping;
  265. if (config_mapping.get("audio", config_audio_mapping))
  266. {
  267. config_audio_mapping->get("sound_enabled", sound_enabled);
  268. config_audio_mapping->get("music_enabled", music_enabled);
  269. config_audio_mapping->get("sound_volume", sound_volume);
  270. config_audio_mapping->get("music_volume", music_volume);
  271. }
  272. std::optional<ReaderMapping> config_control_mapping;
  273. if (config_mapping.get("control", config_control_mapping))
  274. {
  275. std::optional<ReaderMapping> keymap_mapping;
  276. if (config_control_mapping->get("keymap", keymap_mapping))
  277. {
  278. keyboard_config.read(*keymap_mapping);
  279. }
  280. std::optional<ReaderMapping> joystick_mapping;
  281. if (config_control_mapping->get("joystick", joystick_mapping))
  282. {
  283. joystick_config.read(*joystick_mapping);
  284. }
  285. config_control_mapping->get("mobile_controls", mobile_controls, SDL_GetNumTouchDevices() > 0);
  286. config_control_mapping->get("mobile_controls_scale", m_mobile_controls_scale, 1);
  287. }
  288. std::optional<ReaderCollection> config_addons_mapping;
  289. if (config_mapping.get("addons", config_addons_mapping))
  290. {
  291. for (auto const& addon_node : config_addons_mapping->get_objects())
  292. {
  293. if (addon_node.get_name() == "addon")
  294. {
  295. auto addon = addon_node.get_mapping();
  296. std::string id;
  297. bool enabled = false;
  298. if (addon.get("id", id) &&
  299. addon.get("enabled", enabled))
  300. {
  301. addons.push_back({id, enabled});
  302. }
  303. }
  304. else
  305. {
  306. log_warning << "Unknown token in config file: " << addon_node.get_name() << std::endl;
  307. }
  308. }
  309. }
  310. }
  311. void
  312. Config::save()
  313. {
  314. Writer writer("config");
  315. writer.start_list("supertux-config");
  316. writer.write("profile", profile);
  317. writer.write("show_fps", show_fps);
  318. writer.write("show_player_pos", show_player_pos);
  319. writer.write("show_controller", show_controller);
  320. writer.write("developer", developer_mode);
  321. writer.write("confirmation_dialog", confirmation_dialog);
  322. writer.write("pause_on_focusloss", pause_on_focusloss);
  323. writer.write("custom_mouse_cursor", custom_mouse_cursor);
  324. writer.write("do_release_check", do_release_check);
  325. writer.write("custom_title_levels", custom_title_levels);
  326. writer.start_list("integrations");
  327. {
  328. writer.write("hide_editor_levelnames", hide_editor_levelnames);
  329. #ifdef ENABLE_DISCORD
  330. writer.write("enable_discord", enable_discord);
  331. #endif
  332. }
  333. writer.end_list("integrations");
  334. writer.start_list("notifications");
  335. for (const auto& notification : notifications)
  336. {
  337. writer.start_list("notification");
  338. writer.write("id", notification.id);
  339. writer.write("disabled", notification.disabled);
  340. writer.end_list("notification");
  341. }
  342. writer.end_list("notifications");
  343. writer.write("editor_autosave_frequency", editor_autosave_frequency);
  344. if (is_christmas()) {
  345. writer.write("christmas", christmas_mode);
  346. }
  347. writer.write("transitions_enabled", transitions_enabled);
  348. writer.write("locale", locale);
  349. writer.write("repository_url", repository_url);
  350. writer.write("multiplayer_auto_manage_players", multiplayer_auto_manage_players);
  351. writer.write("multiplayer_multibind", multiplayer_multibind);
  352. writer.write("multiplayer_buzz_controllers", multiplayer_buzz_controllers);
  353. writer.start_list("interface_colors");
  354. writer.write("menubackcolor", menubackcolor.toVector());
  355. writer.write("menufrontcolor", menufrontcolor.toVector());
  356. writer.write("menuhelpbackcolor", menuhelpbackcolor.toVector());
  357. writer.write("menuhelpfrontcolor", menuhelpfrontcolor.toVector());
  358. writer.write("labeltextcolor", labeltextcolor.toVector());
  359. writer.write("activetextcolor", activetextcolor.toVector());
  360. writer.write("hlcolor", hlcolor.toVector());
  361. writer.write("editorcolor", editorcolor.toVector());
  362. writer.write("editorhovercolor", editorhovercolor.toVector());
  363. writer.write("editorgrabcolor", editorgrabcolor.toVector());
  364. writer.write("menuroundness", menuroundness);
  365. writer.end_list("interface_colors");
  366. writer.start_list("video");
  367. writer.write("fullscreen", use_fullscreen);
  368. if (video == VideoSystem::VIDEO_NULL) {
  369. // Avoid saving a NULL renderer to the configuration, as starting SuperTux without
  370. // getting a window is rather confusing.
  371. } else {
  372. writer.write("video", VideoSystem::get_video_string(video));
  373. }
  374. writer.write("vsync", vsync);
  375. writer.write("fullscreen_width", fullscreen_size.width);
  376. writer.write("fullscreen_height", fullscreen_size.height);
  377. writer.write("fullscreen_refresh_rate", fullscreen_refresh_rate);
  378. writer.write("window_width", window_size.width);
  379. writer.write("window_height", window_size.height);
  380. writer.write("window_resizable", window_resizable);
  381. writer.write("aspect_width", aspect_size.width);
  382. writer.write("aspect_height", aspect_size.height);
  383. #ifdef __EMSCRIPTEN__
  384. // Forcibly set autofit to true
  385. // TODO: Remove the autofit parameter entirely - it should always be true
  386. writer.write("fit_window", true /* fit_window */);
  387. #endif
  388. writer.write("magnification", magnification);
  389. writer.end_list("video");
  390. writer.start_list("audio");
  391. writer.write("sound_enabled", sound_enabled);
  392. writer.write("music_enabled", music_enabled);
  393. writer.write("sound_volume", sound_volume);
  394. writer.write("music_volume", music_volume);
  395. writer.end_list("audio");
  396. writer.start_list("control");
  397. {
  398. writer.start_list("keymap");
  399. keyboard_config.write(writer);
  400. writer.end_list("keymap");
  401. writer.start_list("joystick");
  402. joystick_config.write(writer);
  403. writer.end_list("joystick");
  404. writer.write("mobile_controls", mobile_controls);
  405. writer.write("mobile_controls_scale", m_mobile_controls_scale);
  406. }
  407. writer.end_list("control");
  408. writer.start_list("addons");
  409. for (const auto& addon : addons)
  410. {
  411. writer.start_list("addon");
  412. writer.write("id", addon.id);
  413. writer.write("enabled", addon.enabled);
  414. writer.end_list("addon");
  415. }
  416. writer.end_list("addons");
  417. writer.start_list("editor");
  418. {
  419. writer.write("autosave_frequency", editor_autosave_frequency);
  420. writer.write("autotile_help", editor_autotile_help);
  421. writer.write("autotile_mode", editor_autotile_mode);
  422. writer.write("render_background", editor_render_background);
  423. writer.write("render_grid", editor_render_grid);
  424. writer.write("render_lighting", editor_render_lighting);
  425. writer.write("selected_snap_grid_size", editor_selected_snap_grid_size);
  426. writer.write("snap_to_grid", editor_snap_to_grid);
  427. writer.write("undo_tracking", editor_undo_tracking);
  428. writer.write("undo_stack_size", editor_undo_stack_size);
  429. writer.write("show_deprecated_tiles", editor_show_deprecated_tiles);
  430. }
  431. writer.end_list("editor");
  432. writer.end_list("supertux-config");
  433. }
  434. bool
  435. Config::is_christmas() const
  436. {
  437. if (christmas_mode)
  438. return true;
  439. std::time_t time = std::time(nullptr);
  440. const std::tm* now = std::localtime(&time);
  441. /* Activate Christmas mode from Dec 6th until Dec 31st. */
  442. return now->tm_mday >= 6 && now->tm_mon == 11;
  443. }
  444. /* EOF */