#include static int on_remove_count = 0; static void Trigger(ecs_iter_t *it) { probe_system_w_ctx(it, it->ctx); } static void Trigger_w_value(ecs_iter_t *it) { probe_system_w_ctx(it, it->ctx); test_int(it->count, 1); test_assert(it->entities != NULL); test_assert(it->entities[0] != 0); Position *p = ecs_field(it, Position, 1); test_int(p->x, 10); test_int(p->y, 20); } static void Trigger_w_value_from_entity(ecs_iter_t *it) { probe_system_w_ctx(it, it->ctx); test_int(it->count, 1); test_assert(it->entities != NULL); test_assert(it->entities[0] == 0); test_assert(it->sources[0] != 0); Position *p = ecs_field(it, Position, 1); test_int(p->x, 10); test_int(p->y, 20); } static void Trigger_w_value_instanced(ecs_iter_t *it) { probe_system_w_ctx(it, it->ctx); test_assert(it->count > 1); Position *p = ecs_field(it, Position, 1); test_int(p->x, 10); test_int(p->y, 20); } static void Trigger_w_filter_term(ecs_iter_t *it) { probe_system_w_ctx(it, it->ctx); test_assert(it->entities != NULL); test_assert(it->entities[0] != 0); test_bool(it->flags & EcsIterNoData, true); } static void Trigger_n_w_values(ecs_iter_t *it) { probe_system_w_ctx(it, it->ctx); Position *p = ecs_field(it, Position, 1); test_assert(it->entities != NULL); int i; for (i = 0; i < it->count; i ++) { test_assert(it->entities[i] != 0); test_int(p[i].x, 10 + i * 20); test_int(p[i].y, 20 + i * 20); } } static void TriggerAdd(ecs_iter_t *it) { ecs_id_t id = *(ecs_id_t*)it->ctx; int i; for (i = 0; i < it->count; i ++) { ecs_add_id(it->world, it->entities[i], id); } } static void TriggerRemove(ecs_iter_t *it) { ecs_id_t id = *(ecs_id_t*)it->ctx; int i; for (i = 0; i < it->count; i ++) { ecs_remove_id(it->world, it->entities[i], id); } } static void TriggerClear(ecs_iter_t *it) { int i; for (i = 0; i < it->count; i ++) { ecs_clear(it->world, it->entities[i]); } } static void TriggerDelete(ecs_iter_t *it) { int i; for (i = 0; i < it->count; i ++) { ecs_delete(it->world, it->entities[i]); } } void Trigger_on_add_trigger_before_table(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); /* Create entity/table after trigger, should invoke trigger */ ecs_entity_t e = ecs_new(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_fini(world); } void Trigger_on_add_trigger_after_table(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); /* Create entity/before trigger */ ecs_entity_init(world, &(ecs_entity_desc_t){ .add = {TagA} }); /* Create trigger after table, should send notification to table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_new(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_fini(world); } void Trigger_on_remove_trigger_before_table(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); /* Create trigger after table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); /* Create entity/table after trigger, should invoke trigger */ ecs_entity_t e = ecs_new(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_remove_id(world, e, TagA); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_fini(world); } void Trigger_on_remove_trigger_after_table(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); /* Create entity/before trigger */ ecs_entity_t e = ecs_new(world, TagA); test_assert(e != 0); /* Create trigger after table, should send notification to table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_remove_id(world, e, TagA); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_fini(world); } void Trigger_on_add_tag(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_new(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_fini(world); } void Trigger_on_add_component(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(Position), .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, ecs_id(Position)); 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)); ecs_fini(world); } void Trigger_on_add_wildcard(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = EcsWildcard, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_new(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_os_zeromem(&ctx); ecs_add_id(world, e, TagB); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagB); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagB); ecs_fini(world); } void Trigger_on_add_pair(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Pred); ECS_TAG(world, Obj); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(Pred, Obj), .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .add = {ecs_pair(Pred, Obj)} }); test_assert(e != 0); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, ecs_pair(Pred, Obj)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(Pred, Obj)); ecs_fini(world); } void Trigger_on_add_pair_obj_wildcard(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Pred); ECS_TAG(world, ObjA); ECS_TAG(world, ObjB); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(Pred, EcsWildcard), .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .add = {ecs_pair(Pred, ObjA)} }); test_assert(e != 0); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, ecs_pair(Pred, ObjA)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(Pred, ObjA)); ecs_os_zeromem(&ctx); ecs_add_pair(world, e, Pred, ObjB); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, ecs_pair(Pred, ObjB)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(Pred, ObjB)); ecs_fini(world); } void Trigger_on_add_pair_pred_wildcard(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, PredA); ECS_TAG(world, PredB); ECS_TAG(world, Obj); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(EcsWildcard, Obj), .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .add = {ecs_pair(PredA, Obj)} }); test_assert(e != 0); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, ecs_pair(PredA, Obj)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(PredA, Obj)); ecs_os_zeromem(&ctx); ecs_add_pair(world, e, PredB, Obj); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, ecs_pair(PredB, Obj)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(PredB, Obj)); ecs_fini(world); } void Trigger_on_add_pair_wildcard(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Pred); ECS_TAG(world, Obj); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(EcsWildcard, EcsWildcard), .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .add = {ecs_pair(Pred, Obj)} }); test_assert(e != 0); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, ecs_pair(Pred, Obj)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(Pred, Obj)); ecs_fini(world); } void Trigger_on_add_any(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); ECS_TAG(world, Rel); ECS_TAG(world, Obj); Probe *ctx_any = ecs_os_calloc_t(Probe); ecs_entity_t t_any = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = EcsAny, .events = {EcsOnAdd}, .callback = Trigger, .ctx = ctx_any }); Probe *ctx_wc = ecs_os_calloc_t(Probe); ecs_entity_t t_wc = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = EcsWildcard, .events = {EcsOnAdd}, .callback = Trigger, .ctx = ctx_wc }); Probe *ctx_wc_pair = ecs_os_calloc_t(Probe); ecs_entity_t t_wc_pair = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(EcsWildcard, EcsWildcard), .events = {EcsOnAdd}, .callback = Trigger, .ctx = ctx_wc_pair }); ecs_os_zeromem(ctx_any); ecs_os_zeromem(ctx_wc); ecs_os_zeromem(ctx_wc_pair); ecs_new(world, Tag); test_int(ctx_any->invoked, 1); test_int(ctx_any->count, 1); test_int(ctx_any->system, t_any); test_int(ctx_any->event, EcsOnAdd); test_int(ctx_any->event_id, Tag); test_int(ctx_wc->invoked, 1); test_int(ctx_wc->count, 1); test_int(ctx_wc->system, t_wc); test_int(ctx_wc->event, EcsOnAdd); test_int(ctx_wc->event_id, Tag); test_int(ctx_wc_pair->invoked, 0); ecs_os_zeromem(ctx_any); ecs_os_zeromem(ctx_wc); ecs_os_zeromem(ctx_wc_pair); ecs_new_w_pair(world, Rel, Obj); test_int(ctx_any->invoked, 1); test_int(ctx_any->count, 1); test_int(ctx_any->system, t_any); test_int(ctx_any->event, EcsOnAdd); test_int(ctx_any->event_id, ecs_pair(Rel, Obj)); test_int(ctx_wc->invoked, 0); test_int(ctx_wc_pair->invoked, 1); test_int(ctx_wc_pair->count, 1); test_int(ctx_wc_pair->system, t_wc_pair); test_int(ctx_wc_pair->event, EcsOnAdd); test_int(ctx_wc_pair->event_id, ecs_pair(Rel, Obj)); ecs_fini(world); ecs_os_free(ctx_any); ecs_os_free(ctx_wc); ecs_os_free(ctx_wc_pair); } void Trigger_on_remove_tag(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_new(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_remove_id(world, e, TagA); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_fini(world); } void Trigger_on_remove_component(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(Position), .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_remove(world, e, Position); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, ecs_id(Position)); 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)); ecs_fini(world); } void Trigger_on_remove_wildcard(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = EcsWildcard, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .add = {TagA, TagB} }); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_remove(world, e, TagA); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_os_zeromem(&ctx); ecs_remove_id(world, e, TagB); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, TagB); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagB); ecs_fini(world); } void Trigger_on_remove_pair(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Pred); ECS_TAG(world, Obj); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(Pred, Obj), .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .add = {ecs_pair(Pred, Obj)} }); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_remove_pair(world, e, Pred, Obj); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, ecs_pair(Pred, Obj)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(Pred, Obj)); ecs_fini(world); } void Trigger_on_remove_pair_obj_wildcard(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Pred); ECS_TAG(world, ObjA); ECS_TAG(world, ObjB); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(Pred, EcsWildcard), .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .add = {ecs_pair(Pred, ObjA), ecs_pair(Pred, ObjB)} }); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_remove_pair(world, e, Pred, ObjA); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, ecs_pair(Pred, ObjA)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(Pred, ObjA)); ecs_os_zeromem(&ctx); ecs_add_pair(world, e, Pred, ObjB); ecs_remove_pair(world, e, Pred, ObjB); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, ecs_pair(Pred, ObjB)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(Pred, ObjB)); ecs_fini(world); } void Trigger_on_remove_pair_pred_wildcard(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, PredA); ECS_TAG(world, PredB); ECS_TAG(world, Obj); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(EcsWildcard, Obj), .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .add = {ecs_pair(PredA, Obj), ecs_pair(PredB, Obj)} }); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_remove_pair(world, e, PredA, Obj); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, ecs_pair(PredA, Obj)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(PredA, Obj)); ecs_os_zeromem(&ctx); ecs_remove_pair(world, e, PredB, Obj); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, ecs_pair(PredB, Obj)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(PredB, Obj)); ecs_fini(world); } void Trigger_on_remove_pair_wildcard(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Pred); ECS_TAG(world, Obj); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(EcsWildcard, EcsWildcard), .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .add = {ecs_pair(Pred, Obj)} }); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_remove_pair(world, e, Pred, Obj); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, ecs_pair(Pred, Obj)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(Pred, Obj)); // Delete trigger so it doesn't go crazy while shutting down the world ecs_delete(world, t); ecs_fini(world); } void Trigger_wildcard_pair_w_pred_component(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, ObjA); ECS_TAG(world, ObjB); ECS_COMPONENT(world, Position); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(ecs_id(Position), EcsWildcard), .events = {EcsOnSet}, .callback = Trigger_w_value, .ctx = &ctx }); test_assert(t != 0); ecs_entity_t e = ecs_new_id(world); test_int(ctx.invoked, 0); ecs_set_pair(world, e, Position, ObjA, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(ecs_id(Position), ObjA)); /* Change existing component without triggering OnSet as the callback * expects value {10, 20}, then add a new component with {10, 20} */ Position *p = ecs_get_mut_pair(world, e, Position, ObjA); test_assert(p != NULL); test_int(p->x, 10); test_int(p->y, 20); p->x = 30; p->y = 40; ecs_os_zeromem(&ctx); ecs_set_pair(world, e, Position, ObjB, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(ecs_id(Position), ObjB)); ecs_fini(world); } void Trigger_wildcard_pair_w_obj_component(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, RelA); ECS_TAG(world, RelB); ECS_COMPONENT(world, Position); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(EcsWildcard, ecs_id(Position)), .events = {EcsOnSet}, .callback = Trigger_w_value, .ctx = &ctx }); test_assert(t != 0); ecs_entity_t e = ecs_new_id(world); test_int(ctx.invoked, 0); ecs_set_pair_object(world, e, RelA, Position, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(RelA, ecs_id(Position))); /* Change existing component without triggering OnSet as the callback * expects value {10, 20}, then add a new component with {10, 20} */ Position *p = ecs_get_mut_pair_second(world, e, RelA, Position); test_assert(p != NULL); test_int(p->x, 10); test_int(p->y, 20); p->x = 30; p->y = 40; ecs_os_zeromem(&ctx); ecs_set_pair_object(world, e, RelB, Position, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(RelB, ecs_id(Position))); ecs_fini(world); } void Trigger_on_set_component(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(Position), .events = {EcsOnSet}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_set(world, e, Position, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.event_id, ecs_id(Position)); 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)); ecs_fini(world); } void Trigger_on_set_wildcard(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = EcsWildcard, .events = {EcsOnSet}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_set(world, e, Position, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.event_id, ecs_id(Position)); 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)); ecs_fini(world); } void Trigger_on_set_pair(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_TAG(world, Obj); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(ecs_id(Position), Obj), .events = {EcsOnSet}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .add = {ecs_pair(ecs_id(Position), Obj)} }); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_set_pair(world, e, Position, Obj, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.event_id, ecs_pair(ecs_id(Position), Obj)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(ecs_id(Position), Obj)); ecs_fini(world); } void Trigger_on_set_pair_w_obj_wildcard(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_TAG(world, Obj); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(ecs_id(Position), EcsWildcard), .events = {EcsOnSet}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .add = {ecs_pair(ecs_id(Position), Obj)} }); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_set_pair(world, e, Position, Obj, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.event_id, ecs_pair(ecs_id(Position), Obj)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(ecs_id(Position), Obj)); ecs_fini(world); } void Trigger_on_set_pair_pred_wildcard(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_TAG(world, Obj); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(EcsWildcard, Obj), .events = {EcsOnSet}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .add = {ecs_pair(ecs_id(Position), Obj)} }); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_set_pair(world, e, Position, Obj, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.event_id, ecs_pair(ecs_id(Position), Obj)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(ecs_id(Position), Obj)); ecs_fini(world); } void Trigger_on_set_pair_wildcard(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_TAG(world, Obj); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(EcsWildcard, EcsWildcard), .events = {EcsOnSet}, .callback = Trigger, .ctx = &ctx }); test_int(ctx.invoked, 1); /* Triggers for self */ ecs_os_zeromem(&ctx); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .add = {ecs_pair(ecs_id(Position), Obj)} }); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_set_pair(world, e, Position, Obj, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.event_id, ecs_pair(ecs_id(Position), Obj)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(ecs_id(Position), Obj)); ecs_fini(world); } void Trigger_on_add_remove(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnAdd, EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_new(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_os_zeromem(&ctx); ecs_remove_id(world, e, TagA); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_fini(world); } void Trigger_on_set_component_after_modified(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(Position), .events = {EcsOnSet}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_modified(world, e, Position); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.event_id, ecs_id(Position)); 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)); ecs_fini(world); } void Trigger_un_set_component(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(Position), .events = {EcsUnSet}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_remove(world, e, Position); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsUnSet); test_int(ctx.event_id, ecs_id(Position)); 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)); ecs_fini(world); } void Trigger_un_set_wildcard(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = EcsWildcard, .events = {EcsUnSet}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_remove(world, e, Position); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsUnSet); test_int(ctx.event_id, ecs_id(Position)); 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)); ecs_fini(world); } void Trigger_un_set_pair(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Rel); ECS_TAG(world, Obj); // UnSet only works on components ecs_set(world, Rel, EcsComponent, {.size = 1, .alignment = 1}); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(Rel, Obj), .events = {EcsUnSet}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .add = {ecs_pair(Rel, Obj)} }); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_remove_pair(world, e, Rel, Obj); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsUnSet); test_int(ctx.event_id, ecs_pair(Rel, Obj)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(Rel, Obj)); ecs_fini(world); } void Trigger_un_set_pair_w_obj_wildcard(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Rel); ECS_TAG(world, Obj); // UnSet only works on components ecs_set(world, Rel, EcsComponent, {.size = 1, .alignment = 1}); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(Rel, EcsWildcard), .events = {EcsUnSet}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .add = {ecs_pair(Rel, Obj)} }); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_remove_pair(world, e, Rel, Obj); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsUnSet); test_int(ctx.event_id, ecs_pair(Rel, Obj)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(Rel, Obj)); ecs_fini(world); } void Trigger_un_set_pair_pred_wildcard(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Rel); ECS_TAG(world, Obj); // UnSet only works on components ecs_set(world, Rel, EcsComponent, {.size = 1, .alignment = 1}); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(EcsWildcard, Obj), .events = {EcsUnSet}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .add = {ecs_pair(Rel, Obj)} }); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_remove_pair(world, e, Rel, Obj); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsUnSet); test_int(ctx.event_id, ecs_pair(Rel, Obj)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(Rel, Obj)); ecs_fini(world); } void Trigger_un_set_pair_wildcard(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Rel); ECS_TAG(world, Obj); // UnSet only works on components ecs_set(world, Rel, EcsComponent, {.size = 1, .alignment = 1}); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(EcsWildcard, EcsWildcard), .events = {EcsUnSet}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .add = {ecs_pair(Rel, Obj)} }); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_remove_pair(world, e, Rel, Obj); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsUnSet); test_int(ctx.event_id, ecs_pair(Rel, Obj)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_pair(Rel, Obj)); // Delete trigger so it doesn't go crazy while shutting down the world ecs_delete(world, t); ecs_fini(world); } void Trigger_on_add_not_tag(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .filter.terms[0].oper = EcsNot, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_new(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_remove(world, e, TagA); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_os_zeromem(&ctx); ecs_add_id(world, e, TagA); test_int(ctx.invoked, 0); ecs_fini(world); } void Trigger_on_remove_not_tag(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .filter.terms[0].oper = EcsNot, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_new(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_os_zeromem(&ctx); ecs_remove(world, e, TagA); test_int(ctx.invoked, 0); ecs_fini(world); } void Trigger_on_add_superset(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .filter.terms[0].src.flags = EcsUp, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t base_no_comp = ecs_new_id(world); test_int(ctx.invoked, 0); ecs_entity_t base = ecs_new(world, TagA); test_int(ctx.invoked, 0); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base_no_comp); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsIsA, base); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); test_int(ctx.s[0][0], base); ecs_os_zeromem(&ctx); ecs_add(world, e, TagA); test_int(ctx.invoked, 0); ecs_fini(world); } void Trigger_on_add_superset_2_levels(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .filter.terms[0].src.flags = EcsUp, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t base_no_comp = ecs_new_id(world); test_int(ctx.invoked, 0); ecs_entity_t base_of_base = ecs_new(world, TagA); test_int(ctx.invoked, 0); ecs_entity_t base = ecs_new_w_pair(world, EcsIsA, base_of_base); test_int(ctx.invoked, 1); ctx = (Probe){0}; ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base_no_comp); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsIsA, base); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); test_int(ctx.s[0][0], base_of_base); ecs_os_zeromem(&ctx); ecs_add(world, e, TagA); test_int(ctx.invoked, 0); ecs_fini(world); } void Trigger_on_remove_superset(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .filter.terms[0].src.flags = EcsUp, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t base_no_comp = ecs_new_id(world); test_int(ctx.invoked, 0); ecs_entity_t base = ecs_new(world, TagA); test_int(ctx.invoked, 0); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base_no_comp); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsIsA, base); test_int(ctx.invoked, 0); ecs_remove_pair(world, e, EcsIsA, base); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_os_zeromem(&ctx); ecs_add(world, e, TagA); test_int(ctx.invoked, 0); ecs_fini(world); } void Trigger_on_add_superset_childof(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { .id = TagA, .src.trav = EcsChildOf, .src.flags = EcsUp }, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t base_no_comp = ecs_new_id(world); test_int(ctx.invoked, 0); ecs_entity_t base = ecs_new(world, TagA); test_int(ctx.invoked, 0); ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, base_no_comp); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsChildOf, base); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_os_zeromem(&ctx); ecs_add(world, e, TagA); test_int(ctx.invoked, 0); ecs_fini(world); } void Trigger_on_remove_superset_childof(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { .id = TagA, .src.trav = EcsChildOf, .src.flags = EcsUp }, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t base_no_comp = ecs_new_id(world); test_int(ctx.invoked, 0); ecs_entity_t base = ecs_new(world, TagA); test_int(ctx.invoked, 0); ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, base_no_comp); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsChildOf, base); test_int(ctx.invoked, 0); ecs_remove_pair(world, e, EcsChildOf, base); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_os_zeromem(&ctx); ecs_add(world, e, TagA); test_int(ctx.invoked, 0); ecs_fini(world); } void Trigger_on_add_self_superset(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .filter.terms[0].src.flags = EcsSelf | EcsUp, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t base_no_comp = ecs_new_id(world); test_int(ctx.invoked, 0); ecs_entity_t base = ecs_new(world, TagA); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], base); test_int(ctx.c[0][0], TagA); ecs_os_zeromem(&ctx); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base_no_comp); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsIsA, base); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_os_zeromem(&ctx); ecs_add(world, e, TagA); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_os_zeromem(&ctx); ecs_remove_pair(world, e, EcsIsA, base); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsIsA, base); test_int(ctx.invoked, 0); ecs_fini(world); } void Trigger_on_remove_self_superset(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .filter.terms[0].src.flags = EcsSelf | EcsUp, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t base_no_comp = ecs_new_id(world); test_int(ctx.invoked, 0); ecs_entity_t base = ecs_new(world, TagA); test_int(ctx.invoked, 0); ecs_os_zeromem(&ctx); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base_no_comp); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsIsA, base); test_int(ctx.invoked, 0); ecs_remove_pair(world, e, EcsIsA, base); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_os_zeromem(&ctx); ecs_add(world, e, TagA); test_int(ctx.invoked, 0); ecs_remove(world, e, TagA); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_os_zeromem(&ctx); ecs_add(world, e, TagA); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsIsA, base); test_int(ctx.invoked, 0); ecs_remove_pair(world, e, EcsIsA, base); test_int(ctx.invoked, 0); ecs_fini(world); } void Trigger_add_twice(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_new(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_os_zeromem(&ctx); ecs_add_id(world, e, TagA); test_int(ctx.invoked, 0); ecs_fini(world); } void Trigger_remove_twice(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_new(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_remove_id(world, e, TagA); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_os_zeromem(&ctx); ecs_remove_id(world, e, TagA); test_int(ctx.invoked, 0); ecs_fini(world); } void Trigger_on_remove_w_clear(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_new(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_clear(world, e); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_fini(world); } void Trigger_on_remove_w_delete(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_new(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_delete(world, e); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_fini(world); } void Trigger_on_remove_w_world_fini(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_new(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_fini(world); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); } void Trigger_on_add_w_clone(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_new(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_os_zeromem(&ctx); ecs_entity_t e2 = ecs_clone(world, 0, e, true); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e2); test_int(ctx.c[0][0], TagA); ecs_fini(world); } void Trigger_add_in_trigger(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = TriggerAdd, .ctx = &TagB }); ecs_entity_t e = ecs_new(world, TagA); test_assert(e != 0); test_assert(ecs_has_id(world, e, TagB)); ecs_fini(world); } void Trigger_remove_in_trigger(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = TriggerRemove, .ctx = &TagB }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .add = {TagA, TagB} }); test_assert(e != 0); test_assert(!ecs_has_id(world, e, TagB)); ecs_fini(world); } void Trigger_clear_in_trigger(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = TriggerClear, }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .add = {TagA, TagB} }); test_assert(e != 0); test_assert(!ecs_has_id(world, e, TagA)); test_assert(!ecs_has_id(world, e, TagB)); ecs_fini(world); } void Trigger_delete_in_trigger(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = TriggerDelete, }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .add = {TagA, TagB} }); test_assert(e != 0); test_assert(!ecs_is_alive(world, e)); ecs_fini(world); } void Trigger_trigger_w_named_entity(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); /* Create trigger before table */ Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .entity = ecs_entity(world, {.name = "MyTrigger"}), .filter.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); /* Create entity/table after trigger, should invoke trigger */ ecs_entity_t e = ecs_new(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 1); ecs_fini(world); } void RemoveSelf(ecs_iter_t *it) { Self *s = ecs_field(it, Self, 1); test_assert(s != NULL); ecs_id_t ecs_id(Self) = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { test_assert(s[i].value == it->entities[i]); const Self *ptr = ecs_get(it->world, it->entities[i], Self); test_assert(ptr != NULL); test_assert(ptr->value == it->entities[i]); // Set to 0 so that if an entity were to get matched twice, it wouldn't // silently succeed. s[i].value = 0; on_remove_count ++; } } void Trigger_on_remove_tree(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Self); ECS_OBSERVER(world, RemoveSelf, EcsOnRemove, Self); ecs_entity_t root = ecs_new_id(world); ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, root); ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, e1); ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, e1); ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, e2); ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, root); ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, e6); ecs_entity_t e8 = ecs_new_w_pair(world, EcsChildOf, e6); ecs_entity_t e9 = ecs_new_w_pair(world, EcsChildOf, e7); ecs_entity_t e10 = ecs_new_w_pair(world, EcsChildOf, e7); ecs_set(world, e1, Self, {e1}); ecs_set(world, e2, Self, {e2}); ecs_set(world, e3, Self, {e3}); ecs_set(world, e4, Self, {e4}); ecs_set(world, e5, Self, {e5}); ecs_set(world, e6, Self, {e6}); ecs_set(world, e7, Self, {e7}); ecs_set(world, e8, Self, {e8}); ecs_set(world, e9, Self, {e9}); ecs_set(world, e10, Self, {e10}); ecs_delete(world, root); test_int(on_remove_count, 10); test_assert(!ecs_is_alive(world, e1)); test_assert(!ecs_is_alive(world, e2)); test_assert(!ecs_is_alive(world, e3)); test_assert(!ecs_is_alive(world, e4)); test_assert(!ecs_is_alive(world, e5)); test_assert(!ecs_is_alive(world, e6)); test_assert(!ecs_is_alive(world, e7)); test_assert(!ecs_is_alive(world, e8)); test_assert(!ecs_is_alive(world, e9)); test_assert(!ecs_is_alive(world, e10)); ecs_fini(world); } void Trigger_set_get_context(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); int32_t ctx_a, ctx_b; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .entity = ecs_entity(world, {.name = "MyTrigger"}), .filter.terms[0].id = Tag, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx_a }); test_assert(t != 0); test_assert(ecs_observer_get_ctx(world, t) == &ctx_a); test_assert(ecs_observer_init(world, &(ecs_observer_desc_t){ .entity = t, .ctx = &ctx_b }) == t); test_assert(ecs_observer_get_ctx(world, t) == &ctx_b); ecs_fini(world); } void Trigger_set_get_binding_context(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); int32_t ctx_a, ctx_b; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .entity = ecs_entity(world, {.name = "MyTrigger"}), .filter.terms[0].id = Tag, .events = {EcsOnAdd}, .callback = Trigger, .binding_ctx = &ctx_a }); test_assert(t != 0); test_assert(ecs_observer_get_binding_ctx(world, t) == &ctx_a); test_assert(ecs_observer_init(world, &(ecs_observer_desc_t){ .entity = t, .binding_ctx = &ctx_b }) == t); test_assert(ecs_observer_get_binding_ctx(world, t) == &ctx_b); ecs_fini(world); } static int ctx_value; static void ctx_free(void *ctx) { test_assert(&ctx_value == ctx); ctx_value ++; } static int binding_ctx_value; static void binding_ctx_free(void *ctx) { test_assert(&binding_ctx_value == ctx); binding_ctx_value ++; } void Trigger_delete_trigger_w_delete_ctx(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = Tag, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx_value, .ctx_free = ctx_free, .binding_ctx = &binding_ctx_value, .binding_ctx_free = binding_ctx_free }); test_assert(t != 0); test_assert(ecs_observer_get_ctx(world, t) == &ctx_value); test_assert(ecs_observer_get_binding_ctx(world, t) == &binding_ctx_value); ecs_delete(world, t); test_int(ctx_value, 1); test_int(binding_ctx_value, 1); ecs_fini(world); } void Trigger_trigger_w_index(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = Tag, .term_index = 50, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); test_assert(t != 0); ecs_new(world, Tag); test_int(ctx.invoked, 1); test_int(ctx.term_index, 50); ecs_fini(world); } static ecs_table_t *trigger_table; void TypeTrigger(ecs_iter_t *it) { trigger_table = it->table; } void Trigger_iter_type_set(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = Tag, .events = {EcsOnAdd}, .callback = TypeTrigger, .ctx = &ctx }); test_assert(t != 0); ecs_new(world, Tag); test_assert(trigger_table != NULL); const ecs_type_t *type = ecs_table_get_type(trigger_table); test_int(type->count, 1); test_int(type->array[0], Tag); ecs_fini(world); } void TriggerReadonly(ecs_iter_t *it) { probe_system_w_ctx(it, it->ctx); test_bool(ecs_field_is_readonly(it, 1), true); } void Trigger_readonly_term(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { TagA, .inout = EcsIn }, .events = {EcsOnAdd}, .callback = TriggerReadonly, .ctx = &ctx }); ecs_entity_t e = ecs_new_id(world); test_assert(e != 0); ecs_add(world, e, TagA); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.c[0][0], TagA); ecs_fini(world); } void Trigger_trigger_on_prefab(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx_1 = {0}; Probe ctx_2 = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { TagA }, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx_1 }); ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms = {{ TagA }, { EcsPrefab, .oper = EcsOptional }}, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx_2 }); ecs_new(world, TagA); test_int(ctx_1.invoked, 1); test_int(ctx_2.invoked, 1); ecs_os_zeromem(&ctx_1); ecs_os_zeromem(&ctx_2); ecs_entity_t e = ecs_new_w_id(world, EcsPrefab); test_int(ctx_1.invoked, 0); test_int(ctx_2.invoked, 0); ecs_add(world, e, TagA); test_int(ctx_1.invoked, 0); test_int(ctx_2.invoked, 1); ecs_os_zeromem(&ctx_2); e = ecs_new_w_id(world, EcsDisabled); test_int(ctx_1.invoked, 0); test_int(ctx_2.invoked, 0); ecs_add(world, e, TagA); test_int(ctx_1.invoked, 0); test_int(ctx_2.invoked, 0); ecs_fini(world); } void Trigger_trigger_on_disabled(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx_1 = {0}; Probe ctx_2 = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { TagA }, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx_1 }); ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms = {{ TagA }, { EcsDisabled, .oper = EcsOptional }}, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx_2 }); ecs_new(world, TagA); test_int(ctx_1.invoked, 1); test_int(ctx_2.invoked, 1); ecs_os_zeromem(&ctx_1); ecs_os_zeromem(&ctx_2); ecs_entity_t e = ecs_new_w_id(world, EcsPrefab); test_int(ctx_1.invoked, 0); test_int(ctx_2.invoked, 0); ecs_add(world, e, TagA); test_int(ctx_1.invoked, 0); test_int(ctx_2.invoked, 0); ecs_os_zeromem(&ctx_2); e = ecs_new_w_id(world, EcsDisabled); test_int(ctx_1.invoked, 0); test_int(ctx_2.invoked, 0); ecs_add(world, e, TagA); test_int(ctx_1.invoked, 0); test_int(ctx_2.invoked, 1); ecs_fini(world); } void Trigger_trigger_on_prefab_tag(void) { ecs_world_t *world = ecs_mini(); Probe ctx_1 = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { EcsPrefab }, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx_1 }); ecs_new_w_id(world, EcsPrefab); test_int(ctx_1.invoked, 1); ecs_fini(world); } void Trigger_trigger_on_disabled_tag(void) { ecs_world_t *world = ecs_mini(); Probe ctx_1 = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { EcsDisabled }, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx_1 }); ecs_new_w_id(world, EcsDisabled); test_int(ctx_1.invoked, 1); ecs_fini(world); } void Trigger_trigger_cleanup_2_w_self_super_id(void) { ecs_world_t * world = ecs_mini(); ECS_TAG(world, Tag); ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { .id = Tag, .src.flags = EcsUp }, .events = { EcsOnAdd }, .callback = Trigger }); ecs_entity_t t2 = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { Tag }, .events = { EcsOnAdd }, .callback = Trigger }); test_assert(t1 != 0); test_assert(t2 != 0); ecs_fini(world); /* Ensure two triggers for Tag and Tag(up) are cleaned up correctly */ } void Trigger_on_add_yield_existing(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); /* Create entities before trigger */ ecs_entity_t e1 = ecs_new(world, Tag); ecs_entity_t e2 = ecs_new(world, Tag); ecs_entity_t e3 = ecs_new(world, Tag); test_assert(e1 != 0); test_assert(e2 != 0); test_assert(e3 != 0); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = Tag, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx, .yield_existing = true }); test_int(ctx.invoked, 1); test_int(ctx.count, 3); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, Tag); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e1); test_int(ctx.e[1], e2); test_int(ctx.e[2], e3); test_int(ctx.c[0][0], Tag); // Ensure normal triggering also still works ecs_os_zeromem(&ctx); ecs_new(world, Tag); test_int(ctx.invoked, 1); ecs_fini(world); } void Trigger_on_add_yield_existing_2_tables(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); /* Create entities before trigger */ ecs_entity_t e1 = ecs_new(world, TagA); ecs_entity_t e2 = ecs_new(world, TagA); ecs_entity_t e3 = ecs_new(world, TagA); test_assert(e1 != 0); test_assert(e2 != 0); test_assert(e3 != 0); ecs_add(world, e3, TagB); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx, .yield_existing = true }); test_int(ctx.invoked, 2); test_int(ctx.count, 3); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e1); test_int(ctx.e[1], e2); test_int(ctx.e[2], e3); test_int(ctx.c[0][0], TagA); test_int(ctx.c[1][0], TagA); // Ensure normal triggering also still works ecs_os_zeromem(&ctx); ecs_new(world, TagA); test_int(ctx.invoked, 1); ecs_fini(world); } void Trigger_on_add_yield_existing_wildcard_pair(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Rel); ECS_TAG(world, ObjA); ECS_TAG(world, ObjB); /* Create entities before trigger */ ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjA); ecs_entity_t e3 = ecs_new_w_pair(world, Rel, ObjB); test_assert(e1 != 0); test_assert(e2 != 0); test_assert(e3 != 0); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(Rel, EcsWildcard), .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx, .yield_existing = true }); test_int(ctx.invoked, 2); test_int(ctx.count, 3); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, ecs_pair(Rel, ObjB)); /* last triggered id */ test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e1); test_int(ctx.e[1], e2); test_int(ctx.e[2], e3); test_int(ctx.c[0][0], ecs_pair(Rel, ObjA)); test_int(ctx.c[1][0], ecs_pair(Rel, ObjB)); // Ensure normal triggering also still works ecs_os_zeromem(&ctx); ecs_new_w_pair(world, Rel, ObjA); test_int(ctx.invoked, 1); ecs_fini(world); } void Trigger_on_set_yield_existing(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); /* Create entities before trigger */ ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); ecs_entity_t e2 = ecs_set(world, 0, Position, {30, 40}); ecs_entity_t e3 = ecs_set(world, 0, Position, {50, 60}); test_assert(e1 != 0); test_assert(e2 != 0); test_assert(e3 != 0); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(Position), .events = {EcsOnSet}, .callback = Trigger_n_w_values, .ctx = &ctx, .yield_existing = true }); test_int(ctx.invoked, 1); test_int(ctx.count, 3); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.event_id, ecs_id(Position)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e1); test_int(ctx.e[1], e2); test_int(ctx.e[2], e3); test_int(ctx.c[0][0], ecs_id(Position)); // Ensure normal triggering also still works ecs_os_zeromem(&ctx); ecs_entity_t e = ecs_new(world, Position); test_int(ctx.invoked, 0); ecs_set(world, e, Position, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.event, EcsOnSet); ecs_fini(world); } void Trigger_filter_term(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { .id = ecs_id(Position), .inout = EcsInOutNone }, .events = {EcsOnSet}, .callback = Trigger_w_filter_term, .ctx = &ctx }); /* Create entities before trigger */ ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); test_assert(e1 != 0); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.event_id, ecs_id(Position)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e1); test_int(ctx.c[0][0], ecs_id(Position)); ecs_fini(world); } void Trigger_on_add_remove_after_exclusive_add(void) { ecs_world_t *world = ecs_mini(); ECS_ENTITY(world, Rel, Exclusive); ECS_TAG(world, ObjA); ECS_TAG(world, ObjB); Probe ctx_add = {0}; ecs_entity_t t_add = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(Rel, EcsWildcard), .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx_add }); Probe ctx_remove = {0}; ecs_entity_t t_remove = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(Rel, EcsWildcard), .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx_remove }); ecs_entity_t e = ecs_new_id(world); ecs_add_pair(world, e, Rel, ObjA); test_assert( ecs_has_pair(world, e, Rel, ObjA)); test_int(ctx_add.invoked, 1); test_int(ctx_add.count, 1); test_int(ctx_add.system, t_add); test_int(ctx_add.event, EcsOnAdd); test_int(ctx_add.event_id, ecs_pair(Rel, ObjA)); test_int(ctx_add.term_count, 1); test_null(ctx_add.param); test_int(ctx_remove.invoked, 0); ecs_os_zeromem(&ctx_add); ecs_add_pair(world, e, Rel, ObjB); test_assert( ecs_has_pair(world, e, Rel, ObjB)); test_assert( !ecs_has_pair(world, e, Rel, ObjA)); test_int(ctx_add.invoked, 1); test_int(ctx_add.count, 1); test_int(ctx_add.system, t_add); test_int(ctx_add.event, EcsOnAdd); test_int(ctx_add.event_id, ecs_pair(Rel, ObjB)); test_int(ctx_add.term_count, 1); test_null(ctx_add.param); test_int(ctx_remove.invoked, 1); test_int(ctx_remove.count, 1); test_int(ctx_remove.system, t_remove); test_int(ctx_remove.event, EcsOnRemove); test_int(ctx_remove.event_id, ecs_pair(Rel, ObjA)); test_int(ctx_remove.term_count, 1); test_null(ctx_remove.param); ecs_fini(world); } void Trigger_on_add_base(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, /* Implicitly also listens to IsA */ .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx.invoked, 0); ecs_add(world, base, TagA); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); ecs_os_zeromem(&ctx); ecs_add(world, base, TagA); ecs_add(world, base, TagB); test_int(ctx.invoked, 0); ecs_fini(world); test_int(ctx.invoked, 0); } void Trigger_on_remove_base(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, /* Implicitly also listens to IsA */ .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx.invoked, 0); ecs_add(world, base, TagA); ecs_add(world, base, TagB); test_int(ctx.invoked, 0); ecs_remove(world, base, TagA); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); ecs_os_zeromem(&ctx); ecs_remove(world, base, TagA); ecs_remove(world, base, TagB); test_int(ctx.invoked, 0); ecs_fini(world); test_int(ctx.invoked, 0); } void Trigger_on_set_base(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(Position), /* Implicitly also listens to IsA */ .events = {EcsOnSet}, .callback = Trigger_w_value, .ctx = &ctx }); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx.invoked, 0); ecs_set(world, base, Position, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.event_id, ecs_id(Position)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); ecs_os_zeromem(&ctx); ecs_set(world, base, Position, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.event_id, ecs_id(Position)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); ecs_os_zeromem(&ctx); ecs_set(world, base, Velocity, {1, 2}); test_int(ctx.invoked, 0); ecs_fini(world); test_int(ctx.invoked, 0); } void Trigger_on_unset_base(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(Position), /* Implicitly also listens to IsA */ .events = {EcsUnSet}, .callback = Trigger_w_value, .ctx = &ctx }); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx.invoked, 0); ecs_set(world, base, Position, {10, 20}); ecs_set(world, base, Velocity, {1, 2}); test_int(ctx.invoked, 0); ecs_remove(world, base, Position); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsUnSet); test_int(ctx.event_id, ecs_id(Position)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); ecs_os_zeromem(&ctx); ecs_remove(world, base, Position); ecs_remove(world, base, Velocity); test_int(ctx.invoked, 0); ecs_fini(world); test_int(ctx.invoked, 0); } void Trigger_on_add_base_superset_trigger(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .filter.terms[0].src.flags = EcsUp, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx.invoked, 0); ecs_add(world, base, TagA); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); ecs_os_zeromem(&ctx); ecs_add(world, base, TagA); ecs_add(world, base, TagB); test_int(ctx.invoked, 0); ecs_fini(world); } void Trigger_on_add_base_superset_trigger_2_lvls(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .filter.terms[0].src.flags = EcsUp, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t base_of_base = ecs_new_w_id(world, EcsPrefab); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_add_pair(world, base, EcsIsA, base_of_base); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx.invoked, 0); ecs_add(world, base_of_base, TagA); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); ecs_os_zeromem(&ctx); ecs_add(world, base_of_base, TagA); ecs_add(world, base_of_base, TagB); test_int(ctx.invoked, 0); ecs_fini(world); test_int(ctx.invoked, 0); } void Trigger_on_add_base_2_entities(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, /* Implicitly also listens to IsA */ .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx.invoked, 0); ecs_add(world, base, TagA); test_int(ctx.invoked, 1); test_int(ctx.count, 2); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e1); test_int(ctx.e[1], e2); ecs_os_zeromem(&ctx); ecs_add(world, base, TagA); ecs_add(world, base, TagB); test_int(ctx.invoked, 0); ecs_fini(world); test_int(ctx.invoked, 0); } void Trigger_on_add_base_2_entities_filter(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, /* Implicitly also listens to IsA */ .filter.terms[0].inout = EcsInOutNone, .events = {EcsOnAdd}, .callback = Trigger_w_filter_term, .ctx = &ctx }); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx.invoked, 0); ecs_add(world, base, TagA); test_int(ctx.invoked, 1); test_int(ctx.count, 2); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e1); test_int(ctx.e[1], e2); ecs_os_zeromem(&ctx); ecs_add(world, base, TagA); ecs_add(world, base, TagB); test_int(ctx.invoked, 0); ecs_fini(world); test_int(ctx.invoked, 0); } void Trigger_on_set_base_w_value_2_entities(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(Position), /* Implicitly also listens to IsA */ .events = {EcsOnSet}, .callback = Trigger_w_value, .ctx = &ctx }); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx.invoked, 0); ecs_set(world, base, Position, {10, 20}); test_int(ctx.invoked, 2); test_int(ctx.count, 2); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.event_id, ecs_id(Position)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e1); test_int(ctx.e[1], e2); ecs_os_zeromem(&ctx); ecs_set(world, base, Position, {10, 20}); test_int(ctx.invoked, 2); test_int(ctx.count, 2); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.event_id, ecs_id(Position)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e1); test_int(ctx.e[1], e2); ecs_os_zeromem(&ctx); ecs_set(world, base, Velocity, {1, 2}); test_int(ctx.invoked, 0); ecs_fini(world); } void Trigger_on_set_base_w_value_2_entities_instanced(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(Position), /* Implicitly also listens to IsA */ .filter.instanced = true, .events = {EcsOnSet}, .callback = Trigger_w_value_instanced, .ctx = &ctx }); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx.invoked, 0); ecs_set(world, base, Position, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 2); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.event_id, ecs_id(Position)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e1); test_int(ctx.e[1], e2); ecs_os_zeromem(&ctx); ecs_set(world, base, Position, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 2); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.event_id, ecs_id(Position)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e1); test_int(ctx.e[1], e2); ecs_os_zeromem(&ctx); ecs_set(world, base, Velocity, {1, 2}); test_int(ctx.invoked, 0); ecs_fini(world); } void Trigger_on_add_base_w_override(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, /* Implicitly also listens to IsA */ .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx.invoked, 0); ecs_add(world, base, TagA); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.s[0][0], base); ecs_os_zeromem(&ctx); ecs_add(world, e, TagA); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.s[0][0], 0); ecs_os_zeromem(&ctx); ecs_remove(world, base, TagA); ecs_add(world, base, TagA); ecs_add(world, base, TagB); test_int(ctx.invoked, 0); ecs_fini(world); test_int(ctx.invoked, 0); } void Trigger_on_set_base_w_override(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(Position), /* Implicitly also listens to IsA */ .events = {EcsOnSet}, .callback = Trigger_w_value, .ctx = &ctx }); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx.invoked, 0); ecs_set(world, base, Position, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.event_id, ecs_id(Position)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.s[0][0], base); ecs_os_zeromem(&ctx); ecs_set(world, e, Position, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.event_id, ecs_id(Position)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], e); test_int(ctx.s[0][0], 0); ecs_os_zeromem(&ctx); ecs_set(world, base, Position, {10, 20}); test_int(ctx.invoked, 0); // no trigger, overridden ecs_set(world, base, Velocity, {1, 2}); test_int(ctx.invoked, 0); ecs_fini(world); test_int(ctx.invoked, 0); } void Trigger_entity_source_1_trigger(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t subj = ecs_new_id(world); ecs_entity_t dummy = ecs_new_id(world); /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(Position), .filter.terms[0].src.id = subj, .events = {EcsOnSet}, .callback = Trigger_w_value_from_entity, .ctx = &ctx }); ecs_set(world, dummy, Position, {10, 20}); test_int(ctx.invoked, 0); ecs_set(world, subj, Position, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.event_id, ecs_id(Position)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], 0); test_int(ctx.s[0][0], subj); ecs_fini(world); } void Trigger_entity_source_2_triggers(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t subj_a = ecs_new_id(world); ecs_entity_t subj_b = ecs_new_id(world); ecs_entity_t dummy = ecs_new_id(world); /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(Position), .filter.terms[0].src.id = subj_a, .events = {EcsOnSet}, .callback = Trigger_w_value_from_entity, .ctx = &ctx }); ecs_entity_t t2 = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(Position), .filter.terms[0].src.id = subj_b, .events = {EcsOnSet}, .callback = Trigger_w_value_from_entity, .ctx = &ctx }); ecs_set(world, dummy, Position, {10, 20}); test_int(ctx.invoked, 0); ecs_set(world, subj_a, Position, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t1); test_int(ctx.event, EcsOnSet); test_int(ctx.event_id, ecs_id(Position)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], 0); test_int(ctx.s[0][0], subj_a); ecs_os_zeromem(&ctx); ecs_set(world, subj_b, Position, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t2); test_int(ctx.event, EcsOnSet); test_int(ctx.event_id, ecs_id(Position)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], 0); test_int(ctx.s[0][0], subj_b); ecs_fini(world); } void Trigger_entity_source_base_set(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_entity_t subj = ecs_new_w_pair(world, EcsIsA, base); ecs_entity_t dummy = ecs_new_w_pair(world, EcsIsA, base); ecs_entity_t dummy_no_base = ecs_new_w_pair(world, EcsIsA, base); /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(Position), .filter.terms[0].src.id = subj, .events = {EcsOnSet}, .callback = Trigger_w_value_from_entity, .ctx = &ctx }); ecs_set(world, dummy, Position, {10, 20}); test_int(ctx.invoked, 0); ecs_set(world, dummy_no_base, Position, {10, 20}); test_int(ctx.invoked, 0); ecs_set(world, base, Position, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnSet); test_int(ctx.event_id, ecs_id(Position)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], 0); test_int(ctx.s[0][0], base); ecs_fini(world); } void Trigger_not_from_superset(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = Tag, .filter.terms[0].oper = EcsNot, .filter.terms[0].src.flags = EcsUp, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t base = ecs_new(world, Tag); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx.invoked, 0); ecs_delete(world, inst); test_int(ctx.invoked, 1); ecs_fini(world); } void Trigger_create_stresstest(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); /* Make sure we can create & delete 100k triggers without running out of * memory to verify there are no leaks in the administration (sanitizer * builds don't detect leaks that are cleaned up @ ecs_fini). */ for (int i = 0; i < 100 * 1000; i ++) { ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = Tag, .filter.terms[0].src.flags = EcsSelf | EcsUp, .events = {EcsOnAdd}, .callback = Trigger }); ecs_delete(world, t); } test_assert(true); ecs_fini(world); } static void Trigger_w_new_entity_value(ecs_iter_t *it) { probe_iter(it); } void Trigger_add_non_existing_entity(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Rel); Probe ctx = {0}; ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(Rel, EcsWildcard), .events = {EcsOnAdd}, .callback = Trigger_w_new_entity_value, .ctx = &ctx }); ecs_entity_t e = ecs_new_id(world); ecs_add_pair(world, e, Rel, 1000); test_assert( ecs_has_pair(world, e, Rel, 1000)); test_assert( ecs_is_alive(world, 1000)); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t1); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, ecs_pair(Rel, 1000)); test_int(ctx.term_count, 1); test_null(ctx.param); ecs_fini(world); } void Trigger_on_add_self_trigger_with_add_isa(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); Probe ctx = {0}; ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = Tag, .filter.terms[0].src.flags = EcsSelf, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); test_assert(t1 != 0); test_int(ctx.invoked, 0); ecs_entity_t base = ecs_new_id(world); ecs_add(world, base, Tag); test_int(ctx.invoked, 1); ctx = (Probe){0}; ecs_entity_t e = ecs_new_id(world); ecs_add_pair(world, e, EcsIsA, base); test_int(ctx.invoked, 0); ecs_fini(world); } void Trigger_on_set_self_trigger_with_add_isa(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); Probe ctx = {0}; ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(Position), .filter.terms[0].src.flags = EcsSelf, .events = {EcsOnSet}, .callback = Trigger, .ctx = &ctx }); test_assert(t1 != 0); test_int(ctx.invoked, 0); ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); test_int(ctx.invoked, 1); ctx = (Probe){0}; ecs_entity_t e = ecs_new_id(world); ecs_add_pair(world, e, EcsIsA, base); test_int(ctx.invoked, 0); ecs_fini(world); } static int invoke_count = 0; static ecs_entity_t base_ent = 0; static ecs_entity_t inst_ent_a = 0; static ecs_entity_t inst_ent_b = 0; static void TriggerTwice(ecs_iter_t *it) { probe_system_w_ctx(it, it->ctx); test_assert(base_ent != 0); test_assert(inst_ent_a != 0); test_assert(inst_ent_b != 0); test_int(it->count, 1); if (invoke_count == 0) { test_assert(it->entities[0] == base_ent); invoke_count ++; } else if (invoke_count == 1) { test_assert(it->entities[0] == inst_ent_b); invoke_count ++; } else { test_int(invoke_count, 2); test_assert(it->entities[0] == inst_ent_a); invoke_count ++; } } void Trigger_notify_propagated_twice(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); Probe ctx = {0}; ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = TriggerTwice, .ctx = &ctx }); test_assert(t1 != 0); base_ent = ecs_new_id(world); inst_ent_a = ecs_new_w_pair(world, EcsIsA, base_ent); ecs_add(world, inst_ent_a, TagB); inst_ent_b = ecs_new_w_pair(world, EcsIsA, base_ent); test_int(ctx.invoked, 0); ecs_add(world, base_ent, TagA); test_int(ctx.invoked, 3); test_int(invoke_count, 3); ecs_fini(world); } void Trigger_trigger_superset_wildcard(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Rel); ECS_TAG(world, ObjA); ECS_TAG(world, ObjB); ecs_entity_t base = ecs_new_id(world); ecs_entity_t inst = ecs_new_id(world); ecs_add_pair(world, inst, EcsIsA, base); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(Rel, EcsWildcard), .filter.terms[0].src.flags = EcsUp, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); test_assert(t != 0); test_int(ctx.invoked, 0); ecs_add_pair(world, base, Rel, ObjA); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, ecs_pair(Rel, ObjA)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], inst); test_int(ctx.s[0][0], base); ecs_os_zeromem(&ctx); ecs_add_pair(world, base, Rel, ObjB); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, ecs_pair(Rel, ObjB)); test_int(ctx.term_count, 1); test_null(ctx.param); test_int(ctx.e[0], inst); test_int(ctx.s[0][0], base); ecs_fini(world); } void Trigger_remove_wildcard_1_id(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Rel); ECS_TAG(world, ObjA); ecs_entity_t e = ecs_new_w_pair(world, Rel, ObjA); test_assert( ecs_has_pair(world, e, Rel, ObjA)); test_assert( ecs_has_pair(world, e, Rel, EcsWildcard)); Probe ctx_a = {0}; ecs_entity_t t_a = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(Rel, ObjA), .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx_a }); test_assert(t_a != 0); Probe ctx_b = {0}; ecs_entity_t t_b = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(Rel, EcsWildcard), .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx_b }); test_assert(t_b != 0); test_int(ctx_a.invoked, 0); test_int(ctx_b.invoked, 0); ecs_remove_pair(world, e, Rel, EcsWildcard); test_assert( !ecs_has_pair(world, e, Rel, ObjA)); test_assert( !ecs_has_pair(world, e, Rel, EcsWildcard)); test_int(ctx_a.invoked, 1); test_int(ctx_a.count, 1); test_int(ctx_a.system, t_a); test_int(ctx_a.event, EcsOnRemove); test_int(ctx_a.event_id, ecs_pair(Rel, ObjA)); test_int(ctx_a.term_count, 1); test_int(ctx_a.e[0], e); test_int(ctx_b.invoked, 1); test_int(ctx_b.count, 1); test_int(ctx_b.system, t_b); test_int(ctx_b.event, EcsOnRemove); test_int(ctx_b.event_id, ecs_pair(Rel, ObjA)); test_int(ctx_b.term_count, 1); test_int(ctx_b.e[0], e); ecs_fini(world); } void Trigger_remove_wildcard_2_ids(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Rel); ECS_TAG(world, ObjA); ECS_TAG(world, ObjB); Probe *ctx_array = ecs_os_calloc_n(Probe, 3); ecs_entity_t e = ecs_new_w_pair(world, Rel, ObjA); ecs_add_pair(world, e, Rel, ObjB); test_assert( ecs_has_pair(world, e, Rel, ObjA)); test_assert( ecs_has_pair(world, e, Rel, ObjB)); test_assert( ecs_has_pair(world, e, Rel, EcsWildcard)); Probe *ctx_a = &ctx_array[0]; ecs_entity_t t_a = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(Rel, EcsWildcard), .events = {EcsOnRemove}, .callback = Trigger, .ctx = ctx_a }); test_assert(t_a != 0); Probe *ctx_b = &ctx_array[1]; ecs_entity_t t_b = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(Rel, ObjA), .events = {EcsOnRemove}, .callback = Trigger, .ctx = ctx_b }); test_assert(t_b != 0); Probe *ctx_c = &ctx_array[2]; ecs_entity_t t_c = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_pair(Rel, ObjB), .events = {EcsOnRemove}, .callback = Trigger, .ctx = ctx_c }); test_assert(t_c != 0); test_int(ctx_a->invoked, 0); test_int(ctx_b->invoked, 0); test_int(ctx_c->invoked, 0); ecs_remove_pair(world, e, Rel, EcsWildcard); test_assert( !ecs_has_pair(world, e, Rel, ObjA)); test_assert( !ecs_has_pair(world, e, Rel, ObjB)); test_assert( !ecs_has_pair(world, e, Rel, EcsWildcard)); test_int(ctx_a->invoked, 2); test_int(ctx_a->count, 2); test_int(ctx_a->system, t_a); test_int(ctx_a->event, EcsOnRemove); test_int(ctx_a->event_id, ecs_pair(Rel, ObjB)); test_int(ctx_a->term_count, 1); test_int(ctx_a->e[0], e); test_int(ctx_b->invoked, 1); test_int(ctx_b->count, 1); test_int(ctx_b->system, t_b); test_int(ctx_b->event, EcsOnRemove); test_int(ctx_b->event_id, ecs_pair(Rel, ObjA)); test_int(ctx_b->term_count, 1); test_int(ctx_b->e[0], e); test_int(ctx_c->invoked, 1); test_int(ctx_c->count, 1); test_int(ctx_c->system, t_c); test_int(ctx_c->event, EcsOnRemove); test_int(ctx_c->event_id, ecs_pair(Rel, ObjB)); test_int(ctx_c->term_count, 1); test_int(ctx_c->e[0], e); ecs_os_free(ctx_array); ecs_fini(world); } void Trigger_on_set_w_tag(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnSet}, .callback = Trigger, .ctx = &ctx }); test_assert(t1 != 0); ecs_entity_t e = ecs_new_id(world); test_assert(e != 0); ecs_add(world, e, TagA); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.e[0], e); test_int(ctx.event, EcsOnAdd); test_int(ctx.event_id, TagA); ecs_fini(world); } static int nested_trigger_invoked = 0; static void NestedTrigger(ecs_iter_t *it) { nested_trigger_invoked ++; } static int create_trigger_invoked = 0; typedef struct { int32_t count; ecs_entity_t first; } CreateTriggers_ctx; static void CreateTriggers(ecs_iter_t *it) { CreateTriggers_ctx *ctx = it->ctx; create_trigger_invoked ++; ctx->first = ecs_new_id(it->world); int i; for (i = 0; i < ctx->count - 1; i ++) { ecs_new_id(it->world); } for (i = 0; i < ctx->count; i ++) { ecs_observer_init(it->world, &(ecs_observer_desc_t){ .filter.terms[0].id = ctx->first + i, .events = {EcsOnAdd}, .callback = NestedTrigger }); } } void Trigger_create_triggers_in_trigger(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); int CreateCount = 10; CreateTriggers_ctx ctx = { .count = CreateCount }; ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = CreateTriggers, .ctx = &ctx }); test_assert(t1 != 0); ecs_entity_t e = ecs_new_id(world); test_assert(e != 0); ecs_add(world, e, TagA); test_int(create_trigger_invoked, 1); test_int(nested_trigger_invoked, 0); int i; for (i = 0; i < CreateCount; i ++) { ecs_entity_t t = ecs_new_id(world); ecs_add_id(world, t, ctx.first + i); test_int(nested_trigger_invoked, i + 1); } test_int(create_trigger_invoked, 1); test_int(nested_trigger_invoked, i); ecs_fini(world); } static void Trigger_w_nonzero_value(ecs_iter_t *it) { probe_system_w_ctx(it, it->ctx); test_int(it->count, 1); test_assert(it->entities != NULL); test_assert(it->entities[0] != 0); Position *p = ecs_field(it, Position, 1); test_assert(p != NULL); } void Trigger_on_add_superset_w_component(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(Position), .events = {EcsOnAdd}, .callback = Trigger_w_nonzero_value, .ctx = &ctx }); ecs_entity_t b = ecs_new(world, 0); ecs_set(world, b, Position, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.e[0], b); test_int(ctx.s[0][0], 0); ecs_os_zeromem(&ctx); ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, b); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.e[0], i); test_int(ctx.s[0][0], b); ecs_fini(world); } void Trigger_on_set_superset_w_component(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(Position), .events = {EcsOnSet}, .callback = Trigger_w_value, .ctx = &ctx }); ecs_entity_t b = ecs_new(world, 0); ecs_set(world, b, Position, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.e[0], b); test_int(ctx.s[0][0], 0); ecs_os_zeromem(&ctx); ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, b); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.e[0], i); test_int(ctx.s[0][0], b); ecs_fini(world); } void Trigger_on_add_base_superset_w_owned(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { .id = ecs_id(Tag), .src.flags = EcsUp, }, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t b = ecs_new(world, 0); ecs_add(world, b, Tag); test_int(ctx.invoked, 0); ecs_entity_t i = ecs_new(world, Tag); test_int(ctx.invoked, 0); ecs_add_pair(world, i, EcsIsA, b); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.e[0], i); test_int(ctx.s[0][0], b); ecs_fini(world); } void Trigger_on_add_base_self_superset_w_owned(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { .id = ecs_id(Tag), .src.flags = EcsSelf|EcsUp, }, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t b = ecs_new(world, 0); ecs_add(world, b, Tag); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.e[0], b); test_int(ctx.s[0][0], 0); ecs_os_zeromem(&ctx); ecs_entity_t i1 = ecs_new(world, Tag); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.e[0], i1); test_int(ctx.s[0][0], 0); ecs_os_zeromem(&ctx); ecs_add_pair(world, i1, EcsIsA, b); test_int(ctx.invoked, 0); ecs_entity_t i2 = ecs_new_w_pair(world, EcsIsA, b); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.e[0], i2); test_int(ctx.s[0][0], b); ecs_fini(world); } void Trigger_on_set_self_from_child_of_prefab(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_entity_t base_child = ecs_new_entity(world, "Child"); ecs_add_id(world, base_child, EcsPrefab); ecs_add_pair(world, base_child, EcsChildOf, base); ecs_set(world, base_child, Position, {10, 20}); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { .id = ecs_id(Position), .src.flags = EcsSelf }, .events = {EcsOnSet}, .callback = Trigger_w_value, .ctx = &ctx }); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx.invoked, 1); test_int(ctx.count, 1); ecs_entity_t inst_child = ecs_lookup_child(world, inst, "Child"); test_assert(inst_child != 0); test_int(ctx.e[0], inst_child); test_int(ctx.s[0][0], 0); test_assert(ecs_has(world, inst_child, Position)); test_assert(ecs_owns(world, inst_child, Position)); ecs_fini(world); } void Trigger_on_set_self_superset_from_child_of_prefab(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_entity_t base_child = ecs_new_entity(world, "Child"); ecs_add_id(world, base_child, EcsPrefab); ecs_add_pair(world, base_child, EcsChildOf, base); ecs_set(world, base_child, Position, {10, 20}); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { .id = ecs_id(Position), .src.flags = EcsSelf | EcsUp }, .events = {EcsOnSet}, .callback = Trigger_w_value, .ctx = &ctx }); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); ecs_entity_t inst_child = ecs_lookup_child(world, inst, "Child"); test_assert(inst_child != 0); test_assert(ecs_has(world, inst_child, Position)); test_assert(ecs_owns(world, inst_child, Position)); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.e[0], inst_child); test_int(ctx.s[0][0], 0); ecs_fini(world); } void Trigger_on_set_self_from_child_base_of_prefab(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_entity_t base_child_base = ecs_new_w_id(world, EcsPrefab); ecs_add_id(world, base_child_base, EcsPrefab); ecs_set(world, base_child_base, Position, {10, 20}); ecs_entity_t base_child = ecs_new_entity(world, "Child"); ecs_add_pair(world, base_child, EcsChildOf, base); ecs_add_pair(world, base_child, EcsIsA, base_child_base); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { .id = ecs_id(Position), .src.flags = EcsSelf }, .events = {EcsOnSet}, .callback = Trigger_w_value, .ctx = &ctx }); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); ecs_entity_t inst_child = ecs_lookup_child(world, inst, "Child"); test_assert(inst_child != 0); test_assert(ecs_has(world, inst_child, Position)); test_assert(!ecs_owns(world, inst_child, Position)); test_int(ctx.invoked, 0); ecs_fini(world); } void Trigger_on_set_self_superset_from_child_base_of_prefab(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_entity_t base_child_base = ecs_new_w_id(world, EcsPrefab); ecs_add_id(world, base_child_base, EcsPrefab); ecs_set(world, base_child_base, Position, {10, 20}); ecs_entity_t base_child = ecs_new_entity(world, "Child"); ecs_add_pair(world, base_child, EcsChildOf, base); ecs_add_pair(world, base_child, EcsIsA, base_child_base); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { .id = ecs_id(Position), .src.flags = EcsSelf | EcsUp }, .events = {EcsOnSet}, .callback = Trigger_w_value, .ctx = &ctx }); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); ecs_entity_t inst_child = ecs_lookup_child(world, inst, "Child"); test_assert(inst_child != 0); test_assert(ecs_has(world, inst_child, Position)); test_assert(!ecs_owns(world, inst_child, Position)); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.e[0], inst_child); test_int(ctx.s[0][0], base_child_base); ecs_fini(world); } void Trigger_on_set_self_auto_override(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); ecs_add_id(world, base, ECS_OVERRIDE | ecs_id(Position)); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { .id = ecs_id(Position), .src.flags = EcsSelf }, .events = {EcsOnSet}, .callback = Trigger_w_value, .ctx = &ctx }); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); test_assert( ecs_has(world, inst, Position)); test_assert( ecs_owns(world, inst, Position)); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.e[0], inst); test_int(ctx.s[0][0], 0); ecs_fini(world); } void Trigger_on_set_self_superset_auto_override(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); ecs_add_id(world, base, ECS_OVERRIDE | ecs_id(Position)); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { .id = ecs_id(Position), .src.flags = EcsSelf | EcsUp }, .events = {EcsOnSet}, .callback = Trigger_w_value, .ctx = &ctx }); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); test_assert( ecs_has(world, inst, Position)); test_assert( ecs_owns(world, inst, Position)); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.e[0], inst); test_int(ctx.s[0][0], 0); ecs_fini(world); } void Trigger_on_set_superset_auto_override(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); ecs_add_id(world, base, ECS_OVERRIDE | ecs_id(Position)); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { .id = ecs_id(Position), .src.flags = EcsUp }, .events = {EcsOnSet}, .callback = Trigger_w_value, .ctx = &ctx }); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); test_assert( ecs_has(world, inst, Position)); test_assert( ecs_owns(world, inst, Position)); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.e[0], inst); test_int(ctx.s[0][0], base); ecs_fini(world); } void Trigger_not_only(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); Probe ctx = {0}; ecs_observer(world, { .filter.terms = { { TagA, .oper = EcsNot } }, .events = { EcsOnAdd }, .callback = Trigger, .ctx = &ctx }); test_int(ctx.invoked, 0); ecs_entity_t i = ecs_new(world, TagA); test_int(ctx.invoked, 0); ecs_delete(world, i); test_int(ctx.invoked, 1); ecs_fini(world); } void Trigger_not_only_w_base(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_observer(world, { .filter.terms = { { TagA, .oper = EcsNot } }, .events = { EcsOnAdd }, .callback = Trigger, .ctx = &ctx }); test_int(ctx.invoked, 0); ecs_entity_t p = ecs_new(world, TagA); test_int(ctx.invoked, 0); ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); test_int(ctx.invoked, 0); ecs_delete(world, i); test_int(ctx.invoked, 1); ecs_fini(world); } void Trigger_not_only_w_base_no_match(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); Probe ctx = {0}; ecs_observer(world, { .filter.terms = { { TagB, .oper = EcsNot } }, .events = { EcsOnAdd }, .callback = Trigger, .ctx = &ctx }); test_int(ctx.invoked, 0); ecs_entity_t p = ecs_new(world, TagA); test_int(ctx.invoked, 0); ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); test_int(ctx.invoked, 0); ecs_delete(world, i); test_int(ctx.invoked, 0); ecs_fini(world); } void Trigger_on_set_superset_after_filter_observer(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); Probe ctx_1 = {0}; ecs_observer(world, { .filter.terms = { { ecs_id(Position) } }, .filter.flags = EcsFilterNoData, .events = { EcsOnSet }, .callback = Trigger, .ctx = &ctx_1 }); Probe ctx_2 = {0}; ecs_observer(world, { .filter.terms = { { ecs_id(Position), .src.flags = EcsUp } }, .events = { EcsOnSet }, .callback = Trigger_w_value, .ctx = &ctx_2 }); ecs_entity_t base = ecs_new_id(world); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx_1.invoked, 0); test_int(ctx_2.invoked, 0); ecs_set(world, base, Position, {10, 20}); test_int(ctx_1.invoked, 2); test_int(ctx_2.invoked, 1); test_int(ctx_2.count, 1); test_int(ctx_2.e[0], inst); test_int(ctx_2.c[0][0], ecs_id(Position)); test_int(ctx_2.s[0][0], base); ecs_fini(world); } void Trigger_on_set_superset_after_filter_observer_w_on_add(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); Probe ctx_1 = {0}; ecs_observer(world, { .filter.terms = { { ecs_id(Position) } }, .filter.flags = EcsFilterNoData, .events = { EcsOnAdd }, .callback = Trigger, .ctx = &ctx_1 }); Probe ctx_2 = {0}; ecs_observer(world, { .filter.terms = { { ecs_id(Position), .src.flags = EcsUp } }, .events = { EcsOnAdd }, .callback = Trigger, .ctx = &ctx_2 }); ecs_entity_t base = ecs_new_id(world); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx_1.invoked, 0); test_int(ctx_2.invoked, 0); ecs_set(world, base, Position, {10, 20}); test_int(ctx_1.invoked, 2); test_int(ctx_2.invoked, 1); test_int(ctx_2.count, 1); test_int(ctx_2.e[0], inst); test_int(ctx_2.c[0][0], ecs_id(Position)); test_int(ctx_2.s[0][0], base); ecs_fini(world); } void Trigger_on_set_superset_after_filter_observer_w_on_add_isa_after_set(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); Probe ctx_1 = {0}; ecs_observer(world, { .filter.terms = { { ecs_id(Position), .src.flags = EcsUp } }, .filter.flags = EcsFilterNoData, .events = { EcsOnAdd }, .callback = Trigger, .ctx = &ctx_1 }); Probe ctx_2 = {0}; ecs_observer(world, { .filter.terms = { { ecs_id(Position), .src.flags = EcsUp } }, .events = { EcsOnAdd }, .callback = Trigger, .ctx = &ctx_2 }); ecs_entity_t base = ecs_new_id(world); ecs_set(world, base, Position, {10, 20}); test_int(ctx_1.invoked, 0); test_int(ctx_2.invoked, 0); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx_1.invoked, 1); test_int(ctx_1.count, 1); test_int(ctx_1.e[0], inst); test_int(ctx_1.c[0][0], ecs_id(Position)); test_int(ctx_1.s[0][0], base); test_int(ctx_2.invoked, 1); test_int(ctx_2.count, 1); test_int(ctx_2.e[0], inst); test_int(ctx_2.c[0][0], ecs_id(Position)); test_int(ctx_2.s[0][0], base); ecs_fini(world); } void Trigger_on_set_superset_after_filter_observer_w_on_add_2(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); // first observer causes observer to be initialized with filter flag Probe ctx_1 = {0}; ecs_observer(world, { .filter.terms = { { ecs_id(Position) } }, .filter.flags = EcsFilterNoData, .events = { EcsOnAdd }, .callback = Trigger, .ctx = &ctx_1 }); // second observer is not a filter, so value should be avaulable Probe ctx_2 = {0}; ecs_observer(world, { .filter.terms = { { ecs_id(Position), .src.flags = EcsUp } }, .events = { EcsOnAdd }, .callback = Trigger, .ctx = &ctx_2 }); ecs_entity_t base = ecs_new_id(world); ecs_set(world, base, Position, {10, 20}); ecs_override(world, base, Position); // override causes emit with 2 ids test_int(ctx_1.invoked, 1); test_int(ctx_2.invoked, 0); ecs_os_zeromem(&ctx_1); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx_1.invoked, 1); test_int(ctx_1.count, 1); test_int(ctx_1.e[0], inst); test_int(ctx_1.c[0][0], ecs_id(Position)); test_int(ctx_1.s[0][0], 0); // override test_int(ctx_2.invoked, 1); test_int(ctx_2.count, 1); test_int(ctx_2.e[0], inst); test_int(ctx_2.c[0][0], ecs_id(Position)); test_int(ctx_2.s[0][0], base); ecs_fini(world); } void Trigger_propagate_w_union_pair(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); ECS_TAG(world, RelX); ECS_ENTITY(world, RelY, Union); ecs_entity_t base = ecs_new_id(world); ecs_entity_t tgt = ecs_new_id(world); ecs_new_w_pair(world, EcsIsA, base); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { .id = Tag, .src.flags = EcsUp }, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); test_int(ctx.invoked, 0); ecs_add_id(world, base, Tag); test_int(ctx.invoked, 1); ecs_os_zeromem(&ctx); ecs_add_pair(world, base, RelX, tgt); test_int(ctx.invoked, 0); ecs_add_pair(world, base, RelY, tgt); test_int(ctx.invoked, 0); ecs_fini(world); }