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

189 lines
7.0 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 "controls.hpp"
  20. #include "resources/resource-manager.hpp"
  21. #include <fstream>
  22. namespace game {
  23. std::string gamepad_calibration_path(const game::context* ctx, const input::gamepad* gamepad)
  24. {
  25. return "gamepad-" + gamepad->get_guid() + ".json";
  26. }
  27. json default_gamepad_calibration()
  28. {
  29. const float activation_min = 0.15f;
  30. const float activation_max = 0.98f;
  31. const bool deadzone_cross = false;
  32. const float deadzone_roundness = 1.0f;
  33. const std::string response_curve = "linear";
  34. json calibration;
  35. calibration["leftx_activation"] = {activation_min, activation_max};
  36. calibration["lefty_activation"] = {activation_min, activation_max};
  37. calibration["rightx_activation"] = {activation_min, activation_max};
  38. calibration["righty_activation"] = {activation_min, activation_max};
  39. calibration["lefttrigger_activation"] = {activation_min, activation_max};
  40. calibration["righttrigger_activation"] = {activation_min, activation_max};
  41. calibration["leftx_response_curve"] = response_curve;
  42. calibration["lefty_response_curve"] = response_curve;
  43. calibration["rightx_response_curve"] = response_curve;
  44. calibration["righty_response_curve"] = response_curve;
  45. calibration["lefttrigger_response_curve"] = response_curve;
  46. calibration["righttrigger_response_curve"] = response_curve;
  47. calibration["left_deadzone_cross"] = deadzone_cross;
  48. calibration["right_deadzone_cross"] = deadzone_cross;
  49. calibration["left_deadzone_roundness"] = deadzone_roundness;
  50. calibration["right_deadzone_roundness"] = deadzone_roundness;
  51. return calibration;
  52. }
  53. json* load_gamepad_calibration(game::context* ctx, input::gamepad* gamepad)
  54. {
  55. // Determine path to gamepad calibration file
  56. std::string filepath = gamepad_calibration_path(ctx, gamepad);
  57. // Load gamepad calibration file
  58. json* calibration = ctx->resource_manager->load<json>(filepath);
  59. return calibration;
  60. }
  61. bool save_gamepad_calibration(const game::context* ctx, const input::gamepad* gamepad, const json& calibration)
  62. {
  63. // Determine path to gamepad calibration file
  64. std::string filepath = ctx->controls_path + gamepad_calibration_path(ctx, gamepad);
  65. // Open calibration file
  66. std::ofstream stream;
  67. stream.open(filepath);
  68. if (!stream)
  69. return false;
  70. // Write calibration to file
  71. stream << calibration.dump(1, '\t');
  72. if (stream.bad())
  73. {
  74. stream.close();
  75. return false;
  76. }
  77. // Close calibration file
  78. stream.close();
  79. return true;
  80. }
  81. void apply_gamepad_calibration(input::gamepad* gamepad, const json& calibration)
  82. {
  83. // Parse and apply activation thresholds
  84. if (calibration.contains("leftx_activation"))
  85. {
  86. float min = calibration["leftx_activation"][0].get<float>();
  87. float max = calibration["leftx_activation"][1].get<float>();
  88. gamepad->set_activation_threshold(input::gamepad_axis::left_x, min, max);
  89. }
  90. if (calibration.contains("lefty_activation"))
  91. {
  92. float min = calibration["lefty_activation"][0].get<float>();
  93. float max = calibration["lefty_activation"][1].get<float>();
  94. gamepad->set_activation_threshold(input::gamepad_axis::left_y, min, max);
  95. }
  96. if (calibration.contains("rightx_activation"))
  97. {
  98. float min = calibration["rightx_activation"][0].get<float>();
  99. float max = calibration["rightx_activation"][1].get<float>();
  100. gamepad->set_activation_threshold(input::gamepad_axis::right_x, min, max);
  101. }
  102. if (calibration.contains("righty_activation"))
  103. {
  104. float min = calibration["righty_activation"][0].get<float>();
  105. float max = calibration["righty_activation"][1].get<float>();
  106. gamepad->set_activation_threshold(input::gamepad_axis::right_y, min, max);
  107. }
  108. if (calibration.contains("lefttrigger_activation"))
  109. {
  110. float min = calibration["lefttrigger_activation"][0].get<float>();
  111. float max = calibration["lefttrigger_activation"][1].get<float>();
  112. gamepad->set_activation_threshold(input::gamepad_axis::left_trigger, min, max);
  113. }
  114. if (calibration.contains("righttrigger_activation"))
  115. {
  116. float min = calibration["righttrigger_activation"][0].get<float>();
  117. float max = calibration["righttrigger_activation"][1].get<float>();
  118. gamepad->set_activation_threshold(input::gamepad_axis::right_trigger, min, max);
  119. }
  120. // Parse and apply deadzone shapes
  121. if (calibration.contains("left_deadzone_cross"))
  122. gamepad->set_left_deadzone_cross(calibration["left_deadzone_cross"].get<bool>());
  123. if (calibration.contains("right_deadzone_cross"))
  124. gamepad->set_right_deadzone_cross(calibration["right_deadzone_cross"].get<bool>());
  125. if (calibration.contains("left_deadzone_roundness"))
  126. gamepad->set_left_deadzone_roundness(calibration["left_deadzone_roundness"].get<float>());
  127. if (calibration.contains("right_deadzone_roundness"))
  128. gamepad->set_right_deadzone_roundness(calibration["right_deadzone_roundness"].get<float>());
  129. auto parse_response_curve = [](const std::string& curve) -> input::gamepad_response_curve
  130. {
  131. if (curve == "square")
  132. return input::gamepad_response_curve::square;
  133. else if (curve == "cube")
  134. return input::gamepad_response_curve::cube;
  135. return input::gamepad_response_curve::linear;
  136. };
  137. // Parse and apply axis response curves
  138. if (calibration.contains("leftx_response_curve"))
  139. {
  140. auto curve = parse_response_curve(calibration["leftx_response_curve"].get<std::string>());
  141. gamepad->set_response_curve(input::gamepad_axis::left_x, curve);
  142. }
  143. if (calibration.contains("lefty_response_curve"))
  144. {
  145. auto curve = parse_response_curve(calibration["lefty_response_curve"].get<std::string>());
  146. gamepad->set_response_curve(input::gamepad_axis::left_y, curve);
  147. }
  148. if (calibration.contains("rightx_response_curve"))
  149. {
  150. auto curve = parse_response_curve(calibration["rightx_response_curve"].get<std::string>());
  151. gamepad->set_response_curve(input::gamepad_axis::right_x, curve);
  152. }
  153. if (calibration.contains("righty_response_curve"))
  154. {
  155. auto curve = parse_response_curve(calibration["righty_response_curve"].get<std::string>());
  156. gamepad->set_response_curve(input::gamepad_axis::right_y, curve);
  157. }
  158. if (calibration.contains("lefttrigger_response_curve"))
  159. {
  160. auto curve = parse_response_curve(calibration["lefttrigger_response_curve"].get<std::string>());
  161. gamepad->set_response_curve(input::gamepad_axis::left_trigger, curve);
  162. }
  163. if (calibration.contains("righttrigger_response_curve"))
  164. {
  165. auto curve = parse_response_curve(calibration["righttrigger_response_curve"].get<std::string>());
  166. gamepad->set_response_curve(input::gamepad_axis::right_trigger, curve);
  167. }
  168. }
  169. } // namespace game