1 /**
2  * @file
3  * @brief functions to handle the storage of all edicts in the game module.
4  */
5 
6 /*
7 All original material Copyright (C) 2002-2013 UFO: Alien Invasion.
8 
9 Original file from Quake 2 v3.21: quake2-2.31/game/g_utils.c
10 Copyright (C) 1997-2001 Id Software, Inc.
11 
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 as published by the Free Software Foundation; either version 2
15 of the License, or (at your option) any later version.
16 
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 
21 See the GNU General Public License for more details.
22 
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26 
27 */
28 
29 #include "g_edicts.h"
30 #include "g_actor.h"
31 
32 /** This is where we store the edicts */
33 static Edict* g_edicts;
34 
35 /**
36  * @brief Allocate space for the entity pointers.
37  * @note No need to set it to zero, G_TagMalloc will do that for us
38  */
G_EdictsConstruct(void)39 Edict* G_EdictsConstruct (void)
40 {
41 	g_edicts = (Edict*)G_TagMalloc(game.sv_maxentities * sizeof(g_edicts[0]), TAG_GAME);
42 	return g_edicts;
43 }
44 
45 /**
46  * @brief Reset the entity pointers for eg. a new game.
47  */
G_EdictsInit(void)48 void G_EdictsInit (void)
49 {
50 	for (int i = 0; i < game.sv_maxentities; i++)
51 		g_edicts[i].init();
52 }
53 
54 /**
55  * @brief Get an entity's ID number
56  * @param ent The entity
57  * @return the entity's ID number, currently the index in the array
58  * @note DO NOT use this number as anything other than an identifier !
59  */
G_EdictsGetNumber(const Edict * ent)60 int G_EdictsGetNumber (const Edict* ent)
61 {
62 	int idx = ent - g_edicts;
63 	assert(idx >= 0 && idx < globals.num_edicts);
64 	return idx;
65 }
66 
67 /**
68  * @brief Check if the given number could point to an existing entity
69  * @note entity must also be 'in use' to be really valid
70  * @param[in] num The entity's index in the array of entities
71  */
G_EdictsIsValidNum(const int num)72 bool G_EdictsIsValidNum (const int num)
73 {
74 	if (num >= 0 && num < globals.num_edicts)
75 		return true;
76 	return false;
77 }
78 
79 /**
80  * @brief Get an entity by it's number
81  * @param[in] num The entity's index in the array of entities
82  */
G_EdictsGetByNum(const int num)83 Edict* G_EdictsGetByNum (const int num)
84 {
85 	if (!G_EdictsIsValidNum(num)) {
86 		gi.DPrintf("Invalid edict num %i\n", num);
87 		return nullptr;
88 	}
89 
90 	return g_edicts + num;
91 }
92 
93 /**
94  * @brief Returns the first entity
95  * @note This is always a world edict - but in case of rma there might be several parts
96  * of the world spread all over the array.
97  */
G_EdictsGetFirst(void)98 Edict* G_EdictsGetFirst (void)
99 {
100 	return &g_edicts[0];
101 }
102 
103 /**
104  * @brief Iterate through the list of entities
105  * @param lastEnt The entity found in the previous iteration; if nullptr, we start at the beginning
106  */
G_EdictsGetNext(Edict * lastEnt)107 Edict* G_EdictsGetNext (Edict* lastEnt)
108 {
109 	Edict* endOfEnts = &g_edicts[globals.num_edicts];
110 	Edict* ent;
111 
112 	if (!globals.num_edicts)
113 		return nullptr;
114 
115 	if (!lastEnt)
116 		return g_edicts;
117 	assert(lastEnt >= g_edicts);
118 	assert(lastEnt < endOfEnts);
119 
120 	ent = lastEnt;
121 
122 	ent++;
123 	if (ent >= endOfEnts)
124 		return nullptr;
125 
126 	return ent;
127 }
128 
G_EdictDuplicate(const Edict * edict)129 Edict* G_EdictDuplicate (const Edict* edict)
130 {
131 	Edict* duplicate = G_EdictsGetNewEdict();
132 	if (duplicate == nullptr)
133 		return nullptr;
134 	memcpy(duplicate, edict, sizeof(*edict));
135 	duplicate->number = G_EdictsGetNumber(duplicate);
136 	return duplicate;
137 }
138 
139 /**
140  * @brief Find an entity that is not in use
141  */
G_EdictsGetNewEdict(void)142 Edict* G_EdictsGetNewEdict (void)
143 {
144 	Edict* ent = nullptr;
145 
146 	/* try to recycle an edict */
147 	while ((ent = G_EdictsGetNext(ent))) {
148 		if (!ent->inuse)
149 			return ent;
150 	}
151 
152 	/* no unused edict found, create a new one */
153 	ent = &g_edicts[globals.num_edicts];
154 	globals.num_edicts++;
155 	if (globals.num_edicts > game.sv_maxentities)
156 		return nullptr;
157 
158 	return ent;
159 }
160 
161 /**
162  * @brief Iterate through the entities that are in use
163  * @note we can hopefully get rid of this function once we know when it makes sense
164  * to iterate through entities that are NOT in use
165  * @param lastEnt The entity found in the previous iteration; if nullptr, we start at the beginning
166  */
G_EdictsGetNextInUse(Edict * lastEnt)167 Edict* G_EdictsGetNextInUse (Edict* lastEnt)
168 {
169 	Edict* ent = lastEnt;
170 
171 	while ((ent = G_EdictsGetNext(ent))) {
172 		if (ent->inuse)
173 			break;
174 	}
175 	return ent;
176 }
177 
178 /**
179  * @brief Iterator through all the trigger_nextmap edicts
180  * @param lastEnt The entity found in the previous iteration; if nullptr, we start at the beginning
181  */
G_EdictsGetTriggerNextMaps(Edict * lastEnt)182 Edict* G_EdictsGetTriggerNextMaps (Edict* lastEnt)
183 {
184 	Edict* ent = lastEnt;
185 
186 	while ((ent = G_EdictsGetNextInUse(ent))) {
187 		if (G_IsTriggerNextMap(ent))
188 			break;
189 	}
190 	return ent;
191 }
192 
193 /**
194  * @brief Iterate through the living actor entities
195  * @param lastEnt The entity found in the previous iteration; if nullptr, we start at the beginning
196  */
G_EdictsGetNextLivingActor(Edict * lastEnt)197 Edict* G_EdictsGetNextLivingActor (Edict* lastEnt)
198 {
199 	Edict* ent = lastEnt;
200 
201 	while ((ent = G_EdictsGetNextInUse(ent))) {
202 		if (G_IsLivingActor(ent))
203 			break;
204 	}
205 	return ent;
206 }
207 
208 /**
209  * @brief Iterate through the living actor entities of the given team
210  * @param lastEnt The entity found in the previous iteration; if nullptr, we start at the beginning
211  * @param team The team we are looking for
212  */
G_EdictsGetNextLivingActorOfTeam(Edict * lastEnt,const int team)213 Edict* G_EdictsGetNextLivingActorOfTeam (Edict* lastEnt, const int team)
214 {
215 	Edict* ent = lastEnt;
216 
217 	while ((ent = G_EdictsGetNextLivingActor(ent))) {
218 		if (ent->team == team)
219 			break;
220 	}
221 	return ent;
222 }
223 
224 /**
225  * @brief Iterate through the actor entities (even the dead! - but skips the invisible)
226  * @param lastEnt The entity found in the previous iteration; if nullptr, we start at the beginning
227  */
G_EdictsGetNextActor(Edict * lastEnt)228 Edict* G_EdictsGetNextActor (Edict* lastEnt)
229 {
230 	Edict* ent = lastEnt;
231 
232 	assert(lastEnt < &g_edicts[globals.num_edicts]);
233 
234 	while ((ent = G_EdictsGetNextInUse(ent))) {
235 		if (G_IsActor(ent))
236 			break;
237 	}
238 	return ent;
239 }
240 
241 /**
242  * @brief Searches an actor by a unique character number
243  * @param[in] ucn The unique character number
244  * @param[in] team The team to get the actor with the ucn from
245  * @return The actor edict if found, otherwise @c nullptr
246  */
G_EdictsGetActorByUCN(const int ucn,const int team)247 Edict* G_EdictsGetActorByUCN (const int ucn, const int team)
248 {
249 	Edict* ent = nullptr;
250 
251 	while ((ent = G_EdictsGetNextActor(ent)))
252 		if (team == ent->team && ent->chr.ucn == ucn)
253 			return ent;
254 
255 	return nullptr;
256 }
257 
258 /**
259  * @brief Searches an actor at the given grid location.
260  * @param pos The grid location to look for an edict.
261  * @return @c nullptr if nothing was found, otherwise the actor located at the given grid position.
262  */
G_EdictsGetLivingActorFromPos(const pos3_t pos)263 Edict* G_EdictsGetLivingActorFromPos (const pos3_t pos)
264 {
265 	Edict* ent = nullptr;
266 
267 	while ((ent = G_EdictsGetNextLivingActor(ent))) {
268 		if (!VectorCompare(pos, ent->pos))
269 			continue;
270 
271 		return ent;
272 	}
273 	/* nothing found at this pos */
274 	return nullptr;
275 }
276 
277 /**
278  * @brief Searches the edict that has the given target as @c targetname set
279  * @param target The target name of the edict that you are searching
280  * @return @c nullptr if no edict with the given target name was found, otherwise
281  * the edict that has the targetname set you were looking for.
282  */
G_EdictsFindTargetEntity(const char * target)283 Edict* G_EdictsFindTargetEntity (const char* target)
284 {
285 	Edict* ent = nullptr;
286 
287 	while ((ent = G_EdictsGetNextInUse(ent))) {
288 		const char* n = ent->targetname;
289 		if (n && Q_streq(n, target))
290 			return ent;
291 	}
292 
293 	return nullptr;
294 }
295 
296 /**
297  * @brief Calculate the edict's origin vector from it's grid position
298  * @param ent The entity
299  */
G_EdictCalcOrigin(Edict * ent)300 void G_EdictCalcOrigin (Edict* ent)
301 {
302 	gi.GridPosToVec(ent->fieldSize, ent->pos, ent->origin);
303 }
304 
305 /**
306  * @brief Set the edict's pos and origin vector to the given grid position
307  * @param ent The entity
308  * @param newPos The new grid position
309  */
G_EdictSetOrigin(Edict * ent,const pos3_t newPos)310 void G_EdictSetOrigin (Edict* ent, const pos3_t newPos)
311 {
312 	VectorCopy(newPos, ent->pos);
313 	gi.GridPosToVec(ent->fieldSize, ent->pos, ent->origin);
314 }
315 
316 /**
317  * @brief Check whether the edict is on the given position
318  * @param ent The entity
319  * @param cmpPos The grid position to compare to
320  * @return true if positions are equal
321  */
G_EdictPosIsSameAs(const Edict * ent,const pos3_t cmpPos)322 bool G_EdictPosIsSameAs (const Edict* ent, const pos3_t cmpPos)
323 {
324 	return VectorCompare(cmpPos, ent->pos);
325 }
326 
327 /**
328  * @brief Called every frame to let the edicts tick
329  */
G_EdictsThink(void)330 void G_EdictsThink (void)
331 {
332 	/* treat each object in turn */
333 	/* even the world gets a chance to think */
334 	Edict* ent = nullptr;
335 	while ((ent = G_EdictsGetNextInUse(ent))) {
336 		if (!ent->think)
337 			continue;
338 		if (ent->nextthink <= 0)
339 			continue;
340 		if (ent->nextthink > level.time + 0.001f)
341 			continue;
342 
343 		ent->nextthink = level.time + SERVER_FRAME_SECONDS;
344 		ent->think(ent);
345 	}
346 }
347