#include void Deinit(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); Velocity *v = NULL; if (it->field_count >= 2) { v = ecs_field(it, Velocity, 2); } probe_iter(it); /* Write to validate columns point to valid memory */ int i; for (i = 0; i < it->count; i ++) { p[i].x = 0; p[i].y = 0; if (v) { v[i].x = 0; v[i].y = 0; } } } static void Remove_from_current(ecs_iter_t *it) { IterData *ctx = ecs_get_ctx(it->world); int i; for (i = 0; i < it->count; i ++) { ecs_entity_t e = it->entities[i]; if (ctx->component) { ecs_remove_id(it->world, e, ctx->component); } if (ctx->component_2) { ecs_remove_id(it->world, e, ctx->component_2); } ctx->entity_count ++; } } void TriggerOnRemove_remove(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_OBSERVER(world, Deinit, EcsOnRemove, Position); Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_int(ctx.count, 0); ecs_remove(world, e, Position); test_int(ctx.count, 1); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_id(Position)); test_int(ctx.s[0][0], 0); ecs_fini(world); } void TriggerOnRemove_remove_no_match(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_OBSERVER(world, Deinit, EcsOnRemove, Position); Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_int(ctx.count, 0); ecs_remove(world, e, Velocity); test_int(ctx.count, 0); ecs_fini(world); } void TriggerOnRemove_delete(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_OBSERVER(world, Deinit, EcsOnRemove, Position); Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_int(ctx.count, 0); ecs_delete(world, e); test_int(ctx.count, 1); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_id(Position)); test_int(ctx.s[0][0], 0); ecs_fini(world); } void TriggerOnRemove_delete_no_match(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_OBSERVER(world, Deinit, EcsOnRemove, Position); Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); ecs_entity_t e = ecs_new(world, Velocity); test_assert(e != 0); test_int(ctx.count, 0); ecs_delete(world, e); test_int(ctx.count, 0); ecs_fini(world); } static Position old_position = {0}; static void RemovePosition(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); test_assert(it->count == 1); old_position = p[0]; } void TriggerOnRemove_remove_watched(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_OBSERVER(world, RemovePosition, EcsOnRemove, Position); ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); test_assert(e != 0); /* Make parent entity watched */ ecs_new_w_pair(world, EcsChildOf, e); ecs_remove(world, e, Position); test_int(old_position.x, 10); test_int(old_position.y, 20); ecs_fini(world); } void TriggerOnRemove_delete_watched(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_OBSERVER(world, RemovePosition, EcsOnRemove, Position); ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); test_assert(e != 0); /* Make parent entity watched */ ecs_new_w_pair(world, EcsChildOf, e); ecs_delete(world, e); test_int(old_position.x, 10); test_int(old_position.y, 20); ecs_fini(world); } static bool dummy_called = false; static void Dummy(ecs_iter_t *it) { dummy_called = true; } void TriggerOnRemove_on_remove_in_on_update(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_SYSTEM(world, Remove_from_current, EcsOnUpdate, Position); ECS_OBSERVER(world, Dummy, EcsOnRemove, Velocity); IterData ctx = {.component = ecs_id(Velocity)}; ecs_set_ctx(world, &ctx, NULL); ECS_ENTITY(world, e1, Position, Velocity); ECS_ENTITY(world, e2, Position, Velocity); ECS_ENTITY(world, e3, Position, Velocity); ecs_progress(world, 1); test_assert( ecs_has(world, e1, Position)); test_assert( ecs_has(world, e2, Position)); test_assert( ecs_has(world, e3, Position)); test_assert( !ecs_has(world, e1, Velocity)); test_assert( !ecs_has(world, e2, Velocity)); test_assert( !ecs_has(world, e3, Velocity)); test_assert(dummy_called); ecs_fini(world); } static int dummy_dtor_invoked = 0; typedef struct DummyComp { int value; } DummyComp; static void RemoveDummyComp(ecs_iter_t *it) { int i; for (i = 0; i < it->count; i ++) { test_assert(ecs_is_valid(it->world, it->entities[i])); test_assert(ecs_is_alive(it->world, it->entities[i])); } dummy_dtor_invoked ++; } void TriggerOnRemove_valid_entity_after_delete(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, DummyComp); ECS_OBSERVER(world, RemoveDummyComp, EcsOnRemove, DummyComp); ecs_entity_t e = ecs_new(world, DummyComp); test_assert(e != 0); ecs_delete(world, e); test_assert(!ecs_is_valid(world, e)); test_assert(!ecs_is_alive(world, e)); test_int(dummy_dtor_invoked, 1); ecs_fini(world); } void TriggerOnRemove_remove_after_delete_trigger(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t trigger = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(Position), .events = {EcsOnRemove}, .callback = Dummy }); ecs_entity_t e1 = ecs_new(world, Position); test_assert(e1 != 0); test_assert(ecs_has(world, e1, Position)); test_int(dummy_called, 0); ecs_remove(world, e1, Position); test_int(dummy_called, 1); dummy_called = 0; ecs_delete(world, trigger); test_int(dummy_called, 0); ecs_entity_t e2 = ecs_new(world, Position); test_assert(e2 != 0); test_assert(ecs_has(world, e2, Position)); test_int(dummy_called, 0); ecs_remove(world, e2, Position); test_int(dummy_called, 0); ecs_fini(world); } void TriggerOnRemove_remove_after_delete_wildcard_id_trigger(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t trigger = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = EcsWildcard, .events = {EcsOnRemove}, .callback = Dummy }); ecs_entity_t e1 = ecs_new(world, Position); test_assert(e1 != 0); test_assert(ecs_has(world, e1, Position)); test_int(dummy_called, 0); ecs_remove(world, e1, Position); test_int(dummy_called, 1); dummy_called = 0; ecs_delete(world, trigger); test_int(dummy_called, 1); dummy_called = 0; ecs_entity_t e2 = ecs_new(world, Position); test_assert(e2 != 0); test_assert(ecs_has(world, e2, Position)); test_int(dummy_called, 0); ecs_remove(world, e2, Position); test_int(dummy_called, 0); ecs_fini(world); } typedef struct on_remove_has_tag_t { ecs_entity_t ent; ecs_entity_t tag; } on_remove_has_tag_t; static void OnRemoveHasTag(ecs_iter_t *it) { on_remove_has_tag_t *ctx = it->ctx; test_assert(ctx != NULL); test_int(it->count, 1); test_assert(it->entities[0] == ctx->ent); test_assert(ecs_field_id(it, 1) == ctx->tag); test_bool(ecs_has_id(it->world, ctx->ent, ctx->tag), true); dummy_called = true; } void TriggerOnRemove_has_removed_tag_trigger_1_tag(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); ecs_entity_t e = ecs_new(world, Tag); test_assert(e != 0); test_assert(ecs_has(world, e, Tag)); on_remove_has_tag_t ctx = { .ent = e, .tag = Tag }; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = Tag, .events = {EcsOnRemove}, .callback = OnRemoveHasTag, .ctx = &ctx }); ecs_remove(world, e, Tag); test_int(dummy_called, 1); ecs_fini(world); } void TriggerOnRemove_has_removed_tag_trigger_2_tags(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); ecs_entity_t e = ecs_new(world, TagA); test_assert(e != 0); test_assert(ecs_has(world, e, TagA)); ecs_add(world, e, TagB); test_assert(ecs_has(world, e, TagB)); on_remove_has_tag_t ctx = { .ent = e, .tag = TagA }; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnRemove}, .callback = OnRemoveHasTag, .ctx = &ctx }); ecs_remove(world, e, TagA); test_int(dummy_called, 1); ecs_fini(world); }