game.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #include "game.h"
  2. #include "flip_world.h"
  3. #include "flip_world_icons.h"
  4. // Background rendering function
  5. // TODO: each object needs a collision box so we can detect collisions and prevent movement through walls.
  6. static void background_render(Canvas *canvas, Vector pos)
  7. {
  8. // Clear the canvas
  9. canvas_clear(canvas);
  10. // Calculate camera offset to center the player
  11. camera_x = pos.x - (SCREEN_WIDTH / 2);
  12. camera_y = pos.y - (SCREEN_HEIGHT / 2);
  13. // Clamp camera position to prevent showing areas outside the world
  14. camera_x = CLAMP(camera_x, WORLD_WIDTH - SCREEN_WIDTH, 0);
  15. camera_y = CLAMP(camera_y, WORLD_HEIGHT - SCREEN_HEIGHT, 0);
  16. // Draw the outer bounds adjusted by camera offset
  17. draw_bounds(canvas);
  18. }
  19. /****** Entities: Player ******/
  20. typedef struct
  21. {
  22. Vector trajectory; // Direction player would like to move.
  23. float radius; // collision radius
  24. int8_t dx; // x direction
  25. int8_t dy; // y direction
  26. Sprite *sprite; // player sprite
  27. } PlayerContext;
  28. // Forward declaration of player_desc, because it's used in player_spawn function.
  29. static const EntityDescription player_desc;
  30. static void player_spawn(Level *level, GameManager *manager)
  31. {
  32. Entity *player = level_add_entity(level, &player_desc);
  33. // Set player position.
  34. // Depends on your game logic, it can be done in start entity function, but also can be done here.
  35. entity_pos_set(player, (Vector){WORLD_WIDTH / 2, WORLD_HEIGHT / 2});
  36. // Add collision box to player entity
  37. // Box is centered in player x and y, and it's size is 10x10
  38. entity_collider_add_rect(player, 10, 10);
  39. // Get player context
  40. PlayerContext *player_context = entity_context_get(player);
  41. // Load player sprite
  42. player_context->sprite = game_manager_sprite_load(manager, "player.fxbm");
  43. }
  44. // Modify player_update to track direction
  45. static void player_update(Entity *self, GameManager *manager, void *context)
  46. {
  47. PlayerContext *player = (PlayerContext *)context;
  48. InputState input = game_manager_input_get(manager);
  49. Vector pos = entity_pos_get(self);
  50. // Reset direction each frame
  51. player->dx = 0;
  52. player->dy = 0;
  53. if (input.held & GameKeyUp)
  54. {
  55. pos.y -= 2;
  56. player->dy = -1;
  57. }
  58. if (input.held & GameKeyDown)
  59. {
  60. pos.y += 2;
  61. player->dy = 1;
  62. }
  63. if (input.held & GameKeyLeft)
  64. {
  65. pos.x -= 2;
  66. player->dx = -1;
  67. }
  68. if (input.held & GameKeyRight)
  69. {
  70. pos.x += 2;
  71. player->dx = 1;
  72. }
  73. pos.x = CLAMP(pos.x, WORLD_WIDTH - 5, 5);
  74. pos.y = CLAMP(pos.y, WORLD_HEIGHT - 5, 5);
  75. entity_pos_set(self, pos);
  76. if (input.pressed & GameKeyBack)
  77. {
  78. game_manager_game_stop(manager);
  79. }
  80. }
  81. static void player_render(Entity *self, GameManager *manager, Canvas *canvas, void *context)
  82. {
  83. // Get player context
  84. UNUSED(manager);
  85. PlayerContext *player = context;
  86. // Get player position
  87. Vector pos = entity_pos_get(self);
  88. // Draw background (updates camera_x and camera_y)
  89. background_render(canvas, pos);
  90. // Draw player sprite relative to camera
  91. canvas_draw_sprite(canvas, player->sprite, pos.x - camera_x - 5, pos.y - camera_y - 5);
  92. }
  93. static const EntityDescription player_desc = {
  94. .start = NULL, // called when entity is added to the level
  95. .stop = NULL, // called when entity is removed from the level
  96. .update = player_update, // called every frame
  97. .render = player_render, // called every frame, after update
  98. .collision = NULL, // called when entity collides with another entity
  99. .event = NULL, // called when entity receives an event
  100. .context_size = sizeof(PlayerContext), // size of entity context, will be automatically allocated and freed
  101. };
  102. /****** Level ******/
  103. static void level_alloc(Level *level, GameManager *manager, void *context)
  104. {
  105. UNUSED(manager);
  106. UNUSED(context);
  107. // Add player entity to the level
  108. player_spawn(level, manager);
  109. draw_tree_world(level);
  110. // draw_example_world(level);
  111. }
  112. static const LevelBehaviour level = {
  113. .alloc = level_alloc, // called once, when level allocated
  114. .free = NULL, // called once, when level freed
  115. .start = NULL, // called when level is changed to this level
  116. .stop = NULL, // called when level is changed from this level
  117. .context_size = 0, // size of level context, will be automatically allocated and freed
  118. };
  119. // Forward declaration of icon_desc
  120. static const EntityDescription icon_desc;
  121. static void icon_collision(Entity *self, Entity *other, GameManager *manager, void *context)
  122. {
  123. UNUSED(manager);
  124. UNUSED(self);
  125. IconContext *icon = (IconContext *)context;
  126. UNUSED(icon);
  127. if (entity_description_get(other) == &player_desc)
  128. {
  129. PlayerContext *player = (PlayerContext *)entity_context_get(other);
  130. if (player)
  131. {
  132. Vector pos = entity_pos_get(other);
  133. // Bounce the player back by 3 units opposite their last movement direction
  134. pos.x -= player->dx * 3;
  135. pos.y -= player->dy * 3;
  136. entity_pos_set(other, pos);
  137. }
  138. }
  139. }
  140. static void icon_render(Entity *self, GameManager *manager, Canvas *canvas, void *context)
  141. {
  142. UNUSED(manager);
  143. IconContext *icon_ctx = (IconContext *)context;
  144. Vector pos = entity_pos_get(self);
  145. canvas_draw_icon(canvas, pos.x - camera_x - 8, pos.y - camera_y - 8, icon_ctx->icon);
  146. }
  147. static void icon_start(Entity *self, GameManager *manager, void *context)
  148. {
  149. UNUSED(manager);
  150. UNUSED(context);
  151. // Just add the collision rectangle for 16x16 icon
  152. entity_collider_add_rect(self, 16, 16);
  153. }
  154. static const EntityDescription icon_desc = {
  155. .start = icon_start,
  156. .stop = NULL,
  157. .update = NULL,
  158. .render = icon_render,
  159. .collision = icon_collision,
  160. .event = NULL,
  161. .context_size = sizeof(IconContext),
  162. };
  163. // Helper function to spawn an icon entity at a given position
  164. void spawn_icon(Level *level, const Icon *icon, float x, float y, uint8_t width, uint8_t height)
  165. {
  166. Entity *e = level_add_entity(level, &icon_desc);
  167. IconContext *icon_ctx = entity_context_get(e);
  168. icon_ctx->icon = icon;
  169. icon_ctx->width = width;
  170. icon_ctx->height = height;
  171. // Set the entity position to the center of the icon
  172. entity_pos_set(e, (Vector){x + 8, y + 8});
  173. }
  174. /****** Game ******/
  175. /*
  176. Write here the start code for your game, for example: creating a level and so on.
  177. Game context is allocated (game.context_size) and passed to this function, you can use it to store your game data.
  178. */
  179. static void game_start(GameManager *game_manager, void *ctx)
  180. {
  181. UNUSED(game_manager);
  182. // Do some initialization here, for example you can load score from storage.
  183. // For simplicity, we will just set it to 0.
  184. GameContext *game_context = ctx;
  185. game_context->score = 0;
  186. // Add level to the game
  187. game_manager_add_level(game_manager, &level);
  188. }
  189. /*
  190. Write here the stop code for your game, for example, freeing memory, if it was allocated.
  191. You don't need to free level, sprites or entities, it will be done automatically.
  192. Also, you don't need to free game_context, it will be done automatically, after this function.
  193. */
  194. static void game_stop(void *ctx)
  195. {
  196. UNUSED(ctx);
  197. // GameContext *game_context = ctx;
  198. // Do some deinitialization here, for example you can save score to storage.
  199. // For simplicity, we will just print it.
  200. // FURI_LOG_I("Game", "Your score: %lu", game_context->score);
  201. }
  202. /*
  203. Your game configuration, do not rename this variable, but you can change its content here.
  204. */
  205. const Game game = {
  206. .target_fps = 60, // target fps, game will try to keep this value
  207. .show_fps = false, // show fps counter on the screen
  208. .always_backlight = true, // keep display backlight always on
  209. .start = game_start, // will be called once, when game starts
  210. .stop = game_stop, // will be called once, when game stops
  211. .context_size = sizeof(GameContext), // size of game context
  212. };