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.entitymanager;
38 
39 import aurorafw.entity.entity;
40 import aurorafw.entity.icomponent;
41 import aurorafw.entity.componentmanager;
42 import aurorafw.entity.world;
43 
44 version(unittest) import aurorafw.unit.assertion;
45 
46 import std.range;
47 import std.exception;
48 
49 
50 class EntityManagerHandlingException : Exception
51 {
52 	mixin basicExceptionCtors;
53 }
54 
55 
56 final class EntityManager
57 {
58 	/**
59 	 * Create an entity
60 	 *
61 	 * Creates a new entity with an unique id
62 	 *
63 	 * Examples:
64 	 * --------------------
65 	 * world.entity.create();
66 	 * Entity e = world.entity.create();
67 	 * --------------------
68 	 */
69 	@safe pure
70 	public Entity create()
71 	{
72 		Entity e = new Entity(this, !this.delEntities.empty ? popDeletedId : nextId++);
73 		this.mEntities[e.id] = e;
74 
75 		return e;
76 	}
77 
78 
79 	/**
80 	 * Pop deleted id
81 	 *
82 	 * Reuses an id which belonged to another entity before
83 	 * If there's not an id available in this, it returns null
84 	 *
85 	 * This function is only used inernaly for the creation of entities
86 	 */
87 	@safe pure
88 	private size_t popDeletedId()
89 	{
90 		size_t eid = this.delEntities.front;
91 		this.delEntities = this.delEntities.length > 1 ? this.delEntities[1 .. $] : null;
92 
93 		return eid;
94 	}
95 
96 
97 	/**
98 	 * Exists
99 	 *
100 	 * Returns whether an entity exists in the scope
101 	 *
102 	 * Examples:
103 	 * --------------------
104 	 * world.manager.create();
105 	 * world.entity.exists(0); // true
106 	 * --------------------
107 	 */
108 	@safe pure
109 	public bool exists(in size_t eid) const
110 	{
111 		return (eid in this.mEntities) !is null;
112 	}
113 
114 
115 	/**
116 	 * Get an entity
117 	 *
118 	 * Returns the entity which contains the id passed
119 	 * If there are none, it returns null
120 	 *
121 	 * Examples:
122 	 * --------------------
123 	 * Entity e = world.entity.get(16);
124 	 * --------------------
125 	 */
126 	@safe pure
127 	public Entity get(in size_t eid)
128 	{
129 		Entity* p;
130 		return ((p = eid in this.mEntities) !is null) ? *p : null;
131 	}
132 
133 
134 	/**
135 	 * Get the first entity in the scope with a component
136 	 *
137 	 * Returns the first entity with the component passed
138 	 * If there's none, it returns null
139 	 *
140 	 * Examples:
141 	 * --------------------
142 	 * Entity e = world.entity.getFirstWith!Foo;
143 	 * --------------------
144 	 */
145 	@safe pure
146 	public Entity getFirstWith(C : IComponent)()
147 	{
148 		foreach(Entity e; this.mEntities)
149 			if (e.contains!C)
150 				return e;
151 
152 		return null;
153 	}
154 
155 
156 	/**
157 	 * Get the first entity in the scope which contains a group of components
158 	 *
159 	 * Returns the first entity with the components passed
160 	 *
161 	 * Examples:
162 	 * --------------------
163 	 * Entity e = world.entity.getFirstWith!(Component1, Component2, Component3, ...);
164 	 * --------------------
165 	 */
166 	@safe pure
167 	public Entity getFirstWith(T...)()
168 	{
169 		foreach(Entity e; this.mEntities)
170 			if (e.containsAll!T)
171 				return e;
172 
173 		return null;
174 	}
175 
176 
177 	/**
178 	 * Get every entity in the scope which contains a component
179 	 *
180 	 * Returns an array of type Entity with every entity which contains the component passed
181 	 *
182 	 * Examples:
183 	 * --------------------
184 	 * Entity[] arr = world.entity.getAllWith!Foo;
185 	 * --------------------
186 	 */
187 	@safe pure
188 	public Entity[] getAllWith(T : IComponent)()
189 	{
190 		Entity[] ret;
191 		foreach(Entity e; this.mEntities)
192 			if (e.contains!T)
193 				ret ~= e;
194 
195 		return ret;
196 	}
197 
198 
199 	/**
200 	 * Get every entity in the scope which contains a group of components
201 	 *
202 	 * Returns an array of type Entity with every entity which contains the components passed
203 	 *
204 	 * Examples:
205 	 * --------------------
206 	 * Entity[] arr = world.entity.getAllWith!(Component1, Component2, Component3, ...);
207 	 * --------------------
208 	 */
209 	@safe pure
210 	public Entity[] getAllWith(T...)()
211 	{
212 		Entity[] ret;
213 		foreach(Entity e; this.mEntities)
214 			if (e.containsAll!T)
215 				ret ~= e;
216 
217 		return ret;
218 	}
219 
220 
221 	/**
222 	 * Get all the deleted ids
223 	 *
224 	 * Returns an array with all the deleted ids currently in the scope
225 	 *
226 	 * Examples:
227 	 * --------------------
228 	 * auto[] arr = world.entity.getDeletedIds();
229 	 * --------------------
230 	 */
231 	@safe pure
232 	public const size_t[] getDeletedIds()
233 	{
234 		return this.delEntities.dup;
235 	}
236 
237 
238 	/**
239 	 * Detach
240 	 *
241 	 * Removes an entity from the world's scope if it exists
242 	 *
243 	 * Examples:
244 	 * --------------------
245 	 * world.entity.detach(e);
246 	 * --------------------
247 	 */
248 	@safe pure
249 	public void detach(Entity e)
250 	{
251 		Entity* p;
252 		p = e.id in this.mEntities;
253 
254 		if (p !is null && e is this.mEntities[e.id])
255 		{
256 			this.mEntities.remove(e.id);
257 			this.delEntities ~= e.id;
258 		}
259 		else
260 			throw new EntityManagerHandlingException(
261 				"Cannot detach entity. Entity doesn't exist in the world's scope."
262 			);
263 	}
264 
265 
266 	/*
267 	 * Clear
268 	 *
269 	 * Removes every entity from the world's scope
270 	 */
271 	@safe pure
272 	public void clear()
273 	{
274 		foreach(key, value; this.mEntities)
275 		{
276 			this.delEntities ~= key;
277 			this.mEntities.remove(key);
278 		}
279 	}
280 
281 
282 	private size_t nextId = 0;
283 	private Entity[size_t] mEntities;
284 	private size_t[] delEntities;
285 }
286 
287 
288 ///
289 @safe pure
290 @("Entity Manager: Entity id counter")
291 unittest
292 {
293 	EntityManager manager = new EntityManager();
294 
295 	Entity e1 = manager.create();
296 	Entity e2 = manager.create();
297 
298 	assertTrue(manager.exists(0));
299 
300 	assertEquals(0, e1.id); // Entity id begins in 0
301 	assertEquals(1, e2.id);
302 
303 	e1.detach(); // Entity id is removed from the world's scope
304 
305 	assertEquals(0, manager.getDeletedIds.front);
306 
307 	Entity e3 = manager.create(); // Entity gets an id which was removed previously
308 
309 	assertEquals(0, e3.id);
310 	assertEquals(0, manager.getDeletedIds.length); // Entity id is no longer deleted
311 
312 	assertThrown!EntityManagerHandlingException(manager.detach(e1)); // This entity is no longer in the world's scope
313 }
314 
315 
316 ///
317 @safe pure
318 @("Entity Manager: Entities getAllWith and getFirstWith")
319 unittest
320 {
321 	final class Foo : IComponent {}
322 	final class Bar : IComponent {}
323 	final class Foobar : IComponent {}
324 
325 	EntityManager manager = new EntityManager();
326 
327 	Entity e1 = manager.create();
328 	Entity e2 = manager.create();
329 	Entity e3 = manager.create();
330 
331 	e1.add(new Foo());
332 	e1.add(new Bar());
333 
334 	e2.add(new Foo());
335 	e2.add(new Bar());
336 
337 	e3.add(new Foo());
338 
339 	auto arr = manager.getAllWith!(Foo, Bar);
340 	auto e = manager.getFirstWith!Foo;
341 
342 	import std.algorithm.comparison : equal;
343 	assertTrue(equal!((a,b) => a is b)([e1, e2], arr));
344 	assertTrue(e is e1);
345 
346 	assertTrue(manager.getFirstWith!Foobar is null); // There are no entities with this Component
347 }
348 
349 
350 ///
351 @safe pure
352 @("Entity Manager: Entity getter")
353 unittest
354 {
355 	EntityManager manager = new EntityManager();
356 
357 	Entity e = manager.create();
358 
359 	assertTrue(e is manager.get(e.id));
360 }
361 
362 
363 ///
364 @safe pure
365 @("Entity Manager: Entities clear")
366 unittest
367 {
368 	EntityManager manager = new EntityManager();
369 	Entity e = manager.create();
370 	manager.clear();
371 
372 	assertEquals(0, manager.mEntities.length);
373 }