flappy_bird.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. #include <stdlib.h>
  2. #include <flappy_bird_icons.h>
  3. #include <furi.h>
  4. #include <gui/gui.h>
  5. #include <input/input.h>
  6. #include <dolphin/dolphin.h>
  7. #include <storage/storage.h>
  8. #include <furi_hal.h>
  9. #define TAG "Flappy"
  10. #define DEBUG false
  11. #define FLAPPY_BIRD_HEIGHT 15
  12. #define FLAPPY_BIRD_WIDTH 10
  13. #define FLAPPY_PILAR_MAX 6
  14. #define FLAPPY_PILAR_DIST 35
  15. #define FLAPPY_GAB_HEIGHT 25
  16. // Increased gap height for Yapper
  17. #define YAPPER_GAB_HEIGHT 35
  18. #define FLAPPY_GAB_WIDTH 10
  19. #define YAPPER_HEIGHT 22
  20. #define YAPPER_WIDTH 16
  21. #define FLAPPY_GRAVITY_JUMP -1.1
  22. #define FLAPPY_GRAVITY_TICK 0.15
  23. #define FLIPPER_LCD_WIDTH 128
  24. #define FLIPPER_LCD_HEIGHT 64
  25. static const char* FLAPPY_SAVE_PATH = APP_DATA_PATH("flappy_high.save");
  26. typedef enum {
  27. BirdState0 = 0,
  28. BirdState1,
  29. BirdState2,
  30. BirdStateMAX
  31. } BirdState;
  32. typedef enum {
  33. BirdTypeDefault = 0,
  34. BirdTypeYapper, // Changed to just have Default and Yapper
  35. BirdTypeMAX
  36. } BirdType;
  37. const Icon* bird_states[BirdStateMAX] = {
  38. &I_bird_01,
  39. &I_bird_02,
  40. &I_bird_03,
  41. };
  42. // Add this structure definition
  43. typedef struct {
  44. int width;
  45. int height;
  46. } CharacterDimensions;
  47. // Add this array definition
  48. static const CharacterDimensions character_dimensions[] = {
  49. {FLAPPY_BIRD_WIDTH, FLAPPY_BIRD_HEIGHT}, // Default bird
  50. {YAPPER_WIDTH, YAPPER_HEIGHT}, // Yapper
  51. };
  52. // Update your bird_sets array
  53. const Icon* bird_sets[BirdTypeMAX][BirdStateMAX] = {
  54. {&I_bird_01, &I_bird_02, &I_bird_03}, // Default bird
  55. {&I_yapper_01, &I_yapper_02, &I_yapper_03}, // Yapper assets
  56. };
  57. typedef enum {
  58. EventTypeTick,
  59. EventTypeKey,
  60. } EventType;
  61. typedef struct {
  62. int x;
  63. int y;
  64. } POINT;
  65. typedef struct {
  66. float gravity;
  67. POINT point;
  68. } BIRD;
  69. typedef struct {
  70. POINT point;
  71. int height;
  72. int visible;
  73. bool passed;
  74. } PILAR;
  75. typedef enum {
  76. GameStateStart, // New state for start screen
  77. GameStateLife,
  78. GameStateGameOver,
  79. } State;
  80. typedef struct {
  81. BIRD bird;
  82. int points;
  83. int high_score;
  84. int pilars_count;
  85. PILAR pilars[FLAPPY_PILAR_MAX];
  86. bool debug;
  87. State state;
  88. FuriMutex* mutex;
  89. uint8_t collision_frame;
  90. BirdType selected_bird; // New field
  91. bool in_bird_select; // New field for menu state
  92. } GameState;
  93. typedef struct {
  94. EventType type;
  95. InputEvent input;
  96. } GameEvent;
  97. typedef enum {
  98. DirectionUp,
  99. DirectionRight,
  100. DirectionDown,
  101. DirectionLeft,
  102. } Direction;
  103. static void flappy_game_save_score(int score) {
  104. Storage* storage = furi_record_open(RECORD_STORAGE);
  105. File* file = storage_file_alloc(storage);
  106. if(storage_file_open(file, FLAPPY_SAVE_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
  107. storage_file_write(file, &score, sizeof(int));
  108. }
  109. storage_file_close(file);
  110. storage_file_free(file);
  111. furi_record_close(RECORD_STORAGE);
  112. }
  113. static int flappy_game_load_score() {
  114. Storage* storage = furi_record_open(RECORD_STORAGE);
  115. File* file = storage_file_alloc(storage);
  116. int score = 0;
  117. if(storage_file_open(file, FLAPPY_SAVE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
  118. storage_file_read(file, &score, sizeof(int));
  119. }
  120. storage_file_close(file);
  121. storage_file_free(file);
  122. furi_record_close(RECORD_STORAGE);
  123. return score;
  124. }
  125. static inline int get_gap_height(BirdType bird_type) {
  126. return (bird_type == BirdTypeYapper) ? YAPPER_GAB_HEIGHT : FLAPPY_GAB_HEIGHT;
  127. }
  128. // Modify the random pilar function to use dynamic gap height
  129. static void flappy_game_random_pilar(GameState* const game_state) {
  130. PILAR pilar;
  131. int gap_height = get_gap_height(game_state->selected_bird);
  132. pilar.passed = false;
  133. pilar.visible = 1;
  134. pilar.height = random() % (FLIPPER_LCD_HEIGHT - gap_height) + 1;
  135. pilar.point.y = 0;
  136. pilar.point.x = FLIPPER_LCD_WIDTH + FLAPPY_GAB_WIDTH + 1;
  137. game_state->pilars_count++;
  138. game_state->pilars[game_state->pilars_count % FLAPPY_PILAR_MAX] = pilar;
  139. }
  140. static void flappy_game_state_init(GameState* const game_state) {
  141. BIRD bird;
  142. bird.gravity = 0.0f;
  143. bird.point.x = 15;
  144. bird.point.y = 32;
  145. game_state->debug = DEBUG;
  146. game_state->bird = bird;
  147. game_state->pilars_count = 0;
  148. game_state->points = 0;
  149. game_state->state = GameStateStart;
  150. game_state->collision_frame = 0;
  151. game_state->in_bird_select = false;
  152. // Keep the selected bird between games
  153. if(game_state->selected_bird >= BirdTypeMAX) {
  154. game_state->selected_bird = BirdTypeDefault;
  155. }
  156. // Only load high score if it's not already loaded
  157. if(game_state->high_score == 0) {
  158. game_state->high_score = flappy_game_load_score();
  159. }
  160. memset(game_state->pilars, 0, sizeof(game_state->pilars));
  161. flappy_game_random_pilar(game_state);
  162. }
  163. static void flappy_game_state_free(GameState* const game_state) {
  164. free(game_state);
  165. }
  166. static bool check_collision(
  167. const GameState* game_state,
  168. const PILAR* pilar,
  169. CharacterDimensions dims,
  170. int gap_height) {
  171. // Create a slightly smaller hitbox for better gameplay feel
  172. int collision_margin = 2;
  173. int effective_width = dims.width - collision_margin;
  174. int effective_height = dims.height - collision_margin;
  175. // For Yapper, adjust the hitbox to be more forgiving
  176. if(game_state->selected_bird == BirdTypeYapper) {
  177. collision_margin = 4; // More forgiving collision for Yapper
  178. effective_width = dims.width - collision_margin;
  179. effective_height = dims.height - collision_margin;
  180. }
  181. // Check horizontal collision
  182. bool horizontally_aligned = (game_state->bird.point.x + effective_height >= pilar->point.x) &&
  183. (game_state->bird.point.x <= pilar->point.x + FLAPPY_GAB_WIDTH);
  184. if(!horizontally_aligned) return false;
  185. // Check vertical collision - upper pipe
  186. if(game_state->bird.point.y < pilar->height + collision_margin / 2) {
  187. return true;
  188. }
  189. // Check vertical collision - lower pipe
  190. if(game_state->bird.point.y + effective_width - collision_margin / 2 >=
  191. pilar->height + gap_height) {
  192. return true;
  193. }
  194. return false;
  195. }
  196. static void flappy_game_tick(GameState* const game_state) {
  197. if(game_state->collision_frame > 0) {
  198. game_state->collision_frame--;
  199. }
  200. if(game_state->state == GameStateLife) {
  201. if(!game_state->debug) {
  202. game_state->bird.gravity += FLAPPY_GRAVITY_TICK;
  203. game_state->bird.point.y += game_state->bird.gravity;
  204. }
  205. // Get current gap height and dimensions
  206. int gap_height = get_gap_height(game_state->selected_bird);
  207. CharacterDimensions dims = character_dimensions[game_state->selected_bird];
  208. // Checking the location of the last respawned pilar
  209. PILAR* pilar = &game_state->pilars[game_state->pilars_count % FLAPPY_PILAR_MAX];
  210. if(pilar->point.x == (FLIPPER_LCD_WIDTH - FLAPPY_PILAR_DIST))
  211. flappy_game_random_pilar(game_state);
  212. for(int i = 0; i < FLAPPY_PILAR_MAX; i++) {
  213. PILAR* pilar = &game_state->pilars[i];
  214. if(pilar != NULL && pilar->visible && game_state->state == GameStateLife) {
  215. pilar->point.x--;
  216. if(game_state->bird.point.x >= pilar->point.x + FLAPPY_GAB_WIDTH &&
  217. pilar->passed == false) {
  218. pilar->passed = true;
  219. game_state->points++;
  220. }
  221. if(pilar->point.x < -FLAPPY_GAB_WIDTH) pilar->visible = 0;
  222. // Updated bounds checking
  223. if(game_state->bird.point.y <= 0) {
  224. game_state->bird.point.y = 0;
  225. game_state->bird.gravity = 0;
  226. }
  227. if(game_state->bird.point.y > FLIPPER_LCD_HEIGHT - dims.width) {
  228. game_state->bird.point.y = FLIPPER_LCD_HEIGHT - dims.width;
  229. game_state->state = GameStateGameOver;
  230. game_state->collision_frame = 4;
  231. break;
  232. }
  233. // Check for collision using new collision detection function
  234. if(check_collision(game_state, pilar, dims, gap_height)) {
  235. game_state->state = GameStateGameOver;
  236. game_state->collision_frame = 4;
  237. if(game_state->points > game_state->high_score) {
  238. game_state->high_score = game_state->points;
  239. flappy_game_save_score(game_state->high_score);
  240. }
  241. break;
  242. }
  243. }
  244. }
  245. }
  246. }
  247. static void flappy_game_flap(GameState* const game_state) {
  248. game_state->bird.gravity = FLAPPY_GRAVITY_JUMP;
  249. }
  250. static void flappy_game_render_callback(Canvas* const canvas, void* ctx) {
  251. furi_assert(ctx);
  252. const GameState* game_state = ctx;
  253. furi_mutex_acquire(game_state->mutex, FuriWaitForever);
  254. canvas_draw_frame(canvas, 0, 0, 128, 64);
  255. if(game_state->state == GameStateStart) {
  256. if(!game_state->in_bird_select) {
  257. // Main menu - original size
  258. canvas_set_color(canvas, ColorWhite);
  259. canvas_draw_box(canvas, 22, 8, 86, 48);
  260. canvas_set_color(canvas, ColorBlack);
  261. canvas_draw_frame(canvas, 22, 8, 86, 48);
  262. canvas_set_font(canvas, FontPrimary);
  263. // Change title based on selected character
  264. if(game_state->selected_bird == BirdTypeYapper) {
  265. canvas_draw_str_aligned(canvas, 64, 20, AlignCenter, AlignBottom, "Yappy Bird");
  266. } else {
  267. canvas_draw_str_aligned(canvas, 64, 20, AlignCenter, AlignBottom, "Flappy Bird");
  268. }
  269. canvas_set_font(canvas, FontSecondary);
  270. canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignBottom, "Press OK to start");
  271. canvas_draw_str_aligned(canvas, 64, 42, AlignCenter, AlignBottom, "UP to select char");
  272. if(game_state->high_score > 0) {
  273. char hi_buffer[24];
  274. snprintf(hi_buffer, sizeof(hi_buffer), "Best: %d", game_state->high_score);
  275. canvas_draw_str_aligned(canvas, 64, 52, AlignCenter, AlignBottom, hi_buffer);
  276. }
  277. } else {
  278. // Character selection menu with larger box
  279. canvas_set_color(canvas, ColorWhite);
  280. canvas_draw_box(canvas, 16, 4, 96, 56); // Much bigger box
  281. canvas_set_color(canvas, ColorBlack);
  282. canvas_draw_frame(canvas, 16, 4, 96, 56);
  283. // Title more space from top
  284. canvas_set_font(canvas, FontPrimary);
  285. canvas_draw_str_aligned(canvas, 64, 14, AlignCenter, AlignBottom, "Select Character");
  286. // Get current character dimensions
  287. CharacterDimensions dims = character_dimensions[game_state->selected_bird];
  288. // Centered position for preview with more vertical space
  289. int preview_x = 64 - (dims.width / 2);
  290. int preview_y = 32 - (dims.height / 2); // Center vertically
  291. // Draw character preview
  292. canvas_draw_icon(
  293. canvas, preview_x, preview_y, bird_sets[game_state->selected_bird][BirdState1]);
  294. // Draw selection arrows with more spacing
  295. canvas_draw_str_aligned(canvas, 26, 34, AlignCenter, AlignBottom, "<");
  296. canvas_draw_str_aligned(canvas, 102, 34, AlignCenter, AlignBottom, ">");
  297. canvas_set_font(canvas, FontSecondary);
  298. // Instructions pushed lower with more spacing
  299. canvas_draw_str_aligned(canvas, 64, 48, AlignCenter, AlignBottom, "</> to choose");
  300. canvas_draw_str_aligned(canvas, 64, 56, AlignCenter, AlignBottom, "OK to confirm");
  301. }
  302. }
  303. if(game_state->state == GameStateLife) {
  304. // Get current gap height for rendering
  305. int gap_height = get_gap_height(game_state->selected_bird);
  306. // Pilars
  307. for(int i = 0; i < FLAPPY_PILAR_MAX; i++) {
  308. const PILAR* pilar = &game_state->pilars[i];
  309. if(pilar != NULL && pilar->visible == 1) {
  310. canvas_draw_frame(
  311. canvas, pilar->point.x, pilar->point.y, FLAPPY_GAB_WIDTH, pilar->height);
  312. canvas_draw_frame(
  313. canvas, pilar->point.x + 1, pilar->point.y, FLAPPY_GAB_WIDTH, pilar->height);
  314. canvas_draw_frame(
  315. canvas,
  316. pilar->point.x + 2,
  317. pilar->point.y,
  318. FLAPPY_GAB_WIDTH - 1,
  319. pilar->height);
  320. canvas_draw_frame(
  321. canvas,
  322. pilar->point.x,
  323. pilar->point.y + pilar->height + gap_height,
  324. FLAPPY_GAB_WIDTH,
  325. FLIPPER_LCD_HEIGHT - pilar->height - gap_height);
  326. canvas_draw_frame(
  327. canvas,
  328. pilar->point.x + 1,
  329. pilar->point.y + pilar->height + gap_height,
  330. FLAPPY_GAB_WIDTH - 1,
  331. FLIPPER_LCD_HEIGHT - pilar->height - gap_height);
  332. canvas_draw_frame(
  333. canvas,
  334. pilar->point.x + 2,
  335. pilar->point.y + pilar->height + gap_height,
  336. FLAPPY_GAB_WIDTH - 1,
  337. FLIPPER_LCD_HEIGHT - pilar->height - gap_height);
  338. }
  339. }
  340. // Switch animation - Using character directly from bird_sets
  341. int bird_state = BirdState1;
  342. if(game_state->bird.gravity < -0.5)
  343. bird_state = BirdState0;
  344. else if(game_state->bird.gravity > 0.5)
  345. bird_state = BirdState2;
  346. // Get current character dimensions
  347. CharacterDimensions dims = character_dimensions[game_state->selected_bird];
  348. // Adjust Y position to keep character in bounds
  349. int adjusted_y = game_state->bird.point.y;
  350. if(adjusted_y < 0) adjusted_y = 0;
  351. if(adjusted_y > FLIPPER_LCD_HEIGHT - dims.width) {
  352. adjusted_y = FLIPPER_LCD_HEIGHT - dims.width;
  353. }
  354. // Draw the character with adjusted position
  355. canvas_draw_icon(
  356. canvas,
  357. game_state->bird.point.x,
  358. adjusted_y,
  359. bird_sets[game_state->selected_bird][bird_state]);
  360. // Score display
  361. canvas_set_font(canvas, FontSecondary);
  362. char buffer[12];
  363. snprintf(buffer, sizeof(buffer), "%u", game_state->points);
  364. canvas_draw_str_aligned(canvas, 64, 12, AlignCenter, AlignBottom, buffer);
  365. if(game_state->debug) {
  366. char coordinates[20];
  367. snprintf(coordinates, sizeof(coordinates), "Y: %u", adjusted_y);
  368. canvas_draw_str_aligned(canvas, 1, 12, AlignCenter, AlignBottom, coordinates);
  369. }
  370. }
  371. if(game_state->state == GameStateGameOver) {
  372. // Adjusted box height for exactly 3 lines of text
  373. canvas_set_color(canvas, ColorWhite);
  374. canvas_draw_box(canvas, 24, 12, 82, 36); // Reduced height from 42 to 36
  375. canvas_set_color(canvas, ColorBlack);
  376. canvas_draw_frame(canvas, 24, 12, 82, 36);
  377. // Game Over text
  378. canvas_set_font(canvas, FontPrimary);
  379. canvas_draw_str_aligned(canvas, 64, 22, AlignCenter, AlignBottom, "Game Over");
  380. // Current score
  381. canvas_set_font(canvas, FontSecondary);
  382. char buffer[12];
  383. snprintf(buffer, sizeof(buffer), "Score: %u", game_state->points);
  384. canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignBottom, buffer);
  385. // High score
  386. char hi_buffer[16];
  387. snprintf(hi_buffer, sizeof(hi_buffer), "Best: %u", game_state->high_score);
  388. canvas_draw_str_aligned(canvas, 64, 42, AlignCenter, AlignBottom, hi_buffer);
  389. // New Best! message (shown outside the box)
  390. if(game_state->points > game_state->high_score) {
  391. canvas_draw_str_aligned(canvas, 64, 52, AlignCenter, AlignBottom, "New Best!");
  392. }
  393. // Collision effect
  394. if(game_state->collision_frame > 0) {
  395. canvas_invert_color(canvas);
  396. }
  397. }
  398. furi_mutex_release(game_state->mutex);
  399. }
  400. static void flappy_game_input_callback(InputEvent* input_event, void* ctx) {
  401. furi_assert(ctx);
  402. FuriMessageQueue* event_queue = ctx;
  403. GameEvent event = {.type = EventTypeKey, .input = *input_event};
  404. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  405. }
  406. static void flappy_game_update_timer_callback(void* ctx) {
  407. furi_assert(ctx);
  408. FuriMessageQueue* event_queue = ctx;
  409. GameEvent event = {.type = EventTypeTick};
  410. furi_message_queue_put(event_queue, &event, 0);
  411. }
  412. int32_t flappy_game_app(void* p) {
  413. UNUSED(p);
  414. int32_t return_code = 0;
  415. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(GameEvent));
  416. GameState* game_state = malloc(sizeof(GameState));
  417. flappy_game_state_init(game_state);
  418. game_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  419. if(!game_state->mutex) {
  420. FURI_LOG_E(TAG, "cannot create mutex\r\n");
  421. return_code = 255;
  422. goto free_and_exit;
  423. }
  424. // Set system callbacks
  425. ViewPort* view_port = view_port_alloc();
  426. view_port_draw_callback_set(view_port, flappy_game_render_callback, game_state);
  427. view_port_input_callback_set(view_port, flappy_game_input_callback, event_queue);
  428. FuriTimer* timer =
  429. furi_timer_alloc(flappy_game_update_timer_callback, FuriTimerTypePeriodic, event_queue);
  430. furi_timer_start(timer, furi_kernel_get_tick_frequency() / 25);
  431. // Open GUI and register view_port
  432. Gui* gui = furi_record_open(RECORD_GUI);
  433. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  434. // Call dolphin deed on game start
  435. dolphin_deed(DolphinDeedPluginGameStart);
  436. GameEvent event;
  437. for(bool processing = true; processing;) {
  438. FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
  439. furi_mutex_acquire(game_state->mutex, FuriWaitForever);
  440. if(event_status == FuriStatusOk) {
  441. // press events
  442. if(event.type == EventTypeKey) {
  443. if(event.input.type == InputTypePress) {
  444. switch(event.input.key) {
  445. case InputKeyUp:
  446. if(game_state->state == GameStateStart && !game_state->in_bird_select) {
  447. game_state->in_bird_select = true;
  448. } else if(game_state->state == GameStateLife) {
  449. flappy_game_flap(game_state);
  450. }
  451. break;
  452. case InputKeyDown:
  453. if(game_state->state == GameStateStart && game_state->in_bird_select) {
  454. game_state->in_bird_select = false;
  455. }
  456. break;
  457. case InputKeyLeft:
  458. if(game_state->state == GameStateStart && game_state->in_bird_select) {
  459. if(game_state->selected_bird == 0) {
  460. game_state->selected_bird = BirdTypeMAX - 1;
  461. } else {
  462. game_state->selected_bird--;
  463. }
  464. }
  465. break;
  466. case InputKeyRight:
  467. if(game_state->state == GameStateStart && game_state->in_bird_select) {
  468. game_state->selected_bird =
  469. (game_state->selected_bird + 1) % BirdTypeMAX;
  470. }
  471. break;
  472. case InputKeyOk:
  473. if(game_state->state == GameStateStart) {
  474. if(game_state->in_bird_select) {
  475. game_state->in_bird_select = false;
  476. } else {
  477. game_state->state = GameStateLife;
  478. }
  479. } else if(game_state->state == GameStateGameOver) {
  480. flappy_game_state_init(game_state);
  481. } else if(game_state->state == GameStateLife) {
  482. flappy_game_flap(game_state);
  483. }
  484. break;
  485. case InputKeyBack:
  486. if(game_state->state == GameStateStart && game_state->in_bird_select) {
  487. game_state->in_bird_select = false;
  488. } else {
  489. processing = false;
  490. }
  491. break;
  492. default:
  493. break;
  494. }
  495. }
  496. } else if(event.type == EventTypeTick) {
  497. flappy_game_tick(game_state);
  498. }
  499. }
  500. furi_mutex_release(game_state->mutex);
  501. view_port_update(view_port);
  502. }
  503. furi_timer_free(timer);
  504. view_port_enabled_set(view_port, false);
  505. gui_remove_view_port(gui, view_port);
  506. furi_record_close(RECORD_GUI);
  507. view_port_free(view_port);
  508. furi_mutex_free(game_state->mutex);
  509. free_and_exit:
  510. flappy_game_state_free(game_state);
  511. furi_message_queue_free(event_queue);
  512. return return_code;
  513. }