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