game.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. #include <gui/view_holder.h>
  2. #include <game/game.h>
  3. #include <game/storage.h>
  4. #include <alloc/alloc.h>
  5. /****** Game ******/
  6. /*
  7. Write here the start code for your game, for example: creating a level and so on.
  8. Game context is allocated (game.context_size) and passed to this function, you can use it to store your game data.
  9. */
  10. static void game_start(GameManager *game_manager, void *ctx)
  11. {
  12. // Do some initialization here, for example you can load score from storage.
  13. // check if enough memory
  14. if (!is_enough_heap(sizeof(GameContext), true))
  15. {
  16. FURI_LOG_E("Game", "Not enough heap memory.. ending game early.");
  17. GameContext *game_context = ctx;
  18. game_context->ended_early = true;
  19. game_manager_game_stop(game_manager); // end game early
  20. return;
  21. }
  22. // For simplicity, we will just set it to 0.
  23. GameContext *game_context = ctx;
  24. game_context->fps = atof_(fps_choices_str[fps_index]);
  25. game_context->player = NULL;
  26. game_context->ended_early = false;
  27. game_context->current_level = 0;
  28. game_context->level_count = 0;
  29. game_context->enemy_count = 0;
  30. game_context->npc_count = 0;
  31. game_context->game_mode = game_mode_index;
  32. // set all levels to NULL
  33. for (int i = 0; i < MAX_LEVELS; i++)
  34. game_context->levels[i] = NULL;
  35. // set all enemies to NULL
  36. for (int i = 0; i < MAX_ENEMIES; i++)
  37. game_context->enemies[i] = NULL;
  38. // set all npcs to NULL
  39. for (int i = 0; i < MAX_NPCS; i++)
  40. game_context->npcs[i] = NULL;
  41. if (game_context->game_mode == GAME_MODE_PVE)
  42. {
  43. // attempt to allocate all levels
  44. for (int i = 0; i < MAX_LEVELS; i++)
  45. {
  46. if (!allocate_level(game_manager, i))
  47. {
  48. FURI_LOG_E("Game", "Failed to allocate level %d", i);
  49. if (i == 0)
  50. {
  51. game_context->levels[0] = game_manager_add_level(game_manager, world_training());
  52. game_context->level_count = 1;
  53. }
  54. break;
  55. }
  56. else
  57. game_context->level_count++;
  58. }
  59. }
  60. else if (game_context->game_mode == GAME_MODE_STORY)
  61. {
  62. // show tutorial only for now
  63. game_context->levels[0] = game_manager_add_level(game_manager, world_training());
  64. game_context->level_count = 1;
  65. }
  66. else if (game_context->game_mode == GAME_MODE_PVP)
  67. {
  68. // show pvp
  69. game_context->levels[0] = game_manager_add_level(game_manager, world_pvp());
  70. game_context->level_count = 1;
  71. }
  72. // imu
  73. game_context->imu = imu_alloc();
  74. game_context->imu_present = imu_present(game_context->imu);
  75. // FlipperHTTP
  76. if (game_context->game_mode == GAME_MODE_PVP)
  77. {
  78. // check if enough memory
  79. if (!is_enough_heap(sizeof(FlipperHTTP), true))
  80. {
  81. FURI_LOG_E("Game", "Not enough heap memory.. ending game early.");
  82. game_context->ended_early = true;
  83. game_manager_game_stop(game_manager); // end game early
  84. return;
  85. }
  86. game_context->fhttp = flipper_http_alloc();
  87. if (!game_context->fhttp)
  88. {
  89. FURI_LOG_E("Game", "Failed to allocate FlipperHTTP");
  90. game_context->ended_early = true;
  91. game_manager_game_stop(game_manager); // end game early
  92. return;
  93. }
  94. game_context->ws_info = furi_string_alloc();
  95. furi_string_reserve(game_context->ws_info, sizeof(game_context->fhttp->last_response));
  96. }
  97. }
  98. static void thanks(Canvas *canvas, void *context)
  99. {
  100. UNUSED(context);
  101. canvas_set_font(canvas, FontPrimary);
  102. canvas_draw_str(canvas, 35, 8, "Saving game");
  103. canvas_set_font(canvas, FontSecondary);
  104. canvas_draw_str(canvas, 0, 50, "Please wait while your");
  105. canvas_draw_str(canvas, 0, 60, "game is saved.");
  106. }
  107. /*
  108. Write here the stop code for your game, for example, freeing memory, if it was allocated.
  109. You don't need to free level, sprites or entities, it will be done automatically.
  110. Also, you don't need to free game_context, it will be done automatically, after this function.
  111. */
  112. static void game_stop(void *ctx)
  113. {
  114. furi_check(ctx);
  115. GameContext *game_context = ctx;
  116. const size_t heap_size = memmgr_heap_get_max_free_block();
  117. imu_free(game_context->imu);
  118. game_context->imu = NULL;
  119. // clear current level early
  120. if (game_context->levels[game_context->current_level])
  121. {
  122. level_clear(game_context->levels[game_context->current_level]);
  123. }
  124. if (game_context->game_mode == GAME_MODE_PVP)
  125. {
  126. if (game_context->fhttp)
  127. {
  128. flipper_http_websocket_stop(game_context->fhttp); // close websocket
  129. remove_player_from_lobby(game_context->fhttp); // remove player from lobby
  130. flipper_http_free(game_context->fhttp);
  131. }
  132. if (game_context->ws_info)
  133. {
  134. furi_string_free(game_context->ws_info);
  135. game_context->ws_info = NULL;
  136. }
  137. }
  138. PlayerContext *player_context = malloc(sizeof(PlayerContext));
  139. if (!player_context)
  140. {
  141. FURI_LOG_E("Game", "Failed to allocate PlayerContext");
  142. return;
  143. }
  144. if (!game_context->ended_early)
  145. easy_flipper_dialog(
  146. "Game Over",
  147. "Thanks for playing FlipWorld!\nHit BACK then wait for\nthe game to save.");
  148. else
  149. {
  150. char message[128];
  151. snprintf(message, sizeof(message), "Ran out of memory so the\ngame ended early. There were\n%zu bytes free.\n\nHit BACK to exit.", heap_size);
  152. easy_flipper_dialog("Game Over", message);
  153. }
  154. // save the player context
  155. if (load_player_context(player_context))
  156. {
  157. ViewPort *view_port = view_port_alloc();
  158. view_port_draw_callback_set(view_port, thanks, NULL);
  159. Gui *gui = furi_record_open(RECORD_GUI);
  160. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  161. uint32_t tick_count = furi_get_tick();
  162. furi_delay_ms(800);
  163. // save the player context to the API
  164. game_context->fhttp = flipper_http_alloc();
  165. if (game_context->fhttp)
  166. {
  167. save_player_context_api(player_context, game_context->fhttp);
  168. flipper_http_free(game_context->fhttp);
  169. }
  170. const uint32_t delay = 3500;
  171. tick_count = (tick_count + delay) - furi_get_tick();
  172. if (tick_count <= delay)
  173. {
  174. furi_delay_ms(tick_count);
  175. }
  176. easy_flipper_dialog("Game Saved", "Hit BACK to exit.");
  177. flip_world_show_submenu();
  178. gui_remove_view_port(gui, view_port);
  179. furi_record_close(RECORD_GUI);
  180. }
  181. // free the player context
  182. if (player_context)
  183. {
  184. free(player_context);
  185. player_context = NULL;
  186. }
  187. }
  188. /*
  189. Your game configuration, do not rename this variable, but you can change its content here.
  190. */
  191. const Game game = {
  192. .target_fps = 0, // set to 0 because we set this in game_app (callback.c line 22)
  193. .show_fps = false, // show fps counter on the screen
  194. .always_backlight = true, // keep display backlight always on
  195. .start = game_start, // will be called once, when game starts
  196. .stop = game_stop, // will be called once, when game stops
  197. .context_size = sizeof(GameContext), // size of game context
  198. };