game.c 7.0 KB

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