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