level.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  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, training_world());
  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. 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(level, manager, single_enemy_data);
  86. furi_string_free(single_enemy_data);
  87. }
  88. furi_string_free(enemy_data_str);
  89. // Draw NPCs
  90. FURI_LOG_I("Game", "Drawing NPCs");
  91. snprintf(file_path, sizeof(file_path),
  92. STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/worlds/%s/%s_npc_data.json",
  93. id, id);
  94. FuriString *npc_data_str = flipper_http_load_from_file(file_path);
  95. if (!npc_data_str || furi_string_empty(npc_data_str))
  96. {
  97. FURI_LOG_E("Game", "Failed to get npc data");
  98. // draw_town_world(manager, level);
  99. return;
  100. }
  101. // Loop through the array
  102. for (int i = 0; i < MAX_NPCS; i++)
  103. {
  104. FuriString *single_npc_data = get_json_array_value_furi("npc_data", i, npc_data_str);
  105. if (!single_npc_data || furi_string_empty(single_npc_data))
  106. {
  107. // No more npc elements found
  108. if (single_npc_data)
  109. furi_string_free(single_npc_data);
  110. break;
  111. }
  112. spawn_npc(level, manager, single_npc_data);
  113. furi_string_free(single_npc_data);
  114. }
  115. furi_string_free(npc_data_str);
  116. FURI_LOG_I("Game", "World drawn");
  117. }
  118. }
  119. static void level_start(Level *level, GameManager *manager, void *context)
  120. {
  121. if (!manager || !level || !context)
  122. {
  123. FURI_LOG_E("Game", "Manager, level, or context is NULL");
  124. return;
  125. }
  126. GameContext *game_context = game_manager_game_context_get(manager);
  127. if (!level || !context)
  128. {
  129. FURI_LOG_E("Game", "Level, context, or manager is NULL");
  130. game_context->is_switching_level = false;
  131. return;
  132. }
  133. level_clear(level);
  134. LevelContext *level_context = context;
  135. if (!level_context)
  136. {
  137. FURI_LOG_E("Game", "Level context is NULL");
  138. game_context->is_switching_level = false;
  139. return;
  140. }
  141. // check if the world exists
  142. if (!world_exists(level_context->id))
  143. {
  144. FURI_LOG_E("Game", "World does not exist.. downloading now");
  145. FuriString *world_data = fetch_world(level_context->id);
  146. if (!world_data)
  147. {
  148. FURI_LOG_E("Game", "Failed to fetch world data");
  149. // draw_town_world(manager, level);
  150. game_context->is_switching_level = false;
  151. // furi_delay_ms(1000);
  152. player_spawn(level, manager);
  153. return;
  154. }
  155. furi_string_free(world_data);
  156. set_world(level, manager, level_context->id);
  157. FURI_LOG_I("Game", "World set.");
  158. // furi_delay_ms(1000);
  159. game_context->is_switching_level = false;
  160. }
  161. else
  162. {
  163. FURI_LOG_I("Game", "World exists.. loading now");
  164. set_world(level, manager, level_context->id);
  165. FURI_LOG_I("Game", "World set.");
  166. // furi_delay_ms(1000);
  167. game_context->is_switching_level = false;
  168. }
  169. /*
  170. adjust the player's position n such based on icon count
  171. the more icons to draw, the slower the player moves
  172. so we'll increase the player's speed as the icon count increases
  173. by 0.1 for every 8 icons
  174. */
  175. game_context->icon_offset = 0;
  176. if (!game_context->imu_present)
  177. {
  178. game_context->icon_offset += ((game_context->icon_count / 8) / 10);
  179. }
  180. player_spawn(level, manager);
  181. }
  182. static LevelContext *level_context_generic;
  183. static LevelContext *level_generic_alloc(const char *id, int index)
  184. {
  185. if (level_context_generic == NULL)
  186. {
  187. size_t heap_size = memmgr_get_free_heap();
  188. if (heap_size < sizeof(LevelContext))
  189. {
  190. FURI_LOG_E("Game", "Not enough heap to allocate level context");
  191. return NULL;
  192. }
  193. level_context_generic = malloc(sizeof(LevelContext));
  194. }
  195. snprintf(level_context_generic->id, sizeof(level_context_generic->id), "%s", id);
  196. level_context_generic->index = index;
  197. return level_context_generic;
  198. }
  199. static void level_generic_free()
  200. {
  201. if (level_context_generic != NULL)
  202. {
  203. free(level_context_generic);
  204. level_context_generic = NULL;
  205. }
  206. }
  207. static void free_level(Level *level, GameManager *manager, void *context)
  208. {
  209. UNUSED(level);
  210. UNUSED(manager);
  211. UNUSED(context);
  212. level_generic_free();
  213. }
  214. static void level_alloc_generic_world(Level *level, GameManager *manager, void *context)
  215. {
  216. UNUSED(manager);
  217. UNUSED(level);
  218. if (!level_context_generic)
  219. {
  220. FURI_LOG_E("Game", "Generic level context not set");
  221. return;
  222. }
  223. if (!context)
  224. {
  225. FURI_LOG_E("Game", "Context is NULL");
  226. return;
  227. }
  228. LevelContext *level_context = context;
  229. snprintf(level_context->id, sizeof(level_context->id), "%s", level_context_generic->id);
  230. level_context->index = level_context_generic->index;
  231. }
  232. const LevelBehaviour _generic_level = {
  233. .alloc = level_alloc_generic_world,
  234. .free = free_level,
  235. .start = level_start,
  236. .stop = NULL,
  237. .context_size = sizeof(LevelContext),
  238. };
  239. const LevelBehaviour *generic_level(const char *id, int index)
  240. {
  241. // free any old context before allocating a new one
  242. level_generic_free();
  243. level_context_generic = level_generic_alloc(id, index);
  244. return &_generic_level;
  245. }