1 /*
2                                     __
3                                    / _|
4   __ _ _   _ _ __ ___  _ __ __ _  | |_ ___  ___ ___
5  / _` | | | | '__/ _ \| '__/ _` | |  _/ _ \/ __/ __|
6 | (_| | |_| | | | (_) | | | (_| | | || (_) \__ \__ \
7  \__,_|\__,_|_|  \___/|_|  \__,_| |_| \___/|___/___/
8 
9 Copyright (C) 2019 Aurora Free Open Source Software.
10 Copyright (C) 2019 João Lourenço <joao@aurorafoss.org>
11 
12 This file is part of the Aurora Free Open Source Software. This
13 organization promote free and open source software that you can
14 redistribute and/or modify under the terms of the GNU Lesser General
15 Public License Version 3 as published by the Free Software Foundation or
16 (at your option) any later version approved by the Aurora Free Open Source
17 Software Organization. The license is available in the package root path
18 as 'LICENSE' file. Please review the following information to ensure the
19 GNU Lesser General Public License version 3 requirements will be met:
20 https://www.gnu.org/licenses/lgpl.html .
21 
22 Alternatively, this file may be used under the terms of the GNU General
23 Public License version 3 or later as published by the Free Software
24 Foundation. Please review the following information to ensure the GNU
25 General Public License requirements will be met:
26 https://www.gnu.org/licenses/gpl-3.0.html.
27 
28 NOTE: All products, services or anything associated to trademarks and
29 service marks used or referenced on this file are the property of their
30 respective companies/owners or its subsidiaries. Other names and brands
31 may be claimed as the property of others.
32 
33 For more info about intellectual property visit: aurorafoss.org or
34 directly send an email to: contact (at) aurorafoss.org .
35 */
36 
37 module aurorafw.entity.entity;
38 
39 import aurorafw.entity.icomponent;
40 import aurorafw.entity.ientity;
41 import aurorafw.entity.componentmanager;
42 import aurorafw.entity.entitymanager;
43 
44 version(unittest) import aurorafw.unit.assertion;
45 
46 import std.exception;
47 import std.traits : fullyQualifiedName, Fields, FieldNameTuple;
48 import std.meta : AliasSeq;
49 
50 
51 class EntityComponentHandlingException : Exception
52 {
53 	mixin basicExceptionCtors;
54 }
55 
56 
57 class Entity : IEntity
58 {
59 	@safe pure
60 	public this(EntityManager manager, size_t id)
61 	{
62 		this.id = id;
63 		this.manager = manager;
64 		this.enabled = true;
65 	}
66 
67 
68 	/**
69 	 * Add a component
70 	 *
71 	 * Every time you inittialize a new component, an unique id is generated
72 	 * Use this function only if you want to generate the component with values
73 	 *
74 	 * Examples:
75 	 * --------------------
76 	 * e.AddComponent(new Foo(value1, value2, ...));
77 	 * --------------------
78 	 *
79 	 * See_Also: T add(T : IComponent)()
80 	 */
81 	@safe pure
82 	public T add(T : IComponent)(T t)
83 	{
84 		enum id = ComponentManager.idOf!T;
85 
86 		if (id in this.components)
87 			throw new EntityComponentHandlingException(
88 				"Cannot add component. Entity already contains the same type."
89 			);
90 
91 		this.components[id] = t;
92 		return t;
93 	}
94 
95 
96 	/**
97 	 * Add a component
98 	 *
99 	 * Use this only if you want to generate the component with init values
100 	 *
101 	 * Examples:
102 	 * --------------------
103 	 * e.AddComponent!Foo;
104 	 * --------------------
105 	 */
106 	@safe pure
107 	public T add(T : IComponent)()
108 	{
109 		return add(new T());
110 	}
111 
112 
113 	/**
114 	 * Remove a component
115 	 *
116 	 * It's called by passing the component's id
117 	 * Every time you inittialize a new component, an unique id is generated
118 	 * If you don't know which id you should pass, use the other variant of this
119 	 *   function, which is the recomended one, as it will get the correct id
120 	 *   for you
121 	 *
122 	 * Examples:
123 	 * --------------------
124 	 * e.remove(world.component.idOf!Foo);
125 	 * --------------------
126 	 *
127 	 * See_Also: remove(C : IComponent)()
128 	 */
129 	@safe pure
130 	public void remove(in string id)
131 	{
132 		if (id in this.components)
133 			this.components.remove(id);
134 
135 		else
136 			throw new EntityComponentHandlingException(
137 				"Cannot remove component. Entity doesn't contain the type you're trying to remove."
138 			);
139 	}
140 
141 
142 	/**
143 	 * Removes a component
144 	 *
145 	 * It's recomended to always use this function, as it will do the 'hard
146 	 *   work' for you
147 	 *
148 	 * Examples:
149 	 * --------------------
150 	 * e.remove!Foo;
151 	 * --------------------
152 	 */
153 	@safe pure
154 	public void remove(C : IComponent)()
155 	{
156 		remove(ComponentManager.idOf!C);
157 	}
158 
159 
160 	@safe pure
161 	public C modify(C : IComponent)(AliasSeq!(Fields!C) args)
162 	{
163 		enum id = ComponentManager.idOf!C;
164 		IComponent* p;
165 		p = id in components;
166 
167 		if(p is null)
168 			throw new EntityComponentHandlingException(
169 				"Cannot modify component. Entity doesn't contain "
170 				~ __traits(identifier, C) ~ "."
171 			);
172 
173 		C c = cast(C)components[id];
174 		import std.conv : to;
175 		static foreach(i, f; [FieldNameTuple!C])
176 		{
177 			mixin("c."~f~"="~"args["~i.to!string~"];");
178 		}
179 
180 		return c;
181 	}
182 
183 	/**
184 	 * Clear
185 	 *
186 	 * Removes every component from this entity
187 	 *
188 	 * Examples:
189 	 * --------------------
190 	 * e.clear();
191 	 * --------------------
192 	 */
193 	@safe pure
194 	public void clear()
195 	{
196 		import std.algorithm.iteration : each;
197 		components.byKey.each!(_ => components.remove(_));
198 	}
199 
200 
201 	/**
202 	 * Get a component
203 	 *
204 	 * Returns:
205 	 *     Same type if it exists
206 	 *     Null otherwise
207 	 *
208 	 * Examples:
209 	 * --------------------
210 	 * e.get!Foo
211 	 *--------------------
212 	 */
213 	@safe pure
214 	public C get(C : IComponent)()
215 	{
216 		enum id = ComponentManager.idOf!C;
217 		IComponent* p;
218 		p = id in components;
219 
220 		return p !is null ? cast(C)(*p) : null;
221 	}
222 
223 
224 	/**
225 	 * Get components
226 	 *
227 	 * Returns:
228 	 *     array of components which are contained in an entity
229 	 *     However if you want to actualy access any of this, this method isn't
230 	 *       recomended
231 	 *     Use the template 'get' as it will return the type of that component
232 	 *     Or use 'contains', 'containsAny', 'containsAll' if you want to know
233 	 *       which components an entity is holding
234 	 */
235 	@safe pure
236 	public IComponent[] getAll()
237 	{
238 		import std.array : array;
239 		return components.byValue.array;
240 	}
241 
242 
243 	/**
244 	 * Contains a component
245 	 *
246 	 * You call this function by passing the component's id
247 	 * Every time you create a component, it'll generate an unique id
248 	 * If you don't know which id you should pass, use the other variant of this
249 	 *   function, which is the recomended one, as it will get the correct id
250 	 *   for you
251 	 *
252 	 * Returns:
253 	 *     True if the entity contains the component
254 	 *     False otherwise
255 	 *
256 	 * Examples:
257 	 * --------------------
258 	 * e.contains(world.component.idOf!Foo);
259 	 * --------------------
260 	 *
261 	 * See_Also: contains(C : IComponent)()
262 	 */
263 	@safe pure
264 	public bool contains(in string id) const
265 	{
266 		return (id in components) !is null;
267 	}
268 
269 
270 	/**
271 	 * Contains a component
272 	 *
273 	 * It's recomended to always use this function, as it will do the 'hard
274 	 *   work' for you
275 	 *
276 	 * Returns:
277 	 *     True if the entity contains the component
278 	 *     False otherwise
279 	 *
280 	 * Examples:
281 	 * --------------------
282 	 * e.contains!Foo;
283 	 * --------------------
284 	 */
285 	public bool contains(C : IComponent)() const
286 	{
287 		return contains(ComponentManager.idOf!C);
288 	}
289 
290 
291 	/**
292 	 * Contains components
293 	 *
294 	 * You call this function by passing an array of the component ids
295 	 * Every time you create a component, it'll generate an unique id
296 	 * If you don't know which ids you should pass, use the other variant of
297 	 *   this function, which is the recomended one, as it will get the correct
298 	 *   id for you
299 	 *
300 	 * Returns:
301 	 *     True if all the components exist
302 	 *     False otherwise
303 	 *
304 	 * Examples:
305 	 * --------------------
306 	 * e.containsAll([world.component.idOf!Foo, world.component.idOf!Bar]);
307 	 * --------------------
308 	 *
309 	 * See_Also: containsAll(C...)()
310 	 */
311 	@safe pure
312 	public bool containsAll(in string[] ids) const
313 	{
314 		foreach(id; ids)
315 			if (!contains(id))
316 				return false;
317 
318 		return true;
319 	}
320 
321 
322 	/**
323 	 * Contains components
324 	 *
325 	 * It's recomended to always use this function, as it will do the 'hard
326 	 *   work' for you
327 	 *
328 	 * Returns:
329 	 *     True if the entity contains all of the components
330 	 *     False otherwise
331 	 *
332 	 * Examples:
333 	 * --------------------
334 	 * e.containsAll!(Foo, Goo);
335 	 * --------------------
336 	 */
337 	@safe pure
338 	public bool containsAll(C...)() const
339 	{
340 		return containsAll(ids!C);
341 	}
342 
343 
344 	/**
345 	 * Contains any component
346 	 *
347 	 * You call this function by passing an array of the component ids
348 	 * Every time you create a component, it'll generate an unique id
349 	 * If you don't know which ids you should pass, use the other variant of
350 	 *   this function, which is the recomended one, as it will get the correct
351 	 *   id for you
352 	 *
353 	 * Returns:
354 	 *     True if the entity contains at least one of the components
355 	 *     False otherwise
356 	 *
357 	 * Examples:
358 	 * --------------------
359 	 * e.containsAny([world.component.idOf!Foo, world.component.idOf!Bar]);
360 	 * --------------------
361 	 *
362 	 * See_Also: containsAny(C...)()
363 	 */
364 	@safe pure
365 	public bool containsAny(in string[] ids) const
366 	{
367 		foreach(id; ids)
368 			if (contains(id))
369 				return true;
370 
371 		return false;
372 	}
373 
374 
375 	/**
376 	 * Contains any component
377 	 *
378 	 * It's recomended to always use this function, as it will do the 'hard
379 	 *   work' for you
380 	 *
381 	 * Returns:
382 	 *     True if the entity contains at least one of the components
383 	 *     False otherwise
384 	 *
385 	 * Examples:
386 	 * --------------------
387 	 * e.hasAnyComponents!(Foo, Goo);
388 	 * --------------------
389 	 */
390 	@safe pure
391 	public bool containsAny(C...)() const
392 	{
393 		return containsAny(ids!C);
394 	}
395 
396 
397 	/**
398 	 * Ids
399 	 *
400 	 * Used internaly only
401 	 * Returns: ids of every component contained by this entity
402 	 */
403 	@safe pure
404 	private auto ids(C...)() const
405 	{
406 		import std.meta : staticMap;
407 		return [staticMap!(fullyQualifiedName, C)];
408 	}
409 
410 
411 	/**
412 	 * Detach
413 	 *
414 	 * Remove an entity from the world's scope
415 	 * Use this when you no longer need the entity
416 	 */
417 	@safe pure
418 	public void detach()
419 	{
420 		manager.detach(this);
421 	}
422 
423 
424 	public immutable size_t id;
425 	public string name;
426 	public string description;
427 	public bool enabled;
428 	private IComponent[string] components;
429 	private EntityManager manager;
430 }
431 
432 
433 version(unittest)
434 {
435 	final class unittest_FooComponent : IComponent { int a; }
436 	final class unittest_BarComponent : IComponent { int a; }
437 }
438 
439 
440 ///
441 @safe pure
442 @("Entity: Adding and removing a component twice")
443 unittest
444 {
445 	Entity e = new Entity(null, 0);
446 
447 	unittest_FooComponent f = new unittest_FooComponent();
448 	e.add(f); // First time the component is added, no error
449 	assertThrown!EntityComponentHandlingException(e.add(f), "Second time the same type is added");
450 
451 	e.remove!unittest_FooComponent; // First time the component is removed, no error
452 	assertThrown!EntityComponentHandlingException(e.remove!unittest_FooComponent,
453 				"Second time the type is being accessed");
454 }
455 
456 
457 ///
458 @safe pure
459 @("Entity: Getting and contained components")
460 unittest
461 {
462 	Entity e = new Entity(null, 0);
463 	unittest_FooComponent foo = new unittest_FooComponent();
464 	e.add(foo); // Entity components == [foo]
465 
466 	assertTrue(e.contains!unittest_FooComponent);
467 	assertTrue(e.containsAll!unittest_FooComponent);
468 	assertTrue(e.containsAny!(unittest_FooComponent, unittest_BarComponent), "Entity contains an unittets_FooComponent");
469 
470 	assertFalse(e.containsAll!(unittest_FooComponent, unittest_BarComponent),
471 				"Entity doesn't contain an unittest_BarComponent");
472 	assertFalse(e.containsAny!(unittest_BarComponent));
473 
474 	import std.range.primitives;
475 	auto arr = e.getAll;
476 
477 	import std.traits : ReturnType;
478 	assertTrue(is(ReturnType!(e.get!unittest_FooComponent) == unittest_FooComponent), "Returns the original type");
479 	assertTrue(foo is e.get!unittest_FooComponent);
480 	assertTrue(e.get!unittest_BarComponent is null, "Entity doesn't contain a type unittest_BarComponent component");
481 	assertEquals(1, arr.length, "Entity should contain only 1 component");
482 	assertTrue(is(typeof(arr.front) == IComponent));
483 }
484 
485 
486 ///
487 @safe pure
488 @("Entity: Entity clear")
489 unittest
490 {
491 	Entity e = new Entity(null, 0);
492 
493 	e.add(new unittest_FooComponent());
494 	e.clear();
495 
496 	assertEquals(0, e.getAll().length);
497 }
498 
499 
500 ///
501 @safe pure
502 @("Entity: Modify component contents")
503 unittest
504 {
505 	Entity e = new Entity(null, 0);
506 	e.add!unittest_FooComponent;
507 
508 	assertEquals(int.init, e.get!unittest_FooComponent.a);
509 
510 	e.modify!unittest_FooComponent(4);
511 
512 	assertEquals(4, e.get!unittest_FooComponent.a);
513 	assertThrown!EntityComponentHandlingException(e.modify!unittest_BarComponent(5));
514 }