laser_tag_app.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. #include "laser_tag_app.h"
  2. #include "laser_tag_view.h"
  3. #include "infrared_controller.h"
  4. #include "game_state.h"
  5. #include <furi.h>
  6. #include <gui/gui.h>
  7. #include <input/input.h>
  8. #include <notification/notification.h>
  9. #define TAG "LaserTagApp"
  10. struct LaserTagApp {
  11. Gui* gui;
  12. ViewPort* view_port;
  13. LaserTagView* view;
  14. FuriMessageQueue* event_queue;
  15. FuriTimer* timer;
  16. NotificationApp* notifications;
  17. InfraredController* ir_controller;
  18. GameState* game_state;
  19. LaserTagState state;
  20. bool need_redraw;
  21. };
  22. const NotificationSequence sequence_vibro_1 = {&message_vibro_on, &message_vibro_off, NULL};
  23. static void laser_tag_app_timer_callback(void* context) {
  24. furi_assert(context);
  25. LaserTagApp* app = context;
  26. FURI_LOG_D(TAG, "Timer callback triggered");
  27. if(app->game_state) {
  28. game_state_update_time(app->game_state, 1);
  29. FURI_LOG_D(TAG, "Updated game time by 1");
  30. if(app->view) {
  31. FURI_LOG_D(TAG, "Updating view with the latest game state");
  32. laser_tag_view_update(app->view, app->game_state);
  33. app->need_redraw = true;
  34. }
  35. }
  36. }
  37. static void laser_tag_app_input_callback(InputEvent* input_event, void* context) {
  38. furi_assert(context);
  39. LaserTagApp* app = context;
  40. FURI_LOG_D(
  41. TAG, "Input callback triggered: type=%d, key=%d", input_event->type, input_event->key);
  42. furi_message_queue_put(app->event_queue, input_event, 0);
  43. FURI_LOG_D(TAG, "Input event queued successfully");
  44. }
  45. static void laser_tag_app_draw_callback(Canvas* canvas, void* context) {
  46. furi_assert(context);
  47. LaserTagApp* app = context;
  48. FURI_LOG_D(TAG, "Entering draw callback");
  49. if(app->state == LaserTagStateTeamSelect) {
  50. FURI_LOG_D(TAG, "Drawing team selection screen");
  51. canvas_clear(canvas);
  52. canvas_set_font(canvas, FontPrimary);
  53. canvas_draw_str(canvas, 32, 32, "Select Team:");
  54. canvas_set_font(canvas, FontSecondary);
  55. canvas_draw_str(canvas, 32, 48, "LEFT: Red RIGHT: Blue");
  56. } else if(app->view) {
  57. FURI_LOG_D(TAG, "Drawing game view");
  58. laser_tag_view_draw(laser_tag_view_get_view(app->view), canvas);
  59. }
  60. FURI_LOG_D(TAG, "Exiting draw callback");
  61. }
  62. LaserTagApp* laser_tag_app_alloc() {
  63. FURI_LOG_D(TAG, "Allocating Laser Tag App");
  64. LaserTagApp* app = malloc(sizeof(LaserTagApp));
  65. if(!app) {
  66. FURI_LOG_E(TAG, "Failed to allocate LaserTagApp");
  67. return NULL;
  68. }
  69. FURI_LOG_D(TAG, "LaserTagApp struct allocated");
  70. memset(app, 0, sizeof(LaserTagApp));
  71. app->gui = furi_record_open(RECORD_GUI);
  72. app->view_port = view_port_alloc();
  73. app->view = laser_tag_view_alloc();
  74. app->notifications = furi_record_open(RECORD_NOTIFICATION);
  75. app->game_state = game_state_alloc();
  76. app->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
  77. if(!app->gui || !app->view_port || !app->view || !app->notifications || !app->game_state ||
  78. !app->event_queue) {
  79. FURI_LOG_E(TAG, "Failed to allocate resources");
  80. laser_tag_app_free(app);
  81. return NULL;
  82. }
  83. app->state = LaserTagStateTeamSelect;
  84. app->need_redraw = true;
  85. FURI_LOG_D(TAG, "Initial state set");
  86. view_port_draw_callback_set(app->view_port, laser_tag_app_draw_callback, app);
  87. view_port_input_callback_set(app->view_port, laser_tag_app_input_callback, app);
  88. gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);
  89. FURI_LOG_D(TAG, "ViewPort callbacks set and added to GUI");
  90. app->timer = furi_timer_alloc(laser_tag_app_timer_callback, FuriTimerTypePeriodic, app);
  91. if(!app->timer) {
  92. FURI_LOG_E(TAG, "Failed to allocate timer");
  93. laser_tag_app_free(app);
  94. return NULL;
  95. }
  96. FURI_LOG_D(TAG, "Timer allocated");
  97. furi_timer_start(app->timer, furi_kernel_get_tick_frequency());
  98. FURI_LOG_D(TAG, "Timer started");
  99. FURI_LOG_D(TAG, "Laser Tag App allocated successfully");
  100. return app;
  101. }
  102. void laser_tag_app_free(LaserTagApp* app) {
  103. FURI_LOG_D(TAG, "Freeing Laser Tag App");
  104. furi_assert(app);
  105. furi_timer_free(app->timer);
  106. view_port_enabled_set(app->view_port, false);
  107. gui_remove_view_port(app->gui, app->view_port);
  108. view_port_free(app->view_port);
  109. laser_tag_view_free(app->view);
  110. furi_message_queue_free(app->event_queue);
  111. if(app->ir_controller) {
  112. infrared_controller_free(app->ir_controller);
  113. }
  114. free(app->game_state);
  115. furi_record_close(RECORD_GUI);
  116. furi_record_close(RECORD_NOTIFICATION);
  117. free(app);
  118. FURI_LOG_D(TAG, "Laser Tag App freed");
  119. }
  120. void laser_tag_app_fire(LaserTagApp* app) {
  121. furi_assert(app);
  122. FURI_LOG_D(TAG, "Firing laser");
  123. if(!app->ir_controller) {
  124. FURI_LOG_E(TAG, "IR controller is NULL in laser_tag_app_fire");
  125. return;
  126. }
  127. FURI_LOG_D(TAG, "Sending infrared signal");
  128. infrared_controller_send(app->ir_controller);
  129. FURI_LOG_D(TAG, "Decreasing ammo by 1");
  130. game_state_decrease_ammo(app->game_state, 1);
  131. FURI_LOG_D(TAG, "Notifying with blink blue");
  132. notification_message(app->notifications, &sequence_blink_blue_100);
  133. app->need_redraw = true;
  134. }
  135. void laser_tag_app_handle_hit(LaserTagApp* app) {
  136. furi_assert(app);
  137. FURI_LOG_D(TAG, "Handling hit");
  138. FURI_LOG_D(TAG, "Decreasing health by 10");
  139. game_state_decrease_health(app->game_state, 10);
  140. FURI_LOG_D(TAG, "Notifying with vibration");
  141. notification_message(app->notifications, &sequence_vibro_1);
  142. app->need_redraw = true;
  143. }
  144. static bool laser_tag_app_enter_game_state(LaserTagApp* app) {
  145. furi_assert(app);
  146. FURI_LOG_D(TAG, "Entering game state");
  147. app->state = LaserTagStateGame;
  148. FURI_LOG_D(TAG, "Resetting game state");
  149. game_state_reset(app->game_state);
  150. FURI_LOG_D(TAG, "Updating view with new game state");
  151. laser_tag_view_update(app->view, app->game_state);
  152. FURI_LOG_D(TAG, "Allocating IR controller");
  153. app->ir_controller = infrared_controller_alloc();
  154. if(!app->ir_controller) {
  155. FURI_LOG_E(TAG, "Failed to allocate IR controller");
  156. return false;
  157. }
  158. FURI_LOG_D(TAG, "IR controller allocated");
  159. FURI_LOG_D(TAG, "Setting IR controller team");
  160. infrared_controller_set_team(app->ir_controller, game_state_get_team(app->game_state));
  161. app->need_redraw = true;
  162. return true;
  163. }
  164. int32_t laser_tag_app(void* p) {
  165. UNUSED(p);
  166. FURI_LOG_I(TAG, "Laser Tag app starting");
  167. LaserTagApp* app = laser_tag_app_alloc();
  168. if(!app) {
  169. FURI_LOG_E(TAG, "Failed to allocate application");
  170. return -1;
  171. }
  172. FURI_LOG_D(TAG, "LaserTagApp allocated successfully");
  173. FURI_LOG_D(TAG, "Entering main loop");
  174. InputEvent event;
  175. bool running = true;
  176. while(running) {
  177. FURI_LOG_D(TAG, "Start of main loop iteration");
  178. FuriStatus status = furi_message_queue_get(app->event_queue, &event, 100);
  179. if(status == FuriStatusOk) {
  180. FURI_LOG_D(TAG, "Received input event: type=%d, key=%d", event.type, event.key);
  181. if(event.type == InputTypePress || event.type == InputTypeRepeat) {
  182. FURI_LOG_D(TAG, "Processing input event");
  183. if(app->state == LaserTagStateTeamSelect) {
  184. switch(event.key) {
  185. case InputKeyLeft:
  186. FURI_LOG_D(TAG, "Selected Red Team");
  187. game_state_set_team(app->game_state, TeamRed);
  188. if(!laser_tag_app_enter_game_state(app)) {
  189. running = false;
  190. }
  191. break;
  192. case InputKeyRight:
  193. FURI_LOG_D(TAG, "Selected Blue Team");
  194. game_state_set_team(app->game_state, TeamBlue);
  195. if(!laser_tag_app_enter_game_state(app)) {
  196. running = false;
  197. }
  198. break;
  199. default:
  200. break;
  201. }
  202. } else {
  203. switch(event.key) {
  204. case InputKeyBack:
  205. FURI_LOG_D(TAG, "Exiting game");
  206. running = false;
  207. break;
  208. case InputKeyOk:
  209. FURI_LOG_D(TAG, "Firing laser");
  210. laser_tag_app_fire(app);
  211. break;
  212. default:
  213. break;
  214. }
  215. }
  216. }
  217. } else if(status == FuriStatusErrorTimeout) {
  218. FURI_LOG_D(TAG, "No input event, continuing");
  219. } else {
  220. FURI_LOG_E(TAG, "Failed to get input event, status: %d", status);
  221. }
  222. if(app->state == LaserTagStateGame && app->ir_controller) {
  223. FURI_LOG_D(TAG, "Game is active");
  224. if(infrared_controller_receive(app->ir_controller)) {
  225. FURI_LOG_D(TAG, "Hit received");
  226. laser_tag_app_handle_hit(app);
  227. }
  228. if(game_state_is_game_over(app->game_state)) {
  229. FURI_LOG_D(TAG, "Game over");
  230. notification_message(app->notifications, &sequence_error);
  231. running = false;
  232. }
  233. }
  234. if(app->need_redraw) {
  235. FURI_LOG_D(TAG, "Updating view port");
  236. view_port_update(app->view_port);
  237. app->need_redraw = false;
  238. }
  239. FURI_LOG_D(TAG, "End of main loop iteration");
  240. furi_delay_ms(10);
  241. }
  242. FURI_LOG_I(TAG, "Laser Tag app exiting");
  243. laser_tag_app_free(app);
  244. return 0;
  245. }