| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- #include "level.h"
- #include "level_i.h"
- #include "entity_i.h"
- #include <m-list.h>
- #include <furi.h>
- LIST_DEF(EntityList, Entity*, M_POD_OPLIST);
- #define M_OPL_EntityList_t() LIST_OPLIST(EntityList)
- #define FOREACH(name, list) for \
- M_EACH(name, list, EntityList_t)
- #define LEVEL_DEBUG(...) FURI_LOG_D("Level", __VA_ARGS__)
- #define LEVEL_ERROR(...) FURI_LOG_E("Level", __VA_ARGS__)
- struct Level {
- EntityList_t entities;
- EntityList_t to_add;
- EntityList_t to_remove;
- const LevelBehaviour* behaviour;
- void* context;
- GameManager* manager;
- };
- Level* level_alloc(const LevelBehaviour* behaviour, GameManager* manager) {
- Level* level = malloc(sizeof(Level));
- level->manager = manager;
- EntityList_init(level->entities);
- EntityList_init(level->to_add);
- EntityList_init(level->to_remove);
- level->behaviour = behaviour;
- if(behaviour->context_size > 0) {
- level->context = malloc(behaviour->context_size);
- } else {
- level->context = NULL;
- }
- LEVEL_DEBUG("Allocated level at %p", level);
- return level;
- }
- static void level_process_add(Level* level) {
- // move entities from to_add to entities
- FOREACH(item, level->to_add) {
- EntityList_push_back(level->entities, *item);
- }
- EntityList_clear(level->to_add);
- }
- static void level_process_remove(Level* level) {
- // remove entities in to_remove from entities and free them
- FOREACH(item, level->to_remove) {
- entity_free(*item);
- EntityList_it_t it;
- // find and remove the entity from the entities list
- for(EntityList_it(it, level->entities); !EntityList_end_p(it); EntityList_next(it)) {
- if(*EntityList_ref(it) == *item) {
- EntityList_remove(level->entities, it);
- break;
- }
- }
- }
- EntityList_clear(level->to_remove);
- }
- static bool level_entity_in_list_p(EntityList_t list, Entity* entity) {
- FOREACH(item, list) {
- if(*item == entity) {
- return true;
- }
- }
- return false;
- }
- void level_free(Level* level) {
- level_clear(level);
- EntityList_clear(level->entities);
- EntityList_clear(level->to_add);
- EntityList_clear(level->to_remove);
- if(level->behaviour->context_size > 0) {
- free(level->context);
- }
- LEVEL_DEBUG("Freeing level at %p", level);
- free(level);
- }
- void level_clear(Level* level) {
- size_t iterations = 0;
- do {
- // process to_add and to_remove
- level_process_add(level);
- level_process_remove(level);
- // remove entities from entities list
- FOREACH(item, level->entities) {
- if(!level_entity_in_list_p(level->to_remove, *item)) {
- level_remove_entity(level, *item);
- }
- }
- // check if we are looping too many times
- iterations++;
- if(iterations >= 100) {
- LEVEL_ERROR("Level free looped too many times");
- }
- // entity_call_stop can call level_remove_entity or level_add_entity
- // so we need to process to_add and to_remove again
- } while(!EntityList_empty_p(level->to_add) || !EntityList_empty_p(level->to_remove));
- }
- Entity* level_add_entity(Level* level, const EntityDescription* description) {
- Entity* entity = entity_alloc(description);
- EntityList_push_back(level->to_add, entity);
- entity_call_start(entity, level->manager);
- return entity;
- }
- void level_remove_entity(Level* level, Entity* entity) {
- EntityList_push_back(level->to_remove, entity);
- entity_call_stop(entity, level->manager);
- }
- void level_send_event(
- Level* level,
- Entity* sender,
- const EntityDescription* receiver_desc,
- uint32_t type,
- EntityEventValue value) {
- FOREACH(item, level->entities) {
- if(receiver_desc == entity_description_get(*item) || receiver_desc == NULL) {
- entity_send_event(sender, *item, level->manager, type, value);
- }
- }
- }
- static void level_process_update(Level* level, GameManager* manager) {
- FOREACH(item, level->entities) {
- entity_call_update(*item, manager);
- }
- }
- static void level_process_collision(Level* level, GameManager* manager) {
- EntityList_it_t it_first;
- EntityList_it_t it_second;
- EntityList_it(it_first, level->entities);
- while(!EntityList_end_p(it_first)) {
- Entity* first = *EntityList_ref(it_first);
- if(entity_collider_exists(first)) {
- // start second iterator at the next entity,
- // so we don't check the same pair twice
- EntityList_it_set(it_second, it_first);
- EntityList_next(it_second);
- while(!EntityList_end_p(it_second)) {
- Entity* second = *EntityList_ref(it_second);
- if(first->collider_dirty || second->collider_dirty) {
- if(entity_collider_exists(second)) {
- if(entity_collider_check_collision(first, second)) {
- entity_call_collision(first, second, manager);
- entity_call_collision(second, first, manager);
- }
- }
- }
- EntityList_next(it_second);
- }
- }
- EntityList_next(it_first);
- }
- FOREACH(item, level->entities) {
- (*item)->collider_dirty = false;
- }
- }
- void level_update(Level* level, GameManager* manager) {
- level_process_add(level);
- level_process_remove(level);
- level_process_update(level, manager);
- level_process_collision(level, manager);
- }
- void level_render(Level* level, GameManager* manager, Canvas* canvas) {
- FOREACH(item, level->entities) {
- entity_call_render(*item, manager, canvas);
- }
- }
- void level_call_start(Level* level) {
- if(level->behaviour->start) {
- level->behaviour->start(level, level->manager, level->context);
- }
- }
- void level_call_stop(Level* level) {
- if(level->behaviour->stop) {
- level->behaviour->stop(level, level->manager, level->context);
- }
- }
- void level_call_alloc(Level* level) {
- if(level->behaviour->alloc) {
- level->behaviour->alloc(level, level->manager, level->context);
- }
- }
- void level_call_free(Level* level) {
- if(level->behaviour->free) {
- level->behaviour->free(level, level->manager, level->context);
- }
- }
|