diff --git a/cpp/src/doc_classes/GFComponent.xml b/cpp/src/doc_classes/GFComponent.xml index 4afeb47..faff8ce 100644 --- a/cpp/src/doc_classes/GFComponent.xml +++ b/cpp/src/doc_classes/GFComponent.xml @@ -1,6 +1,5 @@ - + A reference to a Glecs component from a [GFWorld] that is attatached to an entity. diff --git a/cpp/src/doc_classes/GFComponentBuilder.xml b/cpp/src/doc_classes/GFComponentBuilder.xml index d6fafef..f914153 100644 --- a/cpp/src/doc_classes/GFComponentBuilder.xml +++ b/cpp/src/doc_classes/GFComponentBuilder.xml @@ -1,5 +1,6 @@ - + Builder for a new component type. diff --git a/cpp/src/doc_classes/GFEntity.xml b/cpp/src/doc_classes/GFEntity.xml index 4de9179..6cda7f3 100644 --- a/cpp/src/doc_classes/GFEntity.xml +++ b/cpp/src/doc_classes/GFEntity.xml @@ -1,6 +1,5 @@ - + A reference to an entity from a [GFWorld]. @@ -10,6 +9,20 @@ https://www.flecs.dev/flecs/md_docs_2EntitiesComponents.html + + + + + Adds component data of type [param component] to this entity. + The component's type ID is coerced from a [Variant]. To learn more about [Variant] coercion see [method GFWorld.coerce_id]. + [codeblock] + var entity:= GFEntity.new() + entity.add(GFPosition2D, Vector2(10, 10)) + entity.get(GFPosition2D).get_vec() == Vector2(10, 10) # true + [/codeblock] + This method returns [code]self[/code] for chaining. + + @@ -32,20 +45,6 @@ This codeblock returns [code]self[/code] for chaining. - - - - - Adds component data of type [param component] to this entity. - The component's type ID is coerced from a [Variant]. To learn more about [Variant] coercion see [method GFWorld.coerce_id]. - [codeblock] - var entity:= GFEntity.new() - entity.add(GFPosition2D, Vector2(10, 10)) - entity.get(GFPosition2D).get_vec() == Vector2(10, 10) # true - [/codeblock] - This method returns [code]self[/code] for chaining. - - diff --git a/cpp/src/doc_classes/GFTag.xml b/cpp/src/doc_classes/GFTag.xml new file mode 100644 index 0000000..883d2eb --- /dev/null +++ b/cpp/src/doc_classes/GFTag.xml @@ -0,0 +1,51 @@ + + + + A reference to an entity with no data. + + + Tags are a kind of entity that can be attached to other entities to mark them without adding data, unlike components which will add data when attatched. + + + https://www.flecs.dev/flecs/md_docs_2Quickstart.html#tag + + + + + + + + Returns an entity from an ID coerced from a [Variant]. + If no world is specified, a default world is used. + To learn more about [Variant] coercion see [method GFWorld.coerce_id]. + [codeblock] + var entity:= GFTag.from("flecs/core/Trait") + [/codeblock] + + + + + + + + Returns an tag from an ID. + If no world is specified, a default world is used. + [codeblock] + var entity:= GFEntity.from_id(255) + [/codeblock] + + + + + + + Returns a reference to a new entity created within [param world]. + [codeblock] + var world:= GFWorld.new() + var entity:= GFEntity.new_in_world(world) + [/codeblock] + + + + diff --git a/cpp/src/register_types.cpp b/cpp/src/register_types.cpp index 92add83..6584185 100644 --- a/cpp/src/register_types.cpp +++ b/cpp/src/register_types.cpp @@ -6,6 +6,7 @@ #include "entity_builder.h" #include "gdextension_interface.h" #include "godot_cpp/classes/engine.hpp" +#include "godot_cpp/core/memory.hpp" #include "module.h" #include "observer_builder.h" #include "pair.h" @@ -16,6 +17,7 @@ #include "register_types.h" #include "registerable_entity.h" #include "system_builder.h" +#include "tag.h" #include "world.h" using namespace godot; @@ -34,6 +36,7 @@ void initialize_module(ModuleInitializationLevel p_level) { godot::ClassDB::register_class(); godot::ClassDB::register_class(); godot::ClassDB::register_class(); + godot::ClassDB::register_class(); godot::ClassDB::register_class(); godot::ClassDB::register_class(); @@ -44,6 +47,12 @@ void initialize_module(ModuleInitializationLevel p_level) { godot::ClassDB::register_class(); godot::ClassDB::register_class(); godot::ClassDB::register_class(); + + if (Engine::get_singleton()->has_singleton(GFWorld::SINGLETON_NAME)) { + Object* world = Engine::get_singleton()->get_singleton(GFWorld::SINGLETON_NAME); + Engine::get_singleton()->unregister_singleton(GFWorld::SINGLETON_NAME); + memdelete(world); + } Engine::get_singleton()->register_singleton(GFWorld::SINGLETON_NAME, memnew(GFWorld)); break; case MODULE_INITIALIZATION_LEVEL_EDITOR: diff --git a/cpp/src/tag.cpp b/cpp/src/tag.cpp new file mode 100644 index 0000000..f5226fb --- /dev/null +++ b/cpp/src/tag.cpp @@ -0,0 +1,46 @@ + +#include "tag.h" +#include "utils.h" +#include +#include +#include "godot_cpp/classes/wrapped.hpp" +#include "godot_cpp/variant/array.hpp" +#include "world.h" +#include +#include + +using namespace godot; + +GFTag::~GFTag() { +} + +Ref GFTag::new_in_world(GFWorld* world) { + return memnew(GFTag(world)); +} + +Ref GFTag::from(Variant tag, GFWorld* world) { + ecs_entity_t tag_id = world->coerce_id(tag); + return from_id(tag_id, world); +} + +Ref GFTag::from_id(ecs_entity_t tag_id, GFWorld* world_) { + GFWorld* world = GFWorld::world_or_singleton(world_); + Ref ett = memnew(GFTag( + tag_id, + world + )); + if (!ett->is_alive()) { + ERR(nullptr, + "Could not instantiate tag from ID\n", + "World/ID is not valid/alive" + ); + } + return ett; +} + + +void GFTag::_bind_methods() { + godot::ClassDB::bind_static_method(get_class_static(), D_METHOD("new_in_world", "world"), &GFTag::new_in_world); + godot::ClassDB::bind_static_method(get_class_static(), D_METHOD("from", "tag", "world"), &GFTag::from, nullptr); + godot::ClassDB::bind_static_method(get_class_static(), D_METHOD("from_id", "tag_id", "world"), &GFTag::from_id, nullptr); +} diff --git a/cpp/src/tag.h b/cpp/src/tag.h new file mode 100644 index 0000000..cd9dbc9 --- /dev/null +++ b/cpp/src/tag.h @@ -0,0 +1,63 @@ + +#ifndef GF_TAG_H +#define GF_TAG_H + +#include "registerable_entity.h" +#include "world.h" + +#include +#include +#include + +namespace godot { + + class GFTag : public GFRegisterableEntity { + GDCLASS(GFTag, GFRegisterableEntity) + + public: + /// Create a new named module + GFTag(GFWorld* world): + GFRegisterableEntity( + ecs_new( + world->raw() + ), + world + ) + {} + /// Create from existing ID + GFTag(ecs_entity_t id, GFWorld* world): + GFRegisterableEntity( + id, + world + ) + {} + GFTag(): + GFRegisterableEntity(GFWorld::singleton()) + {} + + ~GFTag(); + + // -------------------------------------- + // --- Exposed + // -------------------------------------- + + static Ref new_in_world(GFWorld* world); + static Ref new_named_in_world(String name, GFWorld*); + static Ref from(Variant module, GFWorld*); + static Ref from_id(ecs_entity_t, GFWorld*); + + // -------------------------------------- + // --- Unexposed + // -------------------------------------- + + protected: + static void _bind_methods(); + + + private: + + }; + +} + +#endif diff --git a/unittests/test_entities.gd b/unittests/test_entities.gd index a5d053d..e03578d 100644 --- a/unittests/test_entities.gd +++ b/unittests/test_entities.gd @@ -3,10 +3,10 @@ extends GutTest var world:GFWorld = null -func before_all(): +func before_each(): world = GFWorld.new() -func after_all(): +func after_each(): world.free() #region Tests diff --git a/unittests/test_tags.gd b/unittests/test_tags.gd new file mode 100644 index 0000000..01c5614 --- /dev/null +++ b/unittests/test_tags.gd @@ -0,0 +1,29 @@ + +extends GutTest + +var world:GFWorld = null + +func before_each(): + world = GFWorld.new() + +func after_each(): + world.free() + +#region Tests + +func _test_tag_creation(): + var enemy:= GFTag.new_in_world(world) + var robot:=GFEntity.new_in_world(world) \ + .add(enemy) + + assert_true(robot.has_entity(enemy), "Expected `robot` to have the `enemy` tag") + +#endregion + +#region Classes + +class Eats extends GFTag: pass + +class Grass extends GFTag: pass + +#endregion