1 /** ecs entity (container of components) */
2 
3 module re.ecs.entity;
4 
5 import std.array;
6 import std.conv;
7 import std.algorithm.iteration;
8 import std.algorithm.searching;
9 import re.ng.scene;
10 import re.ecs.manager;
11 import re.ecs.component;
12 import re.ecs.storage;
13 import re.math;
14 
15 /// a container for components
16 class Entity {
17     /// the unique id of this entity
18     public size_t id;
19     /// whether this entity is alive
20     public bool alive;
21     /// the scene that this entity is part of
22     public Scene scene;
23     /// world transform of entity
24     public Transform transform;
25     /// friendly name
26     public string name;
27     /// ecs manager
28     public EntityManager manager;
29     /// list of component references
30     public ComponentId[] components;
31 
32     /// create entity given the ecs
33     this(EntityManager manager) {
34         this.manager = manager;
35     }
36 
37     /// prepare the instance as a fresh entity
38     public void initialize() {
39         alive = true;
40         transform = Transform();
41         name = string.init;
42         components = [];
43     }
44 
45     /// release all resources and deinitialize
46     public void destroy() {
47         alive = false;
48         manager.storage.destroy_all(this); // destroy all components
49         name = string.init;
50     }
51 
52     /// add an instance of a component given the type
53     public T add_component(T)() {
54         return add_component(new T());
55     }
56 
57     /// add a component
58     public T add_component(T)(T component) {
59         auto id = manager.storage.insert(this, component);
60         components ~= id;
61         component.entity = this;
62         component.setup();
63         return component;
64     }
65 
66     /// remove a matching component given a type
67     public void remove_component(T)() {
68         auto component = get_component!T();
69         remove_component(component);
70     }
71 
72     /// remove a specific component
73     public void remove_component(T)(T component) {
74         manager.storage.remove(this, component);
75     }
76 
77     /// checks whether this entity contains a matching component
78     public bool has_component(T)() {
79         return manager.storage.has_component!T(this);
80     }
81 
82     /// gets a matching component given a type
83     public T get_component(T)() {
84         return cast(T) manager.storage.get!T(this);
85 
86     }
87 
88     /// gets all matching components given a type
89     public T[] get_components(T)() {
90         return manager.storage.get_all!T(this);
91     }
92 
93     /// gets all components attached to this entity
94     public Component[] get_all_components() {
95         return manager.storage.get_all(this);
96     }
97 
98     // - transform
99 
100     /// forwards to transform
101     @property Vector2 position2() {
102         return transform.position2;
103     }
104 
105     /// forwards to transform
106     @property Vector2 position2(Vector2 value) {
107         return transform.position2 = value;
108     }
109 
110     /// forwards to transform
111     @property ref Vector3 position() {
112         return transform.position;
113     }
114 
115     /// forwards to transform
116     @property Vector3 position(Vector3 value) {
117         return transform.position = value;
118     }
119 
120     public override string toString() const {
121         import std.string : format;
122 
123         return format("Entity[%d, %s]", id, name);
124     }
125 }
126 
127 @("ecs-entity-basic")
128 unittest {
129     import std.string : format;
130     import re.ecs.renderable;
131     import re.ecs.updatable;
132 
133     static class Thing1 : Component {
134     }
135 
136     static class Thing2 : Component, Updatable {
137         void update() {
138         }
139     }
140 
141     auto ecs = new EntityManager();
142     auto nt = ecs.create_entity();
143 
144     // try adding stuff
145     // insert 6 thing1, and 4 thing2
146     auto thing11 = nt.add_component(new Thing1());
147     auto thing12 = nt.add_component(new Thing1());
148     auto thing13 = nt.add_component(new Thing1());
149     auto thing14 = nt.add_component(new Thing1());
150     auto thing15 = nt.add_component(new Thing1());
151     auto thing16 = nt.add_component(new Thing1());
152     auto thing21 = nt.add_component(new Thing2());
153     auto thing22 = nt.add_component(new Thing2());
154     auto thing23 = nt.add_component(new Thing2());
155     auto thing24 = nt.add_component(new Thing2());
156 
157     // check that we have the right number of components
158     auto thing1s = nt.get_components!Thing1();
159     assert(thing1s.length == 6, format("expected 6 thing1s, got %d", thing1s.length));
160 
161     auto thing2s = nt.get_components!Thing2();
162     assert(thing2s.length == 4, format("expected 4 thing2s, got %d", thing2s.length));
163 
164     // try removing random stuff
165     nt.remove_component(thing13);
166     thing1s = nt.get_components!Thing1();
167     assert(thing1s.length == 5, format("expected 5 thing1s, got %d", thing1s.length));
168     nt.remove_component(thing11);
169     thing1s = nt.get_components!Thing1();
170     assert(thing1s.length == 4, format("expected 4 thing1s, got %d", thing1s.length));
171 
172     nt.remove_component(thing22);
173     thing2s = nt.get_components!Thing2();
174     assert(thing2s.length == 3, format("expected 3 thing2s, got %d", thing2s.length));
175 }