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