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
G_ProjectSource2(vec3_t point,vec3_t distance,vec3_t forward,vec3_t right,vec3_t up,vec3_t result)13 void G_ProjectSource2 (vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t up, vec3_t result)
14 {
15 result[0] = point[0] + forward[0] * distance[0] + right[0] * distance[1] + up[0] * distance[2];
16 result[1] = point[1] + forward[1] * distance[0] + right[1] * distance[1] + up[1] * distance[2];
17 result[2] = point[2] + forward[2] * distance[0] + right[2] * distance[1] + up[2] * distance[2];
18 }
19
20 /*
21 =============
22 G_Find
23
24 Searches all active entities for the next one that holds
25 the matching string at fieldofs (use the FOFS() macro) in the structure.
26
27 Searches beginning at the edict after from, or the beginning if NULL
28 NULL will be returned if the end of the list is reached.
29
30 =============
31 */
G_Find(edict_t * from,int fieldofs,char * match)32 edict_t *G_Find (edict_t *from, int fieldofs, char *match)
33 {
34 char *s;
35
36 if (!from)
37 from = g_edicts;
38 else
39 from++;
40
41 for ( ; from < &g_edicts[globals.num_edicts] ; from++)
42 {
43 if (!from->inuse)
44 continue;
45 s = *(char **) ((byte *)from + fieldofs);
46 if (!s)
47 continue;
48 if (!Q_stricmp (s, match))
49 return from;
50 }
51
52 return NULL;
53 }
54
55
56 /*
57 =================
58 findradius
59
60 Returns entities that have origins within a spherical area
61
62 findradius (origin, radius)
63 =================
64 */
findradius(edict_t * from,vec3_t org,float rad)65 edict_t *findradius (edict_t *from, vec3_t org, float rad)
66 {
67 vec3_t eorg;
68 int j;
69
70 if (!from)
71 from = g_edicts;
72 else
73 from++;
74 for ( ; from < &g_edicts[globals.num_edicts]; from++)
75 {
76 if (!from->inuse)
77 continue;
78 if (from->solid == SOLID_NOT)
79 continue;
80 for (j=0 ; j<3 ; j++)
81 eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5);
82 if (VectorLength(eorg) > rad)
83 continue;
84 return from;
85 }
86
87 return NULL;
88 }
89
90 /*
91 =================
92 findradius2
93
94 Returns entities that have origins within a spherical area
95
96 ROGUE - tweaks for performance for tesla specific code
97 only returns entities that can be damaged
98 only returns entities that are SVF_DAMAGEABLE
99
100 findradius2 (origin, radius)
101 =================
102 */
findradius2(edict_t * from,vec3_t org,float rad)103 edict_t *findradius2 (edict_t *from, vec3_t org, float rad)
104 {
105 // rad must be positive
106 vec3_t eorg;
107 int j;
108
109 if (!from)
110 from = g_edicts;
111 else
112 from++;
113 for ( ; from < &g_edicts[globals.num_edicts]; from++)
114 {
115 if (!from->inuse)
116 continue;
117 if (from->solid == SOLID_NOT)
118 continue;
119 if (!from->takedamage)
120 continue;
121 if (!(from->svflags & SVF_DAMAGEABLE))
122 continue;
123 for (j=0 ; j<3 ; j++)
124 eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5);
125 if (VectorLength(eorg) > rad)
126 continue;
127 return from;
128 }
129
130 return NULL;
131 }
132
133
134 /*
135 =============
136 G_PickTarget
137
138 Searches all active entities for the next one that holds
139 the matching string at fieldofs (use the FOFS() macro) in the structure.
140
141 Searches beginning at the edict after from, or the beginning if NULL
142 NULL will be returned if the end of the list is reached.
143
144 =============
145 */
146 #define MAXCHOICES 8
147
G_PickTarget(char * targetname)148 edict_t *G_PickTarget (char *targetname)
149 {
150 edict_t *ent = NULL;
151 int num_choices = 0;
152 edict_t *choice[MAXCHOICES];
153
154 if (!targetname)
155 {
156 gi.dprintf("G_PickTarget called with NULL targetname\n");
157 return NULL;
158 }
159
160 while(1)
161 {
162 ent = G_Find (ent, FOFS(targetname), targetname);
163 if (!ent)
164 break;
165 choice[num_choices++] = ent;
166 if (num_choices == MAXCHOICES)
167 break;
168 }
169
170 if (!num_choices)
171 {
172 gi.dprintf("G_PickTarget: target %s not found\n", targetname);
173 return NULL;
174 }
175
176 return choice[rand() % num_choices];
177 }
178
179
180
Think_Delay(edict_t * ent)181 void Think_Delay (edict_t *ent)
182 {
183 G_UseTargets (ent, ent->activator);
184 G_FreeEdict (ent);
185 }
186
187 /*
188 ==============================
189 G_UseTargets
190
191 the global "activator" should be set to the entity that initiated the firing.
192
193 If self.delay is set, a DelayedUse entity will be created that will actually
194 do the SUB_UseTargets after that many seconds have passed.
195
196 Centerprints any self.message to the activator.
197
198 Search for (string)targetname in all entities that
199 match (string)self.target and call their .use function
200
201 ==============================
202 */
G_UseTargets(edict_t * ent,edict_t * activator)203 void G_UseTargets (edict_t *ent, edict_t *activator)
204 {
205 edict_t *t;
206 edict_t *master;
207 qboolean done = false;
208
209 //
210 // check for a delay
211 //
212 if (ent->delay)
213 {
214 // create a temp object to fire at a later time
215 t = G_Spawn();
216 t->classname = "DelayedUse";
217 t->nextthink = level.time + ent->delay;
218 t->think = Think_Delay;
219 t->activator = activator;
220 if (!activator)
221 gi.dprintf ("Think_Delay with no activator\n");
222 t->message = ent->message;
223 t->target = ent->target;
224 t->killtarget = ent->killtarget;
225 return;
226 }
227
228
229 //
230 // print the message
231 //
232 if ((ent->message) && !(activator->svflags & SVF_MONSTER))
233 {
234 gi.centerprintf (activator, "%s", ent->message);
235 if (ent->noise_index)
236 gi.sound (activator, CHAN_AUTO, ent->noise_index, 1, ATTN_NORM, 0);
237 else
238 gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
239 }
240
241 //
242 // kill killtargets
243 //
244 if (ent->killtarget)
245 {
246 t = NULL;
247 while ((t = G_Find (t, FOFS(targetname), ent->killtarget)))
248 {
249 // PMM - if this entity is part of a train, cleanly remove it
250 if (t->flags & FL_TEAMSLAVE)
251 {
252 // if ((g_showlogic) && (g_showlogic->value))
253 // gi.dprintf ("Removing %s from train!\n", t->classname);
254
255 if (t->teammaster)
256 {
257 master = t->teammaster;
258 while (!done)
259 {
260 if (master->teamchain == t)
261 {
262 master->teamchain = t->teamchain;
263 done = true;
264 }
265 master = master->teamchain;
266 if (!master)
267 {
268 // if ((g_showlogic) && (g_showlogic->value))
269 // gi.dprintf ("Couldn't find myself in master's chain, ignoring!\n");
270 }
271 }
272 }
273 else
274 {
275 // if ((g_showlogic) && (g_showlogic->value))
276 // gi.dprintf ("No master to free myself from, ignoring!\n");
277 }
278 }
279 // PMM
280 G_FreeEdict (t);
281 if (!ent->inuse)
282 {
283 gi.dprintf("entity was removed while using killtargets\n");
284 return;
285 }
286 }
287 }
288
289 //
290 // fire targets
291 //
292 if (ent->target)
293 {
294 t = NULL;
295 while ((t = G_Find (t, FOFS(targetname), ent->target)))
296 {
297 // doors fire area portals in a specific way
298 if (!Q_stricmp(t->classname, "func_areaportal") &&
299 (!Q_stricmp(ent->classname, "func_door") || !Q_stricmp(ent->classname, "func_door_rotating")))
300 continue;
301
302 if (t == ent)
303 {
304 gi.dprintf ("WARNING: Entity used itself.\n");
305 }
306 else
307 {
308 if (t->use)
309 t->use (t, ent, activator);
310 }
311 if (!ent->inuse)
312 {
313 gi.dprintf("entity was removed while using targets\n");
314 return;
315 }
316 }
317 }
318 }
319
320
321 /*
322 =============
323 TempVector
324
325 This is just a convenience function
326 for making temporary vectors for function calls
327 =============
328 */
tv(float x,float y,float z)329 float *tv (float x, float y, float z)
330 {
331 static int index;
332 static vec3_t vecs[8];
333 float *v;
334
335 // use an array so that multiple tempvectors won't collide
336 // for a while
337 v = vecs[index];
338 index = (index + 1)&7;
339
340 v[0] = x;
341 v[1] = y;
342 v[2] = z;
343
344 return v;
345 }
346
347
348 /*
349 =============
350 VectorToString
351
352 This is just a convenience function
353 for printing vectors
354 =============
355 */
vtos(vec3_t v)356 char *vtos (vec3_t v)
357 {
358 static int index;
359 static char str[8][32];
360 char *s;
361
362 // use an array so that multiple vtos won't collide
363 s = str[index];
364 index = (index + 1)&7;
365
366 Com_sprintf (s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]);
367
368 return s;
369 }
370
371
372 vec3_t VEC_UP = {0, -1, 0};
373 vec3_t MOVEDIR_UP = {0, 0, 1};
374 vec3_t VEC_DOWN = {0, -2, 0};
375 vec3_t MOVEDIR_DOWN = {0, 0, -1};
376
G_SetMovedir(vec3_t angles,vec3_t movedir)377 void G_SetMovedir (vec3_t angles, vec3_t movedir)
378 {
379 if (VectorCompare (angles, VEC_UP))
380 {
381 VectorCopy (MOVEDIR_UP, movedir);
382 }
383 else if (VectorCompare (angles, VEC_DOWN))
384 {
385 VectorCopy (MOVEDIR_DOWN, movedir);
386 }
387 else
388 {
389 AngleVectors (angles, movedir, NULL, NULL);
390 }
391
392 VectorClear (angles);
393 }
394
395
vectoyaw(vec3_t vec)396 float vectoyaw (vec3_t vec)
397 {
398 float yaw;
399
400 // PMM - fixed to correct for pitch of 0
401 if (/*vec[YAW] == 0 &&*/ vec[PITCH] == 0)
402 if (vec[YAW] == 0)
403 yaw = 0;
404 else if (vec[YAW] > 0)
405 yaw = 90;
406 else
407 yaw = 270;
408 else
409 {
410 yaw = (int) (atan2(vec[YAW], vec[PITCH]) * 180 / M_PI);
411 if (yaw < 0)
412 yaw += 360;
413 }
414
415 return yaw;
416 }
417
vectoyaw2(vec3_t vec)418 float vectoyaw2 (vec3_t vec)
419 {
420 float yaw;
421
422 // PMM - fixed to correct for pitch of 0
423 if (/*vec[YAW] == 0 &&*/ vec[PITCH] == 0)
424 if (vec[YAW] == 0)
425 yaw = 0;
426 else if (vec[YAW] > 0)
427 yaw = 90;
428 else
429 yaw = 270;
430 else
431 {
432 yaw = (atan2(vec[YAW], vec[PITCH]) * 180 / M_PI);
433 if (yaw < 0)
434 yaw += 360;
435 }
436
437 return yaw;
438 }
439
440
vectoangles(vec3_t value1,vec3_t angles)441 void vectoangles (vec3_t value1, vec3_t angles)
442 {
443 float forward;
444 float yaw, pitch;
445
446 if (value1[1] == 0 && value1[0] == 0)
447 {
448 yaw = 0;
449 if (value1[2] > 0)
450 pitch = 90;
451 else
452 pitch = 270;
453 }
454 else
455 {
456 // PMM - fixed to correct for pitch of 0
457 if (value1[0])
458 yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
459 else if (value1[1] > 0)
460 yaw = 90;
461 else
462 yaw = 270;
463 if (yaw < 0)
464 yaw += 360;
465
466 forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
467 pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
468 if (pitch < 0)
469 pitch += 360;
470 }
471
472 angles[PITCH] = -pitch;
473 angles[YAW] = yaw;
474 angles[ROLL] = 0;
475 }
476
vectoangles2(vec3_t value1,vec3_t angles)477 void vectoangles2 (vec3_t value1, vec3_t angles)
478 {
479 float forward;
480 float yaw, pitch;
481
482 if (value1[1] == 0 && value1[0] == 0)
483 {
484 yaw = 0;
485 if (value1[2] > 0)
486 pitch = 90;
487 else
488 pitch = 270;
489 }
490 else
491 {
492 // PMM - fixed to correct for pitch of 0
493 if (value1[0])
494 yaw = (atan2(value1[1], value1[0]) * 180 / M_PI);
495 else if (value1[1] > 0)
496 yaw = 90;
497 else
498 yaw = 270;
499
500 if (yaw < 0)
501 yaw += 360;
502
503 forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
504 pitch = (atan2(value1[2], forward) * 180 / M_PI);
505 if (pitch < 0)
506 pitch += 360;
507 }
508
509 angles[PITCH] = -pitch;
510 angles[YAW] = yaw;
511 angles[ROLL] = 0;
512 }
513
G_CopyString(char * in)514 char *G_CopyString (char *in)
515 {
516 char *out;
517
518 out = gi.TagMalloc (strlen(in)+1, TAG_LEVEL);
519 strcpy (out, in);
520 return out;
521 }
522
523
G_InitEdict(edict_t * e)524 void G_InitEdict (edict_t *e)
525 {
526 // ROGUE
527 // FIXME -
528 // this fixes a bug somewhere that is settling "nextthink" for an entity that has
529 // already been released. nextthink is being set to FRAMETIME after level.time,
530 // since freetime = nextthink - 0.1
531 if (e->nextthink)
532 {
533 // if ((g_showlogic) && (g_showlogic->value))
534 // gi.dprintf ("G_SPAWN: Fixed bad nextthink time\n");
535 e->nextthink = 0;
536 }
537 // ROGUE
538
539 e->inuse = true;
540 e->classname = "noclass";
541 e->gravity = 1.0;
542 e->s.number = e - g_edicts;
543
544 //PGM - do this before calling the spawn function so it can be overridden.
545 #ifdef ROGUE_GRAVITY
546 e->gravityVector[0] = 0.0;
547 e->gravityVector[1] = 0.0;
548 e->gravityVector[2] = -1.0;
549 #endif
550 //PGM
551 }
552
553 /*
554 =================
555 G_Spawn
556
557 Either finds a free edict, or allocates a new one.
558 Try to avoid reusing an entity that was recently freed, because it
559 can cause the client to think the entity morphed into something else
560 instead of being removed and recreated, which can cause interpolated
561 angles and bad trails.
562 =================
563 */
G_Spawn(void)564 edict_t *G_Spawn (void)
565 {
566 int i;
567 edict_t *e;
568
569 e = &g_edicts[(int)maxclients->value+1];
570 for ( i=maxclients->value+1 ; i<globals.num_edicts ; i++, e++)
571 {
572 // the first couple seconds of server time can involve a lot of
573 // freeing and allocating, so relax the replacement policy
574 if (!e->inuse && ( e->freetime < 2 || level.time - e->freetime > 0.5 ) )
575 {
576 G_InitEdict (e);
577 return e;
578 }
579 }
580
581 if (i == game.maxentities)
582 gi.error ("ED_Alloc: no free edicts");
583
584 globals.num_edicts++;
585 G_InitEdict (e);
586 return e;
587 }
588
589 /*
590 =================
591 G_FreeEdict
592
593 Marks the edict as free
594 =================
595 */
G_FreeEdict(edict_t * ed)596 void G_FreeEdict (edict_t *ed)
597 {
598 gi.unlinkentity (ed); // unlink from world
599
600 if ((ed - g_edicts) <= (maxclients->value + BODY_QUEUE_SIZE))
601 {
602 // gi.dprintf("tried to free special edict\n");
603 return;
604 }
605
606 memset (ed, 0, sizeof(*ed));
607 ed->classname = "freed";
608 ed->freetime = level.time;
609 ed->inuse = false;
610 }
611
612
613 /*
614 ============
615 G_TouchTriggers
616
617 ============
618 */
G_TouchTriggers(edict_t * ent)619 void G_TouchTriggers (edict_t *ent)
620 {
621 int i, num;
622 edict_t *touch[MAX_EDICTS], *hit;
623
624 // dead things don't activate triggers!
625 if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0))
626 return;
627
628 num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
629 , MAX_EDICTS, AREA_TRIGGERS);
630
631 // be careful, it is possible to have an entity in this
632 // list removed before we get to it (killtriggered)
633 for (i=0 ; i<num ; i++)
634 {
635 hit = touch[i];
636 if (!hit->inuse)
637 continue;
638 if (!hit->touch)
639 continue;
640 hit->touch (hit, ent, NULL, NULL);
641 }
642 }
643
644 /*
645 ============
646 G_TouchSolids
647
648 Call after linking a new trigger in during gameplay
649 to force all entities it covers to immediately touch it
650 ============
651 */
G_TouchSolids(edict_t * ent)652 void G_TouchSolids (edict_t *ent)
653 {
654 int i, num;
655 edict_t *touch[MAX_EDICTS], *hit;
656
657 num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
658 , MAX_EDICTS, AREA_SOLID);
659
660 // be careful, it is possible to have an entity in this
661 // list removed before we get to it (killtriggered)
662 for (i=0 ; i<num ; i++)
663 {
664 hit = touch[i];
665 if (!hit->inuse)
666 continue;
667 if (ent->touch)
668 ent->touch (hit, ent, NULL, NULL);
669 if (!ent->inuse)
670 break;
671 }
672 }
673
674
675
676
677 /*
678 ==============================================================================
679
680 Kill box
681
682 ==============================================================================
683 */
684
685 /*
686 =================
687 KillBox
688
689 Kills all entities that would touch the proposed new positioning
690 of ent. Ent should be unlinked before calling this!
691 =================
692 */
KillBox(edict_t * ent)693 qboolean KillBox (edict_t *ent)
694 {
695 trace_t tr;
696
697 while (1)
698 {
699 tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID);
700 if (!tr.ent)
701 break;
702
703 // nail it
704 T_Damage (tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
705
706 // if we didn't kill it, fail
707 if (tr.ent->solid)
708 return false;
709 }
710
711 return true; // all clear
712 }
713