1 // g_utils.c -- misc utility functions for game module
2
3 #include "g_local.h"
4
5
G_ProjectSource(vec3_t point,vec3_t distance,vec3_t forward,vec3_t right,vec3_t result)6 void G_ProjectSource (vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result)
7 {
8 result[0] = point[0] + forward[0] * distance[0] + right[0] * distance[1];
9 result[1] = point[1] + forward[1] * distance[0] + right[1] * distance[1];
10 result[2] = point[2] + forward[2] * distance[0] + right[2] * distance[1] + distance[2];
11 }
12
13
14 /*
15 =============
16 G_Find
17
18 Searches all active entities for the next one that holds
19 the matching string at fieldofs (use the FOFS() macro) in the structure.
20
21 Searches beginning at the edict after from, or the beginning if NULL
22 NULL will be returned if the end of the list is reached.
23
24 =============
25 */
G_Find(edict_t * from,int fieldofs,char * match)26 edict_t *G_Find (edict_t *from, int fieldofs, char *match)
27 {
28 char *s;
29
30 if (!from)
31 from = g_edicts;
32 else
33 from++;
34
35 for ( ; from < &g_edicts[globals.num_edicts] ; from++)
36 {
37 if (!from->inuse)
38 continue;
39 s = *(char **) ((byte *)from + fieldofs);
40 if (!s)
41 continue;
42 if (!Q_stricmp (s, match))
43 return from;
44 }
45
46 return NULL;
47 }
48
49
50 /*
51 =================
52 findradius
53
54 Returns entities that have origins within a spherical area
55
56 findradius (origin, radius)
57 =================
58 */
findradius(edict_t * from,vec3_t org,float rad)59 edict_t *findradius (edict_t *from, vec3_t org, float rad)
60 {
61 vec3_t eorg;
62 int j;
63
64 if (!from)
65 from = g_edicts;
66 else
67 from++;
68 for ( ; from < &g_edicts[globals.num_edicts]; from++)
69 {
70 if (!from->inuse)
71 continue;
72 if (from->solid == SOLID_NOT)
73 continue;
74 for (j=0 ; j<3 ; j++)
75 eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5);
76 if (VectorLength(eorg) > rad)
77 continue;
78 return from;
79 }
80
81 return NULL;
82 }
83
84
85 /*
86 =============
87 G_PickTarget
88
89 Searches all active entities for the next one that holds
90 the matching string at fieldofs (use the FOFS() macro) in the structure.
91
92 Searches beginning at the edict after from, or the beginning if NULL
93 NULL will be returned if the end of the list is reached.
94
95 =============
96 */
97 #define MAXCHOICES 8
98
G_PickTarget(char * targetname)99 edict_t *G_PickTarget (char *targetname)
100 {
101 edict_t *ent = NULL;
102 int num_choices = 0;
103 edict_t *choice[MAXCHOICES];
104
105 if (!targetname)
106 {
107 gi.dprintf("G_PickTarget called with NULL targetname\n");
108 return NULL;
109 }
110
111 while(1)
112 {
113 ent = G_Find (ent, FOFS(targetname), targetname);
114 if (!ent)
115 break;
116 choice[num_choices++] = ent;
117 if (num_choices == MAXCHOICES)
118 break;
119 }
120
121 if (!num_choices)
122 {
123 gi.dprintf("G_PickTarget: target %s not found\n", targetname);
124 return NULL;
125 }
126
127 return choice[rand() % num_choices];
128 }
129
130
131
Think_Delay(edict_t * ent)132 void Think_Delay (edict_t *ent)
133 {
134 G_UseTargets (ent, ent->activator);
135 G_FreeEdict (ent);
136 }
137
138 /*
139 ==============================
140 G_UseTargets
141
142 the global "activator" should be set to the entity that initiated the firing.
143
144 If self.delay is set, a DelayedUse entity will be created that will actually
145 do the SUB_UseTargets after that many seconds have passed.
146
147 Centerprints any self.message to the activator.
148
149 Search for (string)targetname in all entities that
150 match (string)self.target and call their .use function
151
152 ==============================
153 */
G_UseTargets(edict_t * ent,edict_t * activator)154 void G_UseTargets (edict_t *ent, edict_t *activator)
155 {
156 edict_t *t;
157
158 //
159 // check for a delay
160 //
161 if (ent->delay)
162 {
163 // create a temp object to fire at a later time
164 t = G_Spawn();
165 t->classname = "DelayedUse";
166 t->nextthink = level.time + ent->delay;
167 t->think = Think_Delay;
168 t->activator = activator;
169 if (!activator)
170 gi.dprintf ("Think_Delay with no activator\n");
171 t->message = ent->message;
172 t->target = ent->target;
173 t->killtarget = ent->killtarget;
174 return;
175 }
176
177
178 //
179 // print the message
180 //
181 if ((ent->message) && !(activator->svflags & SVF_MONSTER))
182 {
183 gi.centerprintf (activator, "%s", ent->message);
184 if (ent->noise_index)
185 gi.sound (activator, CHAN_AUTO, ent->noise_index, 1, ATTN_NORM, 0);
186 else
187 gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
188 }
189
190 //
191 // kill killtargets
192 //
193 if (ent->killtarget)
194 {
195 t = NULL;
196 while ((t = G_Find (t, FOFS(targetname), ent->killtarget)))
197 {
198 G_FreeEdict (t);
199 if (!ent->inuse)
200 {
201 gi.dprintf("entity was removed while using killtargets\n");
202 return;
203 }
204 }
205 }
206
207 //
208 // fire targets
209 //
210 if (ent->target)
211 {
212 t = NULL;
213 while ((t = G_Find (t, FOFS(targetname), ent->target)))
214 {
215 // doors fire area portals in a specific way
216 if (!Q_stricmp(t->classname, "func_areaportal") &&
217 (!Q_stricmp(ent->classname, "func_door") || !Q_stricmp(ent->classname, "func_door_rotating")))
218 continue;
219
220 if (t == ent)
221 {
222 gi.dprintf ("WARNING: Entity used itself.\n");
223 }
224 else
225 {
226 if (t->use)
227 t->use (t, ent, activator);
228 }
229 if (!ent->inuse)
230 {
231 gi.dprintf("entity was removed while using targets\n");
232 return;
233 }
234 }
235 }
236 }
237
238
239 /*
240 =============
241 TempVector
242
243 This is just a convenience function
244 for making temporary vectors for function calls
245 =============
246 */
tv(float x,float y,float z)247 float *tv (float x, float y, float z)
248 {
249 static int index;
250 static vec3_t vecs[8];
251 float *v;
252
253 // use an array so that multiple tempvectors won't collide
254 // for a while
255 v = vecs[index];
256 index = (index + 1)&7;
257
258 v[0] = x;
259 v[1] = y;
260 v[2] = z;
261
262 return v;
263 }
264
265
266 /*
267 =============
268 VectorToString
269
270 This is just a convenience function
271 for printing vectors
272 =============
273 */
vtos(vec3_t v)274 char *vtos (vec3_t v)
275 {
276 static int index;
277 static char str[8][32];
278 char *s;
279
280 // use an array so that multiple vtos won't collide
281 s = str[index];
282 index = (index + 1)&7;
283
284 Com_sprintf (s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]);
285
286 return s;
287 }
288
289
290 vec3_t VEC_UP = {0, -1, 0};
291 vec3_t MOVEDIR_UP = {0, 0, 1};
292 vec3_t VEC_DOWN = {0, -2, 0};
293 vec3_t MOVEDIR_DOWN = {0, 0, -1};
294
G_SetMovedir(vec3_t angles,vec3_t movedir)295 void G_SetMovedir (vec3_t angles, vec3_t movedir)
296 {
297 if (VectorCompare (angles, VEC_UP))
298 {
299 VectorCopy (MOVEDIR_UP, movedir);
300 }
301 else if (VectorCompare (angles, VEC_DOWN))
302 {
303 VectorCopy (MOVEDIR_DOWN, movedir);
304 }
305 else
306 {
307 AngleVectors (angles, movedir, NULL, NULL);
308 }
309
310 VectorClear (angles);
311 }
312
313
vectoyaw(vec3_t vec)314 float vectoyaw (vec3_t vec)
315 {
316 float yaw;
317
318 if (/*vec[YAW] == 0 &&*/ vec[PITCH] == 0)
319 {
320 yaw = 0;
321 if (vec[YAW] > 0)
322 yaw = 90;
323 else if (vec[YAW] < 0)
324 yaw = -90;
325 }
326 else
327 {
328 yaw = (int) (atan2(vec[YAW], vec[PITCH]) * 180 / M_PI);
329 if (yaw < 0)
330 yaw += 360;
331 }
332
333 return yaw;
334 }
335
336
vectoangles(vec3_t value1,vec3_t angles)337 void vectoangles (vec3_t value1, vec3_t angles)
338 {
339 float forward;
340 float yaw, pitch;
341
342 if (value1[1] == 0 && value1[0] == 0)
343 {
344 yaw = 0;
345 if (value1[2] > 0)
346 pitch = 90;
347 else
348 pitch = 270;
349 }
350 else
351 {
352 if (value1[0])
353 yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
354 else if (value1[1] > 0)
355 yaw = 90;
356 else
357 yaw = -90;
358 if (yaw < 0)
359 yaw += 360;
360
361 forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
362 pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
363 if (pitch < 0)
364 pitch += 360;
365 }
366
367 angles[PITCH] = -pitch;
368 angles[YAW] = yaw;
369 angles[ROLL] = 0;
370 }
371
372
G_CopyString(char * in)373 char *G_CopyString (char *in)
374 {
375 char *out;
376
377 out = gi.TagMalloc (strlen(in)+1, TAG_LEVEL);
378 strcpy (out, in);
379 return out;
380 }
381
382
G_InitEdict(edict_t * e)383 void G_InitEdict (edict_t *e)
384 {
385 e->inuse = true;
386 e->classname = "noclass";
387 e->gravity = 1.0;
388 e->s.number = e - g_edicts;
389 }
390
391 /*
392 =================
393 G_Spawn
394
395 Either finds a free edict, or allocates a new one.
396 Try to avoid reusing an entity that was recently freed, because it
397 can cause the client to think the entity morphed into something else
398 instead of being removed and recreated, which can cause interpolated
399 angles and bad trails.
400 =================
401 */
G_Spawn(void)402 edict_t *G_Spawn (void)
403 {
404 int i;
405 edict_t *e;
406
407 e = &g_edicts[(int)maxclients->value+1];
408 for ( i=maxclients->value+1 ; i<globals.num_edicts ; i++, e++)
409 {
410 // the first couple seconds of server time can involve a lot of
411 // freeing and allocating, so relax the replacement policy
412 if (!e->inuse && ( e->freetime < 2 || level.time - e->freetime > 0.5 ) )
413 {
414 G_InitEdict (e);
415 return e;
416 }
417 }
418
419 if (i == game.maxentities)
420 gi.error ("ED_Alloc: no free edicts");
421
422 globals.num_edicts++;
423 G_InitEdict (e);
424 return e;
425 }
426
427 /*
428 =================
429 G_FreeEdict
430
431 Marks the edict as free
432 =================
433 */
G_FreeEdict(edict_t * ed)434 void G_FreeEdict (edict_t *ed)
435 {
436 CleanUpEnt (ed);
437 gi.unlinkentity (ed); // unlink from world
438
439 if ((ed - g_edicts) <= (maxclients->value + BODY_QUEUE_SIZE))
440 {
441 // gi.dprintf("tried to free special edict\n");
442 return;
443 }
444
445 memset (ed, 0, sizeof(*ed));
446 ed->classname = "freed";
447 ed->freetime = level.time;
448 ed->inuse = false;
449 }
450
451
452 /*
453 ============
454 G_TouchTriggers
455
456 ============
457 */
G_TouchTriggers(edict_t * ent)458 void G_TouchTriggers (edict_t *ent)
459 {
460 int i, num;
461 edict_t *touch[MAX_EDICTS], *hit;
462
463 // dead things don't activate triggers!
464 if (((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0))||(ent->IsGrapple))
465 return;
466
467 num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
468 , MAX_EDICTS, AREA_TRIGGERS);
469
470 // be careful, it is possible to have an entity in this
471 // list removed before we get to it (killtriggered)
472 for (i=0 ; i<num ; i++)
473 {
474 hit = touch[i];
475 if (!hit->inuse)
476 continue;
477 if (!hit->touch)
478 continue;
479 hit->touch (hit, ent, NULL, NULL);
480 }
481 }
482
483 /*
484 ============
485 G_TouchSolids
486
487 Call after linking a new trigger in during gameplay
488 to force all entities it covers to immediately touch it
489 ============
490 */
G_TouchSolids(edict_t * ent)491 void G_TouchSolids (edict_t *ent)
492 {
493 int i, num;
494 edict_t *touch[MAX_EDICTS], *hit;
495
496 num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
497 , MAX_EDICTS, AREA_SOLID);
498
499 // be careful, it is possible to have an entity in this
500 // list removed before we get to it (killtriggered)
501 for (i=0 ; i<num ; i++)
502 {
503 hit = touch[i];
504 if (!hit->inuse)
505 continue;
506 if (ent->touch)
507 ent->touch (hit, ent, NULL, NULL);
508 if (!ent->inuse)
509 break;
510 }
511 }
512
513
514
515
516 /*
517 ==============================================================================
518
519 Kill box
520
521 ==============================================================================
522 */
523
524 /*
525 =================
526 KillBox
527
528 Kills all entities that would touch the proposed new positioning
529 of ent. Ent should be unlinked before calling this!
530 =================
531 */
KillBox(edict_t * ent)532 qboolean KillBox (edict_t *ent)
533 {
534 trace_t tr;
535
536 while (1)
537 {
538 tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID);
539 if (!tr.ent)
540 break;
541
542 // nail it
543 T_Damage (tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
544
545 // if we didn't kill it, fail
546 if (tr.ent->solid)
547 return false;
548 }
549
550 return true; // all clear
551 }
552