world.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. #include <game/world.h>
  2. #include <flip_storage/storage.h>
  3. void draw_bounds(Canvas *canvas)
  4. {
  5. // Draw the outer bounds adjusted by camera offset
  6. // we draw this last to ensure users can see the bounds
  7. canvas_draw_frame(canvas, -camera_x, -camera_y, WORLD_WIDTH, WORLD_HEIGHT);
  8. }
  9. bool draw_json_world(Level *level, const char *json_data)
  10. {
  11. for (int i = 0; i < MAX_WORLD_OBJECTS; i++)
  12. {
  13. // 1) Get data array item
  14. char *data = get_json_array_value("json_data", i, json_data);
  15. if (!data)
  16. {
  17. // Means we've reached the end of the array
  18. break;
  19. }
  20. // 2) Extract all required fields
  21. char *icon = get_json_value("icon", data);
  22. char *x = get_json_value("x", data);
  23. char *y = get_json_value("y", data);
  24. char *amount = get_json_value("amount", data);
  25. char *horizontal = get_json_value("horizontal", data);
  26. // 3) Check for any NULL pointers
  27. if (!icon || !x || !y || !amount || !horizontal)
  28. {
  29. FURI_LOG_E("Game", "Failed Data: %s", data);
  30. // Free everything carefully
  31. if (data)
  32. free(data);
  33. if (icon)
  34. free(icon);
  35. if (x)
  36. free(x);
  37. if (y)
  38. free(y);
  39. if (amount)
  40. free(amount);
  41. if (horizontal)
  42. free(horizontal);
  43. level_clear(level);
  44. return false;
  45. }
  46. // 4) Get the IconContext
  47. IconContext *icon_context = get_icon_context(icon);
  48. if (!icon_context)
  49. {
  50. FURI_LOG_E("Game", "Failed Icon: %s", icon);
  51. free(data);
  52. free(icon);
  53. free(x);
  54. free(y);
  55. free(amount);
  56. free(horizontal);
  57. level_clear(level);
  58. return false;
  59. }
  60. // 5) Decide how many icons to spawn
  61. int count = atoi(amount);
  62. if (count < 2)
  63. {
  64. // Just one icon
  65. spawn_icon(
  66. level,
  67. icon_context->icon,
  68. atoi(x),
  69. atoi(y),
  70. icon_context->width,
  71. icon_context->height);
  72. }
  73. else
  74. {
  75. // Spawn multiple in a line
  76. bool is_horizontal = (strcmp(horizontal, "true") == 0);
  77. spawn_icon_line(
  78. level,
  79. icon_context->icon,
  80. atoi(x),
  81. atoi(y),
  82. icon_context->width,
  83. icon_context->height,
  84. count,
  85. is_horizontal);
  86. }
  87. // 6) Cleanup
  88. free(data);
  89. free(icon);
  90. free(x);
  91. free(y);
  92. free(amount);
  93. free(horizontal);
  94. free(icon_context);
  95. }
  96. return true;
  97. }
  98. bool draw_json_world_furi(Level *level, FuriString *json_data)
  99. {
  100. for (int i = 0; i < MAX_WORLD_OBJECTS; i++)
  101. {
  102. // 1) Get data array item as FuriString
  103. FuriString *data = get_json_array_value_furi("json_data", i, json_data);
  104. if (!data)
  105. {
  106. // Means we've reached the end of the array
  107. break;
  108. }
  109. // 2) Extract all required fields
  110. FuriString *icon = get_json_value_furi("icon", data);
  111. FuriString *x = get_json_value_furi("x", data);
  112. FuriString *y = get_json_value_furi("y", data);
  113. FuriString *amount = get_json_value_furi("amount", data);
  114. FuriString *horizontal = get_json_value_furi("horizontal", data);
  115. // 3) Check for any NULL pointers
  116. if (!icon || !x || !y || !amount || !horizontal)
  117. {
  118. FURI_LOG_E("Game", "Failed Data: %s", furi_string_get_cstr(data));
  119. if (data)
  120. furi_string_free(data);
  121. if (icon)
  122. furi_string_free(icon);
  123. if (x)
  124. furi_string_free(x);
  125. if (y)
  126. furi_string_free(y);
  127. if (amount)
  128. furi_string_free(amount);
  129. if (horizontal)
  130. furi_string_free(horizontal);
  131. level_clear(level);
  132. return false;
  133. }
  134. // 4) Get the IconContext from a FuriString
  135. IconContext *icon_context = get_icon_context_furi(icon);
  136. if (!icon_context)
  137. {
  138. FURI_LOG_E("Game", "Failed Icon: %s", furi_string_get_cstr(icon));
  139. furi_string_free(data);
  140. furi_string_free(icon);
  141. furi_string_free(x);
  142. furi_string_free(y);
  143. furi_string_free(amount);
  144. furi_string_free(horizontal);
  145. level_clear(level);
  146. return false;
  147. }
  148. // 5) Decide how many icons to spawn
  149. int count = atoi(furi_string_get_cstr(amount));
  150. if (count < 2)
  151. {
  152. // Just one icon
  153. spawn_icon(
  154. level,
  155. icon_context->icon,
  156. atoi(furi_string_get_cstr(x)),
  157. atoi(furi_string_get_cstr(y)),
  158. icon_context->width,
  159. icon_context->height);
  160. }
  161. else
  162. {
  163. // Spawn multiple in a line
  164. bool is_horizontal = (furi_string_cmp(horizontal, "true") == 0);
  165. spawn_icon_line(
  166. level,
  167. icon_context->icon,
  168. atoi(furi_string_get_cstr(x)),
  169. atoi(furi_string_get_cstr(y)),
  170. icon_context->width,
  171. icon_context->height,
  172. count,
  173. is_horizontal);
  174. }
  175. // 6) Clean up after every iteration
  176. furi_string_free(data);
  177. furi_string_free(icon);
  178. furi_string_free(x);
  179. furi_string_free(y);
  180. furi_string_free(amount);
  181. furi_string_free(horizontal);
  182. free(icon_context);
  183. }
  184. return true;
  185. }
  186. void draw_tree_world(Level *level)
  187. {
  188. IconContext *tree_icon = get_icon_context("tree");
  189. if (!tree_icon)
  190. {
  191. FURI_LOG_E("Game", "Failed to get tree icon context");
  192. return;
  193. }
  194. // Spawn two full left/up tree lines
  195. for (int i = 0; i < 2; i++)
  196. {
  197. // Horizontal line of 22 icons
  198. spawn_icon_line(level, tree_icon->icon, 5, 2 + i * 17, tree_icon->width, tree_icon->height, 22, true);
  199. // Vertical line of 11 icons
  200. spawn_icon_line(level, tree_icon->icon, 5 + i * 17, 2, tree_icon->width, tree_icon->height, 11, false);
  201. }
  202. // Spawn two full down tree lines
  203. for (int i = 9; i < 11; i++)
  204. {
  205. // Horizontal line of 22 icons
  206. spawn_icon_line(level, tree_icon->icon, 5, 2 + i * 17, tree_icon->width, tree_icon->height, 22, true);
  207. }
  208. // Spawn two full right tree lines
  209. for (int i = 20; i < 22; i++)
  210. {
  211. // Vertical line of 8 icons starting further down (y=50)
  212. spawn_icon_line(level, tree_icon->icon, 5 + i * 17, 50, tree_icon->width, tree_icon->height, 8, false);
  213. }
  214. // Labyrinth lines
  215. // Third line (14 left, then a gap, then 3 middle)
  216. spawn_icon_line(level, tree_icon->icon, 5, 2 + 2 * 17, tree_icon->width, tree_icon->height, 14, true);
  217. spawn_icon_line(level, tree_icon->icon, 5 + 16 * 17, 2 + 2 * 17, tree_icon->width, tree_icon->height, 3, true);
  218. // Fourth line (3 left, 6 middle, 4 right)
  219. spawn_icon_line(level, tree_icon->icon, 5, 2 + 3 * 17, tree_icon->width, tree_icon->height, 3, true); // 3 left
  220. spawn_icon_line(level, tree_icon->icon, 5 + 7 * 17, 2 + 3 * 17, tree_icon->width, tree_icon->height, 6, true); // 6 middle
  221. spawn_icon_line(level, tree_icon->icon, 5 + 15 * 17, 2 + 3 * 17, tree_icon->width, tree_icon->height, 4, true); // 4 right
  222. // Fifth line (6 left, 7 middle)
  223. spawn_icon_line(level, tree_icon->icon, 5, 2 + 4 * 17, tree_icon->width, tree_icon->height, 6, true);
  224. spawn_icon_line(level, tree_icon->icon, 5 + 7 * 17, 2 + 4 * 17, tree_icon->width, tree_icon->height, 7, true);
  225. // Sixth line (5 left, 3 middle, 7 right)
  226. spawn_icon_line(level, tree_icon->icon, 5, 2 + 5 * 17, tree_icon->width, tree_icon->height, 5, true); // 5 left
  227. spawn_icon_line(level, tree_icon->icon, 5 + 7 * 17, 2 + 5 * 17, tree_icon->width, tree_icon->height, 3, true); // 3 middle
  228. spawn_icon_line(level, tree_icon->icon, 5 + 15 * 17, 2 + 5 * 17, tree_icon->width, tree_icon->height, 7, true); // 7 right
  229. // Seventh line (0 left, 7 middle, 4 right)
  230. spawn_icon_line(level, tree_icon->icon, 5 + 6 * 17, 2 + 6 * 17, tree_icon->width, tree_icon->height, 7, true); // 7 middle
  231. spawn_icon_line(level, tree_icon->icon, 5 + 14 * 17, 2 + 6 * 17, tree_icon->width, tree_icon->height, 4, true); // 4 right
  232. // Eighth line (4 left, 3 middle, 4 right)
  233. spawn_icon_line(level, tree_icon->icon, 5, 2 + 7 * 17, tree_icon->width, tree_icon->height, 4, true); // 4 left
  234. spawn_icon_line(level, tree_icon->icon, 5 + 7 * 17, 2 + 7 * 17, tree_icon->width, tree_icon->height, 3, true); // 3 middle
  235. spawn_icon_line(level, tree_icon->icon, 5 + 15 * 17, 2 + 7 * 17, tree_icon->width, tree_icon->height, 4, true); // 4 right
  236. // Ninth line (3 left, 1 middle, 3 right)
  237. spawn_icon_line(level, tree_icon->icon, 5, 2 + 8 * 17, tree_icon->width, tree_icon->height, 3, true); // 3 left
  238. spawn_icon_line(level, tree_icon->icon, 5 + 5 * 17, 2 + 8 * 17, tree_icon->width, tree_icon->height, 1, true); // 1 middle
  239. spawn_icon_line(level, tree_icon->icon, 5 + 11 * 17, 2 + 8 * 17, tree_icon->width, tree_icon->height, 3, true); // 3 right
  240. free(tree_icon);
  241. }
  242. void draw_town_world(Level *level)
  243. {
  244. // define all the icons
  245. IconContext *house_icon = get_icon_context("house");
  246. IconContext *fence_icon = get_icon_context("fence");
  247. IconContext *fence_end_icon = get_icon_context("fence_end");
  248. IconContext *plant_icon = get_icon_context("plant");
  249. IconContext *flower_icon = get_icon_context("flower");
  250. IconContext *man_icon = get_icon_context("man");
  251. IconContext *woman_icon = get_icon_context("woman");
  252. IconContext *lake_top_left_icon = get_icon_context("lake_top_left");
  253. IconContext *lake_top_icon = get_icon_context("lake_top");
  254. IconContext *lake_top_right_icon = get_icon_context("lake_top_right");
  255. IconContext *lake_left_icon = get_icon_context("lake_left");
  256. IconContext *lake_right_icon = get_icon_context("lake_right");
  257. IconContext *lake_bottom_left_icon = get_icon_context("lake_bottom_left");
  258. IconContext *lake_bottom_icon = get_icon_context("lake_bottom");
  259. IconContext *lake_bottom_right_icon = get_icon_context("lake_bottom_right");
  260. IconContext *tree_icon = get_icon_context("tree");
  261. // check if any of the icons are NULL
  262. if (!house_icon || !fence_icon || !fence_end_icon || !plant_icon || !flower_icon ||
  263. !man_icon || !woman_icon || !lake_top_left_icon || !lake_top_icon || !lake_top_right_icon ||
  264. !lake_left_icon || !lake_right_icon || !lake_bottom_left_icon || !lake_bottom_icon || !lake_bottom_right_icon || !tree_icon)
  265. {
  266. FURI_LOG_E("Game", "Failed to get icon context");
  267. return;
  268. }
  269. // house-fence group 1
  270. spawn_icon(level, house_icon->icon, 148, 36, house_icon->width, house_icon->height);
  271. spawn_icon(level, fence_icon->icon, 148, 72, fence_icon->width, fence_icon->height);
  272. spawn_icon(level, fence_icon->icon, 164, 72, fence_icon->width, fence_icon->height);
  273. spawn_icon(level, fence_end_icon->icon, 180, 72, fence_end_icon->width, fence_end_icon->height);
  274. // house-fence group 4 (the left of group 1)
  275. spawn_icon(level, house_icon->icon, 96, 36, house_icon->width, house_icon->height);
  276. spawn_icon(level, fence_icon->icon, 96, 72, fence_icon->width, fence_icon->height);
  277. spawn_icon(level, fence_icon->icon, 110, 72, fence_icon->width, fence_icon->height);
  278. spawn_icon(level, fence_end_icon->icon, 126, 72, fence_end_icon->width, fence_end_icon->height);
  279. // house-fence group 5 (the left of group 4)
  280. spawn_icon(level, house_icon->icon, 40, 36, house_icon->width, house_icon->height);
  281. spawn_icon(level, fence_icon->icon, 40, 72, fence_icon->width, fence_icon->height);
  282. spawn_icon(level, fence_icon->icon, 56, 72, fence_icon->width, fence_icon->height);
  283. spawn_icon(level, fence_end_icon->icon, 72, 72, fence_end_icon->width, fence_end_icon->height);
  284. // line of fences on the 8th row (using spawn_icon_line)
  285. spawn_icon_line(level, fence_icon->icon, 8, 100, fence_icon->width, fence_icon->height, 10, true);
  286. // plants spaced out underneath the fences
  287. spawn_icon_line(level, plant_icon->icon, 40, 110, plant_icon->width, plant_icon->height, 6, true);
  288. spawn_icon_line(level, flower_icon->icon, 40, 140, flower_icon->width, flower_icon->height, 6, true);
  289. // man and woman
  290. spawn_icon(level, man_icon->icon, 156, 110, man_icon->width, man_icon->height);
  291. spawn_icon(level, woman_icon->icon, 164, 110, woman_icon->width, woman_icon->height);
  292. // lake
  293. // Top row
  294. spawn_icon(level, lake_top_left_icon->icon, 240, 52, lake_top_left_icon->width, lake_top_left_icon->height);
  295. spawn_icon(level, lake_top_icon->icon, 264, 52, lake_top_icon->width, lake_top_icon->height);
  296. spawn_icon(level, lake_top_right_icon->icon, 295, 52, lake_top_right_icon->width, lake_top_right_icon->height);
  297. // Middle row
  298. spawn_icon(level, lake_left_icon->icon, 231, 74, lake_left_icon->width, lake_left_icon->height);
  299. spawn_icon(level, lake_right_icon->icon, 317, 74, lake_right_icon->width, lake_right_icon->height);
  300. // Bottom row
  301. spawn_icon(level, lake_bottom_left_icon->icon, 240, 105, lake_bottom_left_icon->width, lake_bottom_left_icon->height);
  302. spawn_icon(level, lake_bottom_icon->icon, 264, 124, lake_bottom_icon->width, lake_bottom_icon->height);
  303. spawn_icon(level, lake_bottom_right_icon->icon, 295, 105, lake_bottom_right_icon->width, lake_bottom_right_icon->height);
  304. // Spawn two full left/up tree lines
  305. for (int i = 0; i < 2; i++)
  306. {
  307. // Horizontal line of 22 icons
  308. spawn_icon_line(level, tree_icon->icon, 5, 2 + i * 17, tree_icon->width, tree_icon->height, 22, true);
  309. // Vertical line of 11 icons
  310. spawn_icon_line(level, tree_icon->icon, 5 + i * 17, 2, tree_icon->width, tree_icon->height, 11, false);
  311. }
  312. // Spawn two full down tree lines
  313. for (int i = 9; i < 11; i++)
  314. {
  315. // Horizontal line of 22 icons
  316. spawn_icon_line(level, tree_icon->icon, 5, 2 + i * 17, tree_icon->width, tree_icon->height, 22, true);
  317. }
  318. // Spawn two full right tree lines
  319. for (int i = 20; i < 22; i++)
  320. {
  321. // Vertical line of 8 icons starting further down (y=50)
  322. spawn_icon_line(level, tree_icon->icon, 5 + i * 17, 50, tree_icon->width, tree_icon->height, 8, false);
  323. }
  324. free(house_icon);
  325. free(fence_icon);
  326. free(fence_end_icon);
  327. free(plant_icon);
  328. free(flower_icon);
  329. free(man_icon);
  330. free(woman_icon);
  331. free(lake_top_left_icon);
  332. free(lake_top_icon);
  333. free(lake_top_right_icon);
  334. free(lake_left_icon);
  335. free(lake_right_icon);
  336. free(lake_bottom_left_icon);
  337. free(lake_bottom_icon);
  338. free(lake_bottom_right_icon);
  339. free(tree_icon);
  340. }
  341. FuriString *fetch_world(const char *name)
  342. {
  343. if (!name)
  344. {
  345. FURI_LOG_E("Game", "World name is NULL");
  346. return NULL;
  347. }
  348. if (!app_instance)
  349. {
  350. // as long as the game is running, app_instance should be non-NULL
  351. FURI_LOG_E("Game", "App instance is NULL");
  352. return NULL;
  353. }
  354. if (!flipper_http_init(flipper_http_rx_callback, app_instance))
  355. {
  356. FURI_LOG_E("Game", "Failed to initialize HTTP");
  357. return NULL;
  358. }
  359. char url[256];
  360. snprintf(url, sizeof(url), "https://www.flipsocial.net/api/world/get/world/%s/", name);
  361. snprintf(fhttp.file_path, sizeof(fhttp.file_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/worlds/%s.json", name);
  362. fhttp.save_received_data = true;
  363. if (!flipper_http_get_request_with_headers(url, "{\"Content-Type\": \"application/json\"}"))
  364. {
  365. FURI_LOG_E("Game", "Failed to send HTTP request");
  366. flipper_http_deinit();
  367. return NULL;
  368. }
  369. fhttp.state = RECEIVING;
  370. furi_timer_start(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
  371. while (fhttp.state == RECEIVING && furi_timer_is_running(fhttp.get_timeout_timer) > 0)
  372. {
  373. // Wait for the request to be received
  374. furi_delay_ms(100);
  375. }
  376. furi_timer_stop(fhttp.get_timeout_timer);
  377. if (fhttp.state != IDLE)
  378. {
  379. FURI_LOG_E("Game", "Failed to receive world data");
  380. flipper_http_deinit();
  381. return NULL;
  382. }
  383. flipper_http_deinit();
  384. FuriString *returned_data = load_furi_world(name);
  385. if (!returned_data)
  386. {
  387. FURI_LOG_E("Game", "Failed to load world data from file");
  388. return NULL;
  389. }
  390. return returned_data;
  391. }