2D Run-and-gun shooter inspired by One Man's Doomsday, Counter-Strike, and Metal Slug.
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.
 
 
 
 
 
 

325 lines
9.8 KiB

  1. #include "IZ_joystick.h"
  2. static INI_ConfigItem joystick_config_items[IZ_PLAYERS * (IZ_CONTROLS - 4 + 3) + 1];
  3. bool IZ_JoystickIsValidAxisThreshold(u16 value) {
  4. return (4000 <= value && value <= 12000);
  5. }
  6. void IZ_JoystickHandleDeviceEvents(IZ_JoystickState* state, SDL_Event e) {
  7. if (e.type == SDL_JOYDEVICEADDED) {
  8. u8 joysticks_count = SDL_NumJoysticks();
  9. if (joysticks_count <= IZ_PLAYERS && !state->device) {
  10. state->device = SDL_JoystickOpen(e.jdevice.which);
  11. }
  12. return;
  13. }
  14. if (e.type == SDL_JOYDEVICEREMOVED) {
  15. if (
  16. state->device
  17. && SDL_JoystickInstanceID(state->device) == e.jdevice.which
  18. ) {
  19. state->device = NULL;
  20. }
  21. }
  22. }
  23. void IZ_JoystickHandleAxisEvents(IZ_JoystickState* state, IZ_Action* action, SDL_Event e) {
  24. if (e.type == SDL_JOYAXISMOTION) {
  25. // XInput handling
  26. u8 control_index;
  27. for (control_index = 4; control_index < IZ_CONTROLS; control_index += 1) {
  28. if (e.jaxis.axis == IZ_JOY_AXIS_DIRECTION_LEFT_SHOULDER && state->config.control_mapping[control_index] == 10) {
  29. const u16 bitflag = (0x1 << control_index);
  30. if (e.jaxis.value > state->config.axis_threshold) {
  31. *action |= bitflag;
  32. return;
  33. }
  34. if (e.jaxis.value <= state->config.axis_threshold) {
  35. *action &= ~bitflag;
  36. return;
  37. }
  38. }
  39. if (e.jaxis.axis == IZ_JOY_AXIS_DIRECTION_RIGHT_SHOULDER && state->config.control_mapping[control_index] == 11) {
  40. const u16 bitflag = (0x1 << control_index);
  41. if (e.jaxis.value > state->config.axis_threshold) {
  42. *action |= bitflag;
  43. return;
  44. }
  45. if (e.jaxis.value <= state->config.axis_threshold) {
  46. *action &= ~bitflag;
  47. return;
  48. }
  49. }
  50. }
  51. if (
  52. e.jaxis.axis == IZ_JOY_AXIS_DIRECTION_HORIZONTAL1
  53. || e.jaxis.axis == IZ_JOY_AXIS_DIRECTION_HORIZONTAL2
  54. ) {
  55. *action &= ~(0x1 << IZ_ACTION_INDEX_RIGHT);
  56. *action &= ~(0x1 << IZ_ACTION_INDEX_LEFT);
  57. if (e.jaxis.value > state->config.axis_threshold) {
  58. *action |= (0x1 << IZ_ACTION_INDEX_RIGHT);
  59. return;
  60. }
  61. if (e.jaxis.value <= -state->config.axis_threshold) {
  62. *action |= (0x1 << IZ_ACTION_INDEX_LEFT);
  63. }
  64. return;
  65. }
  66. if (
  67. e.jaxis.axis == IZ_JOY_AXIS_DIRECTION_VERTICAL1
  68. || e.jaxis.axis == IZ_JOY_AXIS_DIRECTION_VERTICAL2
  69. ) {
  70. *action &= ~(0x1 << IZ_ACTION_INDEX_UP);
  71. *action &= ~(0x1 << IZ_ACTION_INDEX_DOWN);
  72. if (e.jaxis.value > state->config.axis_threshold) {
  73. *action |= (0x1 << IZ_ACTION_INDEX_DOWN);
  74. return;
  75. }
  76. if (e.jaxis.value <= -state->config.axis_threshold) {
  77. *action |= (0x1 << IZ_ACTION_INDEX_UP);
  78. }
  79. }
  80. }
  81. }
  82. void IZ_JoystickHandleHatEvents(IZ_Action* action, SDL_Event e) {
  83. if (e.type == SDL_JOYHATMOTION) {
  84. *action &= ~(0x1 << IZ_ACTION_INDEX_UP);
  85. *action &= ~(0x1 << IZ_ACTION_INDEX_RIGHT);
  86. *action &= ~(0x1 << IZ_ACTION_INDEX_DOWN);
  87. *action &= ~(0x1 << IZ_ACTION_INDEX_LEFT);
  88. if (e.jhat.value != 0) {
  89. *action |= e.jhat.value;
  90. }
  91. }
  92. }
  93. void IZ_JoystickHandleButtonEvents(IZ_JoystickState* state, IZ_Action* action, SDL_Event e) {
  94. u8 control_index;
  95. SDL_JoystickGUID joystick_guid = SDL_JoystickGetGUID(SDL_JoystickFromInstanceID(e.jbutton.which));
  96. bool is_valid_event = false;
  97. for (u8 zz = 0; zz < 16; zz += 1) {
  98. is_valid_event |= joystick_guid.data[zz] != 0;
  99. }
  100. if (!is_valid_event) {
  101. return;
  102. }
  103. // printf("button event from guid: ");
  104. // for (u8 zz = 0; zz < 16; zz += 1) {
  105. // printf("%02x", joystick_guid.data[zz]);
  106. // }
  107. // printf("\n");
  108. for (control_index = 4; control_index < IZ_CONTROLS; control_index += 1) {
  109. u8 normalized_button = e.jbutton.button;
  110. /*
  111. * ZL ZR
  112. * L R
  113. *
  114. * U SLCT STRT 3
  115. * L R 2 0
  116. * D 1
  117. *
  118. *
  119. *
  120. */
  121. // TODO test with XInput, make compatible with Apple
  122. // TODO honor mapping with controller
  123. #ifdef IZ_MACOS
  124. //printf("%d\n", e.jbutton.button);
  125. // if (e.jbutton.button == 2) {
  126. // normalized_button = 4;
  127. // } else if (e.jbutton.button == 6) {
  128. // normalized_button = 11;
  129. // } else if (e.jbutton.button == 4) {
  130. // normalized_button = 10;
  131. // }
  132. #endif
  133. if (normalized_button == state->config.control_mapping[control_index]) {
  134. const u16 bitflag = (0x1 << control_index);
  135. if (e.type == SDL_JOYBUTTONDOWN) {
  136. *action |= bitflag;
  137. return;
  138. }
  139. if (e.type == SDL_JOYBUTTONUP) {
  140. *action &= ~bitflag;
  141. return;
  142. }
  143. }
  144. }
  145. }
  146. void IZ_JoystickHandleEvents(IZ_JoystickState(* state)[IZ_PLAYERS], IZ_Action(* action)[IZ_PLAYERS], SDL_Event e) {
  147. u8 player_index;
  148. for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) {
  149. IZ_JoystickHandleDeviceEvents(&(*state)[player_index], e);
  150. IZ_JoystickHandleAxisEvents(&(*state)[player_index], &(*action)[player_index], e);
  151. IZ_JoystickHandleHatEvents(&(*action)[player_index], e);
  152. IZ_JoystickHandleButtonEvents(&(*state)[player_index], &(*action)[player_index], e);
  153. }
  154. }
  155. void IZ_JoystickBindStateToConfig(IZ_JoystickState(* state)[IZ_PLAYERS], INI_ConfigItem config_items[]) {
  156. u8 player_index;
  157. u8 control_index;
  158. for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) {
  159. u8 base_index = (player_index * (IZ_CONTROLS - 4 + 2));
  160. config_items[base_index].dest = &((*state)[player_index].config.device_id);
  161. config_items[base_index + 1].dest = &((*state)[player_index].config.axis_threshold);
  162. config_items[base_index + 2].dest = &((*state)[player_index].config.guid);
  163. for (control_index = 4; control_index < IZ_CONTROLS; control_index += 1) {
  164. config_items[base_index + 3 + (control_index - 4)].dest = &((*state)[player_index].config.control_mapping[control_index]);
  165. }
  166. }
  167. }
  168. IZ_ProcedureResult IZ_JoystickSaveConfig(IZ_JoystickState(* state)[IZ_PLAYERS], const char* config_path) {
  169. IZ_JoystickBindStateToConfig(state, joystick_config_items);
  170. return INI_ConfigSave(joystick_config_items, config_path);
  171. }
  172. void IZ_JoystickInitializeConfigItems(INI_ConfigItem config_items[]) {
  173. u8 player_index;
  174. u8 control_index;
  175. char* main_section_name;
  176. char* control_mapping_section_name;
  177. for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) {
  178. main_section_name = IZ_calloc(64, sizeof(char));
  179. sprintf(main_section_name, "Joystick.%d", player_index);
  180. // The `+ 3` corresponds to the first three config items. Add as appropriate.
  181. u8 base_index = (player_index * (IZ_CONTROLS - 4 + 3));
  182. config_items[base_index] = (INI_ConfigItem) {
  183. INI_CONFIG_TYPE_I32,
  184. main_section_name,
  185. "DeviceID",
  186. NULL,
  187. &IZ_JOYSTICK_DEFAULT_STATE[player_index].config.device_id,
  188. NULL,
  189. INI_CONFIG_TRANSFORMER_NONE,
  190. NULL,
  191. };
  192. config_items[base_index + 1] = (INI_ConfigItem) {
  193. INI_CONFIG_TYPE_U16,
  194. main_section_name,
  195. "AxisThreshold",
  196. NULL,
  197. &IZ_JOYSTICK_DEFAULT_STATE[player_index].config.axis_threshold,
  198. IZ_JoystickIsValidAxisThreshold,
  199. INI_CONFIG_TRANSFORMER_NONE,
  200. NULL,
  201. };
  202. config_items[base_index + 2] = (INI_ConfigItem) {
  203. INI_CONFIG_TYPE_GUID,
  204. main_section_name,
  205. "GUID",
  206. NULL,
  207. &IZ_JOYSTICK_DEFAULT_STATE[player_index].config.guid,
  208. NULL,
  209. INI_CONFIG_TRANSFORMER_NONE,
  210. NULL,
  211. };
  212. // todo add game controller GUID for determining mappings
  213. control_mapping_section_name = IZ_calloc(64, sizeof(char));
  214. sprintf(control_mapping_section_name, "Joystick.%d.ControlMapping", player_index);
  215. for (control_index = 4; control_index < IZ_CONTROLS; control_index += 1) {
  216. config_items[base_index + 3 + (control_index - 4)] = (INI_ConfigItem) {
  217. INI_CONFIG_TYPE_U8,
  218. control_mapping_section_name,
  219. IZ_ACTION_NAMES[control_index],
  220. NULL,
  221. &IZ_JOYSTICK_DEFAULT_STATE[player_index].config.control_mapping[control_index],
  222. NULL,
  223. INI_CONFIG_TRANSFORMER_NONE,
  224. NULL,
  225. };
  226. }
  227. }
  228. config_items[IZ_PLAYERS * (IZ_CONTROLS - 4 + 3)] = INI_CONFIG_ITEM_NULL;
  229. }
  230. IZ_ProcedureResult IZ_JoystickInitializeConfig(IZ_JoystickState(* state)[IZ_PLAYERS], const char* config_path, u8 argc, const char* argv[]) {
  231. IZ_JoystickInitializeConfigItems(joystick_config_items);
  232. IZ_JoystickBindStateToConfig(state, joystick_config_items);
  233. if (INI_ConfigInitialize(joystick_config_items, config_path, argc, argv) < 0) {
  234. return -1;
  235. }
  236. return 0;
  237. }
  238. IZ_ProcedureResult IZ_JoystickInitialize(IZ_JoystickState(* state)[IZ_PLAYERS], const char* config_path, u8 argc, const char* argv[]) {
  239. IZ_memcpy(state, sizeof(IZ_JoystickState) * IZ_PLAYERS, &IZ_JOYSTICK_DEFAULT_STATE, sizeof(IZ_JoystickState) * IZ_PLAYERS);
  240. // TODO query this file from the internet?
  241. SDL_GameControllerAddMappingsFromFile("assets/gamecontrollerdb.txt");
  242. u8 player_index;
  243. if (IZ_JoystickInitializeConfig(state, config_path, argc, argv) < 0) {
  244. return -2;
  245. }
  246. u8 joysticks_count = SDL_NumJoysticks();
  247. for (player_index = 0; player_index < joysticks_count; player_index += 1) {
  248. if (player_index >= IZ_PLAYERS) {
  249. break;
  250. }
  251. (*state)[player_index].device = SDL_JoystickOpen(state[player_index]->config.device_id);
  252. if (!(*state)[player_index].device) {
  253. break;
  254. }
  255. (*state)[player_index].config.device_id = SDL_JoystickInstanceID((*state)[player_index].device);
  256. SDL_GUID joystick_guid = SDL_JoystickGetGUID((*state)[player_index].device);
  257. IZ_memcpy(&(*state)[player_index].config.guid, sizeof(SDL_GUID), &joystick_guid, sizeof(SDL_GUID));
  258. //(*state)[player_index].config.guid = joystick_guid;
  259. // printf("[INPUT:JOYSTICK] Initialize event from GUID: ");
  260. // for (u8 zz = 0; zz < 16; zz += 1) {
  261. // printf("%02x", (*state)[player_index].config.guid.data[zz]);
  262. // }
  263. // printf("\n");
  264. }
  265. // Post config (after joystick GUIDs have been queried), this is unique to joysticks since they can be plugged in any
  266. // time.
  267. INI_ConfigSaveResult post_config_save_result = IZ_JoystickSaveConfig(state, config_path);
  268. if (post_config_save_result < 0) {
  269. return -3;
  270. }
  271. return 0;
  272. }
  273. void IZ_JoystickTeardown(IZ_JoystickState(* state)[IZ_PLAYERS]) {
  274. u8 player_index;
  275. for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) {
  276. if (!(*state)[player_index].device) {
  277. continue;
  278. }
  279. SDL_JoystickClose((*state)[player_index].device);
  280. }
  281. }