level.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. #include <game/level.h>
  2. #include <flip_storage/storage.h>
  3. #include <game/storage.h>
  4. bool allocate_level(GameManager *manager, int index)
  5. {
  6. GameContext *game_context = game_manager_game_context_get(manager);
  7. // open the world list from storage, then create a level for each world
  8. char file_path[128];
  9. snprintf(file_path, sizeof(file_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/worlds/world_list.json");
  10. FuriString *world_list = flipper_http_load_from_file(file_path);
  11. if (!world_list)
  12. {
  13. FURI_LOG_E("Game", "Failed to load world list");
  14. game_context->levels[0] = game_manager_add_level(manager, generic_level("town_world_v2", 0));
  15. game_context->level_count = 1;
  16. return false;
  17. }
  18. FuriString *world_name = get_json_array_value_furi("worlds", index, world_list);
  19. if (!world_name)
  20. {
  21. FURI_LOG_E("Game", "Failed to get world name");
  22. furi_string_free(world_list);
  23. return false;
  24. }
  25. FURI_LOG_I("Game", "Allocating level %d for world %s", index, furi_string_get_cstr(world_name));
  26. game_context->levels[index] = game_manager_add_level(manager, generic_level(furi_string_get_cstr(world_name), index));
  27. furi_string_free(world_name);
  28. furi_string_free(world_list);
  29. return true;
  30. }
  31. static void set_world(Level *level, GameManager *manager, char *id)
  32. {
  33. char file_path[256];
  34. snprintf(file_path, sizeof(file_path),
  35. STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/worlds/%s/%s_json_data.json",
  36. id, id);
  37. FuriString *json_data_str = flipper_http_load_from_file(file_path);
  38. if (!json_data_str || furi_string_empty(json_data_str))
  39. {
  40. FURI_LOG_E("Game", "Failed to load json data from file");
  41. draw_town_world(manager, level);
  42. return;
  43. }
  44. if (!is_enough_heap(28400))
  45. {
  46. FURI_LOG_E("Game", "Not enough heap memory.. ending game early.");
  47. GameContext *game_context = game_manager_game_context_get(manager);
  48. game_context->ended_early = true;
  49. game_manager_game_stop(manager); // end game early
  50. furi_string_free(json_data_str);
  51. return;
  52. }
  53. FURI_LOG_I("Game", "Drawing world");
  54. if (!draw_json_world_furi(manager, level, json_data_str))
  55. {
  56. FURI_LOG_E("Game", "Failed to draw world");
  57. draw_town_world(manager, level);
  58. furi_string_free(json_data_str);
  59. }
  60. else
  61. {
  62. FURI_LOG_I("Game", "Drawing enemies");
  63. furi_string_free(json_data_str);
  64. snprintf(file_path, sizeof(file_path),
  65. STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/worlds/%s/%s_enemy_data.json",
  66. id, id);
  67. FuriString *enemy_data_str = flipper_http_load_from_file(file_path);
  68. if (!enemy_data_str || furi_string_empty(enemy_data_str))
  69. {
  70. FURI_LOG_E("Game", "Failed to get enemy data");
  71. draw_town_world(manager, level);
  72. return;
  73. }
  74. // Loop through the array
  75. for (int i = 0; i < MAX_ENEMIES; i++)
  76. {
  77. FuriString *single_enemy_data = get_json_array_value_furi("enemy_data", i, enemy_data_str);
  78. if (!single_enemy_data || furi_string_empty(single_enemy_data))
  79. {
  80. // No more enemy elements found
  81. if (single_enemy_data)
  82. furi_string_free(single_enemy_data);
  83. break;
  84. }
  85. spawn_enemy_json_furi(level, manager, single_enemy_data);
  86. furi_string_free(single_enemy_data);
  87. }
  88. furi_string_free(enemy_data_str);
  89. FURI_LOG_I("Game", "Finished loading world data");
  90. }
  91. }
  92. static void level_start(Level *level, GameManager *manager, void *context)
  93. {
  94. if (!manager || !level || !context)
  95. {
  96. FURI_LOG_E("Game", "Manager, level, or context is NULL");
  97. return;
  98. }
  99. GameContext *game_context = game_manager_game_context_get(manager);
  100. if (!level || !context)
  101. {
  102. FURI_LOG_E("Game", "Level, context, or manager is NULL");
  103. game_context->is_switching_level = false;
  104. return;
  105. }
  106. level_clear(level);
  107. LevelContext *level_context = context;
  108. if (!level_context)
  109. {
  110. FURI_LOG_E("Game", "Level context is NULL");
  111. game_context->is_switching_level = false;
  112. return;
  113. }
  114. // check if the world exists
  115. if (!world_exists(level_context->id))
  116. {
  117. FURI_LOG_E("Game", "World does not exist.. downloading now");
  118. FuriString *world_data = fetch_world(level_context->id);
  119. if (!world_data)
  120. {
  121. FURI_LOG_E("Game", "Failed to fetch world data");
  122. draw_town_world(manager, level);
  123. game_context->is_switching_level = false;
  124. // furi_delay_ms(1000);
  125. player_spawn(level, manager);
  126. return;
  127. }
  128. furi_string_free(world_data);
  129. set_world(level, manager, level_context->id);
  130. FURI_LOG_I("Game", "World set.");
  131. // furi_delay_ms(1000);
  132. game_context->is_switching_level = false;
  133. }
  134. else
  135. {
  136. FURI_LOG_I("Game", "World exists.. loading now");
  137. set_world(level, manager, level_context->id);
  138. FURI_LOG_I("Game", "World set.");
  139. // furi_delay_ms(1000);
  140. game_context->is_switching_level = false;
  141. }
  142. /*
  143. adjust the player's position n such based on icon count
  144. the more icons to draw, the slower the player moves
  145. so we'll increase the player's speed as the icon count increases
  146. by 0.1 for every 8 icons
  147. */
  148. game_context->icon_offset = 0;
  149. if (!game_context->imu_present)
  150. {
  151. game_context->icon_offset += ((game_context->icon_count / 8) / 10);
  152. }
  153. player_spawn(level, manager);
  154. }
  155. static LevelContext *level_context_generic;
  156. static LevelContext *level_generic_alloc(const char *id, int index)
  157. {
  158. if (level_context_generic == NULL)
  159. {
  160. size_t heap_size = memmgr_get_free_heap();
  161. if (heap_size < sizeof(LevelContext))
  162. {
  163. FURI_LOG_E("Game", "Not enough heap to allocate level context");
  164. return NULL;
  165. }
  166. level_context_generic = malloc(sizeof(LevelContext));
  167. }
  168. snprintf(level_context_generic->id, sizeof(level_context_generic->id), "%s", id);
  169. level_context_generic->index = index;
  170. return level_context_generic;
  171. }
  172. static void level_generic_free()
  173. {
  174. if (level_context_generic != NULL)
  175. {
  176. free(level_context_generic);
  177. level_context_generic = NULL;
  178. }
  179. }
  180. static void free_level(Level *level, GameManager *manager, void *context)
  181. {
  182. UNUSED(level);
  183. UNUSED(manager);
  184. UNUSED(context);
  185. level_generic_free();
  186. }
  187. static void level_alloc_generic_world(Level *level, GameManager *manager, void *context)
  188. {
  189. UNUSED(manager);
  190. UNUSED(level);
  191. if (!level_context_generic)
  192. {
  193. FURI_LOG_E("Game", "Generic level context not set");
  194. return;
  195. }
  196. if (!context)
  197. {
  198. FURI_LOG_E("Game", "Context is NULL");
  199. return;
  200. }
  201. LevelContext *level_context = context;
  202. snprintf(level_context->id, sizeof(level_context->id), "%s", level_context_generic->id);
  203. level_context->index = level_context_generic->index;
  204. }
  205. const LevelBehaviour _generic_level = {
  206. .alloc = level_alloc_generic_world,
  207. .free = free_level,
  208. .start = level_start,
  209. .stop = NULL,
  210. .context_size = sizeof(LevelContext),
  211. };
  212. const LevelBehaviour *generic_level(const char *id, int index)
  213. {
  214. // free any old context before allocating a new one
  215. level_generic_free();
  216. level_context_generic = level_generic_alloc(id, index);
  217. return &_generic_level;
  218. }