level.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. #include "level.h"
  2. #include "level_i.h"
  3. #include "entity_i.h"
  4. #include <m-list.h>
  5. #include <furi.h>
  6. LIST_DEF(EntityList, Entity*, M_POD_OPLIST);
  7. #define M_OPL_EntityList_t() LIST_OPLIST(EntityList)
  8. #define FOREACH(name, list) for \
  9. M_EACH(name, list, EntityList_t)
  10. #define LEVEL_DEBUG(...) FURI_LOG_D("Level", __VA_ARGS__)
  11. #define LEVEL_ERROR(...) FURI_LOG_E("Level", __VA_ARGS__)
  12. struct Level {
  13. EntityList_t entities;
  14. EntityList_t to_add;
  15. EntityList_t to_remove;
  16. LevelBehaviour behaviour;
  17. void* context;
  18. };
  19. Level* level_alloc(void) {
  20. Level* level = malloc(sizeof(Level));
  21. EntityList_init(level->entities);
  22. EntityList_init(level->to_add);
  23. EntityList_init(level->to_remove);
  24. level->behaviour = LEVEL_BEHAVIOUR_EMPTY;
  25. level->context = NULL;
  26. LEVEL_DEBUG("Allocated level at %p", level);
  27. return level;
  28. }
  29. static void level_process_add(Level* level) {
  30. // move entities from to_add to entities
  31. FOREACH(item, level->to_add) {
  32. EntityList_push_back(level->entities, *item);
  33. }
  34. EntityList_clear(level->to_add);
  35. }
  36. static void level_process_remove(Level* level) {
  37. // remove entities in to_remove from entities and free them
  38. FOREACH(item, level->to_remove) {
  39. entity_free(*item);
  40. EntityList_it_t it;
  41. // find and remove the entity from the entities list
  42. for(EntityList_it(it, level->entities); !EntityList_end_p(it); EntityList_next(it)) {
  43. if(*EntityList_ref(it) == *item) {
  44. EntityList_remove(level->entities, it);
  45. break;
  46. }
  47. }
  48. }
  49. EntityList_clear(level->to_remove);
  50. }
  51. static bool level_entity_in_list_p(EntityList_t list, Entity* entity) {
  52. FOREACH(item, list) {
  53. if(*item == entity) {
  54. return true;
  55. }
  56. }
  57. return false;
  58. }
  59. void level_free(Level* level) {
  60. size_t iterations = 0;
  61. do {
  62. // process to_add and to_remove
  63. level_process_add(level);
  64. level_process_remove(level);
  65. // remove entities from entities list
  66. FOREACH(item, level->entities) {
  67. if(!level_entity_in_list_p(level->to_remove, *item)) {
  68. level_remove_entity(level, *item);
  69. }
  70. }
  71. // check if we are looping too many times
  72. iterations++;
  73. if(iterations >= 100) {
  74. LEVEL_ERROR("Level free looped too many times");
  75. }
  76. // entity_call_stop can call level_remove_entity or level_add_entity
  77. // so we need to process to_add and to_remove again
  78. } while(!EntityList_empty_p(level->to_add) || !EntityList_empty_p(level->to_remove));
  79. EntityList_clear(level->entities);
  80. EntityList_clear(level->to_add);
  81. EntityList_clear(level->to_remove);
  82. LEVEL_DEBUG("Freeing level at %p", level);
  83. free(level);
  84. }
  85. void level_behaviour_set(Level* level, LevelBehaviour behaviour, void* context) {
  86. level->behaviour = behaviour;
  87. level->context = context;
  88. }
  89. Entity* level_add_entity(Level* level, const EntityDescription* behaviour) {
  90. Entity* entity = entity_alloc(behaviour);
  91. EntityList_push_back(level->to_add, entity);
  92. entity_call_start(level, entity);
  93. return entity;
  94. }
  95. void level_remove_entity(Level* level, Entity* entity) {
  96. EntityList_push_back(level->to_remove, entity);
  97. entity_call_stop(level, entity);
  98. }
  99. void level_update(Level* level, Director* director) {
  100. level_process_add(level);
  101. level_process_remove(level);
  102. FOREACH(item, level->entities) {
  103. entity_call_update(*item, director);
  104. }
  105. }
  106. void level_render(Level* level, Director* director, Canvas* canvas) {
  107. FOREACH(item, level->entities) {
  108. entity_call_render(*item, director, canvas);
  109. }
  110. }