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 #ifdef WITH_ACEBOT
235 		safe_centerprintf
236 #else
237 		gi.centerprintf
238 #endif
239 		 (activator, "%s", ent->message);
240 		if (ent->noise_index)
241 			gi.sound (activator, CHAN_AUTO, ent->noise_index, 1, ATTN_NORM, 0);
242 		else
243 			gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
244 	}
245 
246 //
247 // kill killtargets
248 //
249 	if (ent->killtarget)
250 	{
251 		t = NULL;
252 		while ((t = G_Find (t, FOFS(targetname), ent->killtarget)))
253 		{
254 			// PMM - if this entity is part of a train, cleanly remove it
255 			if (t->flags & FL_TEAMSLAVE)
256 			{
257 //				if ((g_showlogic) && (g_showlogic->value))
258 //					gi.dprintf ("Removing %s from train!\n", t->classname);
259 
260 				if (t->teammaster)
261 				{
262 					master = t->teammaster;
263 					while (!done)
264 					{
265 						if (master->teamchain == t)
266 						{
267 							master->teamchain = t->teamchain;
268 							done = true;
269 						}
270 						master = master->teamchain;
271 						if (!master)
272 						{
273 //							if ((g_showlogic) && (g_showlogic->value))
274 //								gi.dprintf ("Couldn't find myself in master's chain, ignoring!\n");
275 						}
276 					}
277 				}
278 				else
279 				{
280 //					if ((g_showlogic) && (g_showlogic->value))
281 //						gi.dprintf ("No master to free myself from, ignoring!\n");
282 				}
283 			}
284 			// PMM
285 			G_FreeEdict (t);
286 			if (!ent->inuse)
287 			{
288 				gi.dprintf("entity was removed while using killtargets\n");
289 				return;
290 			}
291 		}
292 	}
293 
294 //
295 // fire targets
296 //
297 	if (ent->target)
298 	{
299 		t = NULL;
300 		while ((t = G_Find (t, FOFS(targetname), ent->target)))
301 		{
302 			// doors fire area portals in a specific way
303 			if (!Q_stricmp(t->classname, "func_areaportal") &&
304 				(!Q_stricmp(ent->classname, "func_door") || !Q_stricmp(ent->classname, "func_door_rotating")))
305 				continue;
306 
307 			if (t == ent)
308 			{
309 				gi.dprintf ("WARNING: Entity used itself.\n");
310 			}
311 			else
312 			{
313 				if (t->use)
314 					t->use (t, ent, activator);
315 			}
316 			if (!ent->inuse)
317 			{
318 				gi.dprintf("entity was removed while using targets\n");
319 				return;
320 			}
321 		}
322 	}
323 }
324 
325 
326 /*
327 =============
328 TempVector
329 
330 This is just a convenience function
331 for making temporary vectors for function calls
332 =============
333 */
tv(float x,float y,float z)334 float	*tv (float x, float y, float z)
335 {
336 	static	int		index;
337 	static	vec3_t	vecs[8];
338 	float	*v;
339 
340 	// use an array so that multiple tempvectors won't collide
341 	// for a while
342 	v = vecs[index];
343 	index = (index + 1)&7;
344 
345 	v[0] = x;
346 	v[1] = y;
347 	v[2] = z;
348 
349 	return v;
350 }
351 
352 
353 /*
354 =============
355 VectorToString
356 
357 This is just a convenience function
358 for printing vectors
359 =============
360 */
vtos(vec3_t v)361 char	*vtos (vec3_t v)
362 {
363 	static	int		index;
364 	static	char	str[8][32];
365 	char	*s;
366 
367 	// use an array so that multiple vtos won't collide
368 	s = str[index];
369 	index = (index + 1)&7;
370 
371 	Com_sprintf (s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]);
372 
373 	return s;
374 }
375 
376 
377 vec3_t VEC_UP		= {0, -1, 0};
378 vec3_t MOVEDIR_UP	= {0, 0, 1};
379 vec3_t VEC_DOWN		= {0, -2, 0};
380 vec3_t MOVEDIR_DOWN	= {0, 0, -1};
381 
G_SetMovedir(vec3_t angles,vec3_t movedir)382 void G_SetMovedir (vec3_t angles, vec3_t movedir)
383 {
384 	if (VectorCompare (angles, VEC_UP))
385 	{
386 		VectorCopy (MOVEDIR_UP, movedir);
387 	}
388 	else if (VectorCompare (angles, VEC_DOWN))
389 	{
390 		VectorCopy (MOVEDIR_DOWN, movedir);
391 	}
392 	else
393 	{
394 		AngleVectors (angles, movedir, NULL, NULL);
395 	}
396 
397 	VectorClear (angles);
398 }
399 
400 
vectoyaw(vec3_t vec)401 float vectoyaw (vec3_t vec)
402 {
403 	float	yaw;
404 
405 	// PMM - fixed to correct for pitch of 0
406 	if (/*vec[YAW] == 0 &&*/ vec[PITCH] == 0)
407 		if (vec[YAW] == 0)
408 			yaw = 0;
409 		else if (vec[YAW] > 0)
410 			yaw = 90;
411 		else
412 			yaw = 270;
413 	else
414 	{
415 		yaw = (int) (atan2(vec[YAW], vec[PITCH]) * 180 / M_PI);
416 		if (yaw < 0)
417 			yaw += 360;
418 	}
419 
420 	return yaw;
421 }
422 
vectoyaw2(vec3_t vec)423 float vectoyaw2 (vec3_t vec)
424 {
425 	float	yaw;
426 
427 	// PMM - fixed to correct for pitch of 0
428 	if (/*vec[YAW] == 0 &&*/ vec[PITCH] == 0)
429 		if (vec[YAW] == 0)
430 			yaw = 0;
431 		else if (vec[YAW] > 0)
432 			yaw = 90;
433 		else
434 			yaw = 270;
435 	else
436 	{
437 		yaw = (atan2(vec[YAW], vec[PITCH]) * 180 / M_PI);
438 		if (yaw < 0)
439 			yaw += 360;
440 	}
441 
442 	return yaw;
443 }
444 
445 
vectoangles(vec3_t value1,vec3_t angles)446 void vectoangles (vec3_t value1, vec3_t angles)
447 {
448 	float	forward;
449 	float	yaw, pitch;
450 
451 	if (value1[1] == 0 && value1[0] == 0)
452 	{
453 		yaw = 0;
454 		if (value1[2] > 0)
455 			pitch = 90;
456 		else
457 			pitch = 270;
458 	}
459 	else
460 	{
461 	// PMM - fixed to correct for pitch of 0
462 		if (value1[0])
463 			yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
464 		else if (value1[1] > 0)
465 			yaw = 90;
466 		else
467 			yaw = 270;
468 		if (yaw < 0)
469 			yaw += 360;
470 
471 		forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
472 		pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
473 		if (pitch < 0)
474 			pitch += 360;
475 	}
476 
477 	angles[PITCH] = -pitch;
478 	angles[YAW] = yaw;
479 	angles[ROLL] = 0;
480 }
481 
vectoangles2(vec3_t value1,vec3_t angles)482 void vectoangles2 (vec3_t value1, vec3_t angles)
483 {
484 	float	forward;
485 	float	yaw, pitch;
486 
487 	if (value1[1] == 0 && value1[0] == 0)
488 	{
489 		yaw = 0;
490 		if (value1[2] > 0)
491 			pitch = 90;
492 		else
493 			pitch = 270;
494 	}
495 	else
496 	{
497 	// PMM - fixed to correct for pitch of 0
498 		if (value1[0])
499 			yaw = (atan2(value1[1], value1[0]) * 180 / M_PI);
500 		else if (value1[1] > 0)
501 			yaw = 90;
502 		else
503 			yaw = 270;
504 
505 		if (yaw < 0)
506 			yaw += 360;
507 
508 		forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
509 		pitch = (atan2(value1[2], forward) * 180 / M_PI);
510 		if (pitch < 0)
511 			pitch += 360;
512 	}
513 
514 	angles[PITCH] = -pitch;
515 	angles[YAW] = yaw;
516 	angles[ROLL] = 0;
517 }
518 
G_CopyString(char * in)519 char *G_CopyString (char *in)
520 {
521 	char	*out;
522 
523 	out = gi.TagMalloc (strlen(in)+1, TAG_LEVEL);
524 	strcpy (out, in);
525 	return out;
526 }
527 
528 
G_InitEdict(edict_t * e)529 void G_InitEdict (edict_t *e)
530 {
531 	// ROGUE
532 	// FIXME -
533 	//   this fixes a bug somewhere that is settling "nextthink" for an entity that has
534 	//   already been released.  nextthink is being set to FRAMETIME after level.time,
535 	//   since freetime = nextthink - 0.1
536 	if (e->nextthink)
537 	{
538 //		if ((g_showlogic) && (g_showlogic->value))
539 //			gi.dprintf ("G_SPAWN:  Fixed bad nextthink time\n");
540 		e->nextthink = 0;
541 	}
542 	// ROGUE
543 
544 	e->inuse = true;
545 	e->classname = "noclass";
546 	e->gravity = 1.0;
547 	e->s.number = e - g_edicts;
548 
549 //PGM - do this before calling the spawn function so it can be overridden.
550 #ifdef ROGUE_GRAVITY
551 	e->gravityVector[0] =  0.0;
552 	e->gravityVector[1] =  0.0;
553 	e->gravityVector[2] = -1.0;
554 #endif
555 //PGM
556 }
557 
558 /*
559 =================
560 G_Spawn
561 
562 Either finds a free edict, or allocates a new one.
563 Try to avoid reusing an entity that was recently freed, because it
564 can cause the client to think the entity morphed into something else
565 instead of being removed and recreated, which can cause interpolated
566 angles and bad trails.
567 =================
568 */
G_Spawn(void)569 edict_t *G_Spawn (void)
570 {
571 	int			i;
572 	edict_t		*e;
573 
574 	e = &g_edicts[(int)maxclients->value+1];
575 	for ( i=maxclients->value+1 ; i<globals.num_edicts ; i++, e++)
576 	{
577 		// the first couple seconds of server time can involve a lot of
578 		// freeing and allocating, so relax the replacement policy
579 		if (!e->inuse && ( e->freetime < 2 || level.time - e->freetime > 0.5 ) )
580 		{
581 			G_InitEdict (e);
582 			return e;
583 		}
584 	}
585 
586 	if (i == game.maxentities)
587 		gi.error ("ED_Alloc: no free edicts");
588 
589 	globals.num_edicts++;
590 	G_InitEdict (e);
591 	return e;
592 }
593 
594 /*
595 =================
596 G_FreeEdict
597 
598 Marks the edict as free
599 =================
600 */
G_FreeEdict(edict_t * ed)601 void G_FreeEdict (edict_t *ed)
602 {
603 	gi.unlinkentity (ed);		// unlink from world
604 
605 	if ((ed - g_edicts) <= (maxclients->value + BODY_QUEUE_SIZE))
606 	{
607 //		gi.dprintf("tried to free special edict\n");
608 		return;
609 	}
610 
611 	memset (ed, 0, sizeof(*ed));
612 	ed->classname = "freed";
613 	ed->freetime = level.time;
614 	ed->inuse = false;
615 }
616 
617 
618 /*
619 ============
620 G_TouchTriggers
621 
622 ============
623 */
G_TouchTriggers(edict_t * ent)624 void	G_TouchTriggers (edict_t *ent)
625 {
626 	int			i, num;
627 	edict_t		*touch[MAX_EDICTS], *hit;
628 
629 	// dead things don't activate triggers!
630 	if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0))
631 		return;
632 
633 	num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
634 		, MAX_EDICTS, AREA_TRIGGERS);
635 
636 	// be careful, it is possible to have an entity in this
637 	// list removed before we get to it (killtriggered)
638 	for (i=0 ; i<num ; i++)
639 	{
640 		hit = touch[i];
641 		if (!hit->inuse)
642 			continue;
643 		if (!hit->touch)
644 			continue;
645 		hit->touch (hit, ent, NULL, NULL);
646 	}
647 }
648 
649 /*
650 ============
651 G_TouchSolids
652 
653 Call after linking a new trigger in during gameplay
654 to force all entities it covers to immediately touch it
655 ============
656 */
G_TouchSolids(edict_t * ent)657 void	G_TouchSolids (edict_t *ent)
658 {
659 	int			i, num;
660 	edict_t		*touch[MAX_EDICTS], *hit;
661 
662 	num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
663 		, MAX_EDICTS, AREA_SOLID);
664 
665 	// be careful, it is possible to have an entity in this
666 	// list removed before we get to it (killtriggered)
667 	for (i=0 ; i<num ; i++)
668 	{
669 		hit = touch[i];
670 		if (!hit->inuse)
671 			continue;
672 		if (ent->touch)
673 			ent->touch (hit, ent, NULL, NULL);
674 		if (!ent->inuse)
675 			break;
676 	}
677 }
678 
679 
680 
681 
682 /*
683 ==============================================================================
684 
685 Kill box
686 
687 ==============================================================================
688 */
689 
690 /*
691 =================
692 KillBox
693 
694 Kills all entities that would touch the proposed new positioning
695 of ent.  Ent should be unlinked before calling this!
696 =================
697 */
KillBox(edict_t * ent)698 qboolean KillBox (edict_t *ent)
699 {
700 	trace_t		tr;
701 
702 	while (1)
703 	{
704 		tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID);
705 		if (!tr.ent)
706 			break;
707 
708 		// nail it
709 		T_Damage (tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
710 
711 		// if we didn't kill it, fail
712 		if (tr.ent->solid)
713 			return false;
714 	}
715 
716 	return true;		// all clear
717 }
718 
GameDirRelativePath(char * filename,char * output)719 void GameDirRelativePath(char *filename, char *output)
720 {
721 	cvar_t	*basedir, *gamedir;
722 
723 	basedir = gi.cvar("basedir", "", 0);
724 	gamedir = gi.cvar("gamedir", "", 0);
725 	if(strlen(gamedir->string))
726 		sprintf(output,"%s/%s/%s",basedir->string,gamedir->string,filename);
727 	else
728 		sprintf(output,"%s/%s",basedir->string,filename);
729 }
730