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