game.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. #include "game.h"
  2. /****** Entities: Player ******/
  3. Level *levels[10];
  4. static Level *get_next_level(GameManager *manager)
  5. {
  6. Level *current_level = game_manager_current_level_get(manager);
  7. for (int i = 0; i < 10; i++)
  8. {
  9. if (levels[i] == current_level)
  10. {
  11. if (levels[i + 1] != NULL)
  12. {
  13. return levels[i + 1];
  14. }
  15. else
  16. {
  17. return levels[0];
  18. }
  19. }
  20. }
  21. return levels[0] ? levels[0] : game_manager_add_level(manager, generic_level("town_world", 0));
  22. }
  23. void player_spawn(Level *level, GameManager *manager)
  24. {
  25. Entity *player = level_add_entity(level, &player_desc);
  26. // Set player position.
  27. // Depends on your game logic, it can be done in start entity function, but also can be done here.
  28. entity_pos_set(player, (Vector){WORLD_WIDTH / 2, WORLD_HEIGHT / 2});
  29. // Add collision box to player entity
  30. // Box is centered in player x and y, and it's size is 10x10
  31. entity_collider_add_rect(player, 10 + PLAYER_COLLISION_HORIZONTAL, 10 + PLAYER_COLLISION_VERTICAL);
  32. // Get player context
  33. PlayerContext *player_context = entity_context_get(player);
  34. // Load player sprite
  35. player_context->sprite_right = game_manager_sprite_load(manager, "player_right.fxbm");
  36. player_context->sprite_left = game_manager_sprite_load(manager, "player_left.fxbm");
  37. player_context->is_looking_left = false; // player starts looking right
  38. }
  39. // Modify player_update to track direction
  40. static void player_update(Entity *self, GameManager *manager, void *context)
  41. {
  42. PlayerContext *player = (PlayerContext *)context;
  43. InputState input = game_manager_input_get(manager);
  44. Vector pos = entity_pos_get(self);
  45. // Store previous direction
  46. int prev_dx = player->dx;
  47. int prev_dy = player->dy;
  48. // Reset movement deltas each frame
  49. player->dx = 0;
  50. player->dy = 0;
  51. // Handle movement input
  52. if (input.held & GameKeyUp)
  53. {
  54. pos.y -= 2;
  55. player->dy = -1;
  56. }
  57. if (input.held & GameKeyDown)
  58. {
  59. pos.y += 2;
  60. player->dy = 1;
  61. }
  62. if (input.held & GameKeyLeft)
  63. {
  64. pos.x -= 2;
  65. player->dx = -1;
  66. player->is_looking_left = true;
  67. }
  68. if (input.held & GameKeyRight)
  69. {
  70. pos.x += 2;
  71. player->dx = 1;
  72. player->is_looking_left = false;
  73. }
  74. // switch levels if holding OK
  75. if (input.held & GameKeyOk)
  76. {
  77. game_manager_next_level_set(manager, get_next_level(manager));
  78. furi_delay_ms(500);
  79. return;
  80. }
  81. // Clamp the player's position to stay within world bounds
  82. pos.x = CLAMP(pos.x, WORLD_WIDTH - 5, 5);
  83. pos.y = CLAMP(pos.y, WORLD_HEIGHT - 5, 5);
  84. // Update player position
  85. entity_pos_set(self, pos);
  86. // If the player is not moving, retain the last movement direction
  87. if (player->dx == 0 && player->dy == 0)
  88. {
  89. player->dx = prev_dx;
  90. player->dy = prev_dy;
  91. }
  92. // Handle back button to stop the game
  93. if (input.pressed & GameKeyBack)
  94. {
  95. game_manager_game_stop(manager);
  96. }
  97. }
  98. static void player_render(Entity *self, GameManager *manager, Canvas *canvas, void *context)
  99. {
  100. // Get player context
  101. UNUSED(manager);
  102. PlayerContext *player = context;
  103. // Get player position
  104. Vector pos = entity_pos_get(self);
  105. // Draw background (updates camera_x and camera_y)
  106. draw_background(canvas, pos);
  107. // Draw player sprite relative to camera, centered on the player's position
  108. canvas_draw_sprite(
  109. canvas,
  110. player->is_looking_left ? player->sprite_left : player->sprite_right,
  111. pos.x - camera_x - 5, // Center the sprite horizontally
  112. pos.y - camera_y - 5 // Center the sprite vertically
  113. );
  114. }
  115. const EntityDescription player_desc = {
  116. .start = NULL, // called when entity is added to the level
  117. .stop = NULL, // called when entity is removed from the level
  118. .update = player_update, // called every frame
  119. .render = player_render, // called every frame, after update
  120. .collision = NULL, // called when entity collides with another entity
  121. .event = NULL, // called when entity receives an event
  122. .context_size = sizeof(PlayerContext), // size of entity context, will be automatically allocated and freed
  123. };
  124. /****** Game ******/
  125. /*
  126. Write here the start code for your game, for example: creating a level and so on.
  127. Game context is allocated (game.context_size) and passed to this function, you can use it to store your game data.
  128. */
  129. static void game_start(GameManager *game_manager, void *ctx)
  130. {
  131. // Do some initialization here, for example you can load score from storage.
  132. // For simplicity, we will just set it to 0.
  133. GameContext *game_context = ctx;
  134. game_context->score = 0;
  135. // open the world list from storage, then create a level for each world
  136. char file_path[128];
  137. snprintf(file_path, sizeof(file_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/worlds/world_list.json");
  138. FuriString *world_list = flipper_http_load_from_file(file_path);
  139. if (!world_list)
  140. {
  141. FURI_LOG_E("Game", "Failed to load world list");
  142. levels[0] = game_manager_add_level(game_manager, generic_level("town_world", 0));
  143. levels[1] = game_manager_add_level(game_manager, generic_level("tree_world", 1));
  144. return;
  145. }
  146. for (int i = 0; i < 10; i++)
  147. {
  148. FuriString *world_name = get_json_array_value_furi("worlds", i, world_list);
  149. if (!world_name)
  150. {
  151. break;
  152. }
  153. levels[i] = game_manager_add_level(game_manager, generic_level(furi_string_get_cstr(world_name), i));
  154. furi_string_free(world_name);
  155. }
  156. furi_string_free(world_list);
  157. }
  158. /*
  159. Write here the stop code for your game, for example, freeing memory, if it was allocated.
  160. You don't need to free level, sprites or entities, it will be done automatically.
  161. Also, you don't need to free game_context, it will be done automatically, after this function.
  162. */
  163. static void game_stop(void *ctx)
  164. {
  165. UNUSED(ctx);
  166. // GameContext *game_context = ctx;
  167. // Do some deinitialization here, for example you can save score to storage.
  168. // For simplicity, we will just print it.
  169. // FURI_LOG_I("Game", "Your score: %lu", game_context->score);
  170. }
  171. /*
  172. Your game configuration, do not rename this variable, but you can change its content here.
  173. */
  174. const Game game = {
  175. .target_fps = 0, // set to 0 because we set this in game_app (callback.c line 22)
  176. .show_fps = false, // show fps counter on the screen
  177. .always_backlight = true, // keep display backlight always on
  178. .start = game_start, // will be called once, when game starts
  179. .stop = game_stop, // will be called once, when game stops
  180. .context_size = sizeof(GameContext), // size of game context
  181. };