world.c 17 KB

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