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 #ifdef WITH_ACEBOT
184 safe_centerprintf
185 #else
186 gi.centerprintf
187 #endif
188
189 (activator, "%s", ent->message);
190 if (ent->noise_index)
191 gi.sound (activator, CHAN_AUTO, ent->noise_index, 1, ATTN_NORM, 0);
192 else
193 gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
194 }
195
196 //
197 // kill killtargets
198 //
199 if (ent->killtarget)
200 {
201 t = NULL;
202 while ((t = G_Find (t, FOFS(targetname), ent->killtarget)))
203 {
204 G_FreeEdict (t);
205 if (!ent->inuse)
206 {
207 gi.dprintf("entity was removed while using killtargets\n");
208 return;
209 }
210 }
211 }
212
213 //
214 // fire targets
215 //
216 if (ent->target)
217 {
218 t = NULL;
219 while ((t = G_Find (t, FOFS(targetname), ent->target)))
220 {
221 // doors fire area portals in a specific way
222 if (!Q_stricmp(t->classname, "func_areaportal") &&
223 (!Q_stricmp(ent->classname, "func_door") || !Q_stricmp(ent->classname, "func_door_rotating")))
224 continue;
225
226 if (t == ent)
227 {
228 gi.dprintf ("WARNING: Entity used itself.\n");
229 }
230 else
231 {
232 if (t->use)
233 t->use (t, ent, activator);
234 }
235 if (!ent->inuse)
236 {
237 gi.dprintf("entity was removed while using targets\n");
238 return;
239 }
240 }
241 }
242 }
243
244
245 /*
246 =============
247 TempVector
248
249 This is just a convenience function
250 for making temporary vectors for function calls
251 =============
252 */
tv(float x,float y,float z)253 float *tv (float x, float y, float z)
254 {
255 static int index;
256 static vec3_t vecs[8];
257 float *v;
258
259 // use an array so that multiple tempvectors won't collide
260 // for a while
261 v = vecs[index];
262 index = (index + 1)&7;
263
264 v[0] = x;
265 v[1] = y;
266 v[2] = z;
267
268 return v;
269 }
270
271
272 /*
273 =============
274 VectorToString
275
276 This is just a convenience function
277 for printing vectors
278 =============
279 */
vtos(vec3_t v)280 char *vtos (vec3_t v)
281 {
282 static int index;
283 static char str[8][32];
284 char *s;
285
286 // use an array so that multiple vtos won't collide
287 s = str[index];
288 index = (index + 1)&7;
289
290 Com_sprintf (s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]);
291
292 return s;
293 }
294
295
296 vec3_t VEC_UP = {0, -1, 0};
297 vec3_t MOVEDIR_UP = {0, 0, 1};
298 vec3_t VEC_DOWN = {0, -2, 0};
299 vec3_t MOVEDIR_DOWN = {0, 0, -1};
300
G_SetMovedir(vec3_t angles,vec3_t movedir)301 void G_SetMovedir (vec3_t angles, vec3_t movedir)
302 {
303 if (VectorCompare (angles, VEC_UP))
304 {
305 VectorCopy (MOVEDIR_UP, movedir);
306 }
307 else if (VectorCompare (angles, VEC_DOWN))
308 {
309 VectorCopy (MOVEDIR_DOWN, movedir);
310 }
311 else
312 {
313 AngleVectors (angles, movedir, NULL, NULL);
314 }
315
316 VectorClear (angles);
317 }
318
319
vectoyaw(vec3_t vec)320 float vectoyaw (vec3_t vec)
321 {
322 float yaw;
323
324 if (/*vec[YAW] == 0 &&*/ vec[PITCH] == 0)
325 {
326 yaw = 0;
327 if (vec[YAW] > 0)
328 yaw = 90;
329 else if (vec[YAW] < 0)
330 yaw = -90;
331 }
332 else
333 {
334 yaw = (int) (atan2(vec[YAW], vec[PITCH]) * 180 / M_PI);
335 if (yaw < 0)
336 yaw += 360;
337 }
338
339 return yaw;
340 }
341
342
vectoangles(vec3_t value1,vec3_t angles)343 void vectoangles (vec3_t value1, vec3_t angles)
344 {
345 float forward;
346 float yaw, pitch;
347
348 if (value1[1] == 0 && value1[0] == 0)
349 {
350 yaw = 0;
351 if (value1[2] > 0)
352 pitch = 90;
353 else
354 pitch = 270;
355 }
356 else
357 {
358 if (value1[0])
359 yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
360 else if (value1[1] > 0)
361 yaw = 90;
362 else
363 yaw = -90;
364 if (yaw < 0)
365 yaw += 360;
366
367 forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
368 pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
369 if (pitch < 0)
370 pitch += 360;
371 }
372
373 angles[PITCH] = -pitch;
374 angles[YAW] = yaw;
375 angles[ROLL] = 0;
376 }
377
G_CopyString(char * in)378 char *G_CopyString (char *in)
379 {
380 char *out;
381
382 out = gi.TagMalloc (strlen(in)+1, TAG_LEVEL);
383 strcpy (out, in);
384 return out;
385 }
386
387
G_InitEdict(edict_t * e)388 void G_InitEdict (edict_t *e)
389 {
390 e->inuse = true;
391 e->classname = "noclass";
392 e->gravity = 1.0;
393 e->s.number = e - g_edicts;
394 }
395
396 /*
397 =================
398 G_Spawn
399
400 Either finds a free edict, or allocates a new one.
401 Try to avoid reusing an entity that was recently freed, because it
402 can cause the client to think the entity morphed into something else
403 instead of being removed and recreated, which can cause interpolated
404 angles and bad trails.
405 =================
406 */
G_Spawn(void)407 edict_t *G_Spawn (void)
408 {
409 int i;
410 edict_t *e;
411
412 e = &g_edicts[(int)maxclients->value+1];
413 for ( i=maxclients->value+1 ; i<globals.num_edicts ; i++, e++)
414 {
415 // the first couple seconds of server time can involve a lot of
416 // freeing and allocating, so relax the replacement policy
417 if (!e->inuse && ( e->freetime < 2 || level.time - e->freetime > 0.5 ) )
418 {
419 G_InitEdict (e);
420 return e;
421 }
422 }
423
424 if (i == game.maxentities)
425 gi.error ("ED_Alloc: no free edicts");
426
427 globals.num_edicts++;
428 G_InitEdict (e);
429 return e;
430 }
431
432 /*
433 =================
434 G_FreeEdict
435
436 Marks the edict as free
437 =================
438 */
G_FreeEdict(edict_t * ed)439 void G_FreeEdict (edict_t *ed)
440 {
441 gi.unlinkentity (ed); // unlink from world
442
443 if ((ed - g_edicts) <= (maxclients->value + BODY_QUEUE_SIZE))
444 {
445 // gi.dprintf("tried to free special edict\n");
446 return;
447 }
448
449 memset (ed, 0, sizeof(*ed));
450 ed->classname = "freed";
451 ed->freetime = level.time;
452 ed->inuse = false;
453 }
454
455
456 /*
457 ============
458 G_TouchTriggers
459
460 ============
461 */
G_TouchTriggers(edict_t * ent)462 void G_TouchTriggers (edict_t *ent)
463 {
464 int i, num;
465 edict_t *touch[MAX_EDICTS], *hit;
466
467 // dead things don't activate triggers!
468 if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0))
469 return;
470
471 num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
472 , MAX_EDICTS, AREA_TRIGGERS);
473
474 // be careful, it is possible to have an entity in this
475 // list removed before we get to it (killtriggered)
476 for (i=0 ; i<num ; i++)
477 {
478 hit = touch[i];
479 if (!hit->inuse)
480 continue;
481 if (!hit->touch)
482 continue;
483 hit->touch (hit, ent, NULL, NULL);
484 }
485 }
486
487 /*
488 ============
489 G_TouchSolids
490
491 Call after linking a new trigger in during gameplay
492 to force all entities it covers to immediately touch it
493 ============
494 */
G_TouchSolids(edict_t * ent)495 void G_TouchSolids (edict_t *ent)
496 {
497 int i, num;
498 edict_t *touch[MAX_EDICTS], *hit;
499
500 num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
501 , MAX_EDICTS, AREA_SOLID);
502
503 // be careful, it is possible to have an entity in this
504 // list removed before we get to it (killtriggered)
505 for (i=0 ; i<num ; i++)
506 {
507 hit = touch[i];
508 if (!hit->inuse)
509 continue;
510 if (ent->touch)
511 ent->touch (hit, ent, NULL, NULL);
512 if (!ent->inuse)
513 break;
514 }
515 }
516
517
518
519
520 /*
521 ==============================================================================
522
523 Kill box
524
525 ==============================================================================
526 */
527
528 /*
529 =================
530 KillBox
531
532 Kills all entities that would touch the proposed new positioning
533 of ent. Ent should be unlinked before calling this!
534 =================
535 */
KillBox(edict_t * ent)536 qboolean KillBox (edict_t *ent)
537 {
538 trace_t tr;
539
540 while (1)
541 {
542 tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID);
543 if (!tr.ent)
544 break;
545
546 // nail it
547 T_Damage (tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
548
549 // if we didn't kill it, fail
550 if (tr.ent->solid)
551 return false;
552 }
553
554 return true; // all clear
555 }
556