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