1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 */
20 // g_utils.c -- misc utility functions for game module
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include "g_local.h"
27 
28 
G_ProjectSource(vec3_t point,vec3_t distance,vec3_t forward,vec3_t right,vec3_t result)29 void G_ProjectSource (vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result)
30 {
31 	result[0] = point[0] + forward[0] * distance[0] + right[0] * distance[1];
32 	result[1] = point[1] + forward[1] * distance[0] + right[1] * distance[1];
33 	result[2] = point[2] + forward[2] * distance[0] + right[2] * distance[1] + distance[2];
34 }
35 
36 
37 /*
38 =============
39 G_Find
40 
41 Searches all active entities for the next one that holds
42 the matching string at fieldofs (use the FOFS() macro) in the structure.
43 
44 Searches beginning at the edict after from, or the beginning if NULL
45 NULL will be returned if the end of the list is reached.
46 
47 =============
48 */
G_Find(edict_t * from,int fieldofs,char * match)49 edict_t *G_Find (edict_t *from, int fieldofs, char *match)
50 {
51 	char	*s;
52 
53 	if (!from)
54 		from = g_edicts;
55 	else
56 		from++;
57 
58 	for ( ; from < &g_edicts[globals.num_edicts] ; from++)
59 	{
60 		if (!from->inuse)
61 			continue;
62 		s = *(char **) ((byte *)from + fieldofs);
63 		if (!s)
64 			continue;
65 		if (!Q_strcasecmp (s, match))
66 			return from;
67 	}
68 
69 	return NULL;
70 }
71 
72 
73 /*
74 =================
75 findradius
76 
77 Returns entities that have origins within a spherical area
78 
79 findradius (origin, radius)
80 =================
81 */
findradius(edict_t * from,vec3_t org,float rad)82 edict_t *findradius (edict_t *from, vec3_t org, float rad)
83 {
84 	vec3_t	eorg;
85 	int		j;
86 
87 	if (!from)
88 		from = g_edicts;
89 	else
90 		from++;
91 	for ( ; from < &g_edicts[globals.num_edicts]; from++)
92 	{
93 		if (!from->inuse)
94 			continue;
95 		if (from->solid == SOLID_NOT)
96 			continue;
97 		for (j=0 ; j<3 ; j++)
98 			eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5);
99 		if (VectorLength(eorg) > rad)
100 			continue;
101 		return from;
102 	}
103 
104 	return NULL;
105 }
106 
107 
108 /*
109 =============
110 G_PickTarget
111 
112 Searches all active entities for the next one that holds
113 the matching string at fieldofs (use the FOFS() macro) in the structure.
114 
115 Searches beginning at the edict after from, or the beginning if NULL
116 NULL will be returned if the end of the list is reached.
117 
118 =============
119 */
120 #define MAXCHOICES	8
121 
G_PickTarget(char * targetname)122 edict_t *G_PickTarget (char *targetname)
123 {
124 	edict_t	*ent = NULL;
125 	int		num_choices = 0;
126 	edict_t	*choice[MAXCHOICES];
127 
128 	if (!targetname)
129 	{
130 		gi.dprintf("G_PickTarget called with NULL targetname\n");
131 		return NULL;
132 	}
133 
134 	while(1)
135 	{
136 		ent = G_Find (ent, FOFS(targetname), targetname);
137 		if (!ent)
138 			break;
139 		choice[num_choices++] = ent;
140 		if (num_choices == MAXCHOICES)
141 			break;
142 	}
143 
144 	if (!num_choices)
145 	{
146 		gi.dprintf("G_PickTarget: target %s not found\n", targetname);
147 		return NULL;
148 	}
149 
150 	return choice[rand() % num_choices];
151 }
152 
153 
154 
Think_Delay(edict_t * ent)155 void Think_Delay (edict_t *ent)
156 {
157 	G_UseTargets (ent, ent->activator);
158 	G_FreeEdict (ent);
159 }
160 
161 /*
162 ==============================
163 G_UseTargets
164 
165 the global "activator" should be set to the entity that initiated the firing.
166 
167 If self.delay is set, a DelayedUse entity will be created that will actually
168 do the SUB_UseTargets after that many seconds have passed.
169 
170 Centerprints any self.message to the activator.
171 
172 Search for (string)targetname in all entities that
173 match (string)self.target and call their .use function
174 
175 ==============================
176 */
G_UseTargets(edict_t * ent,edict_t * activator)177 void G_UseTargets (edict_t *ent, edict_t *activator)
178 {
179 	edict_t		*t;
180 
181 //
182 // check for a delay
183 //
184 	if (ent->delay)
185 	{
186 	// create a temp object to fire at a later time
187 		t = G_Spawn();
188 		t->classname = "DelayedUse";
189 		t->nextthink = level.time + ent->delay;
190 		t->think = Think_Delay;
191 		t->activator = activator;
192 		if (!activator)
193 			gi.dprintf ("Think_Delay with no activator\n");
194 		t->message = ent->message;
195 		t->target = ent->target;
196 		t->killtarget = ent->killtarget;
197 		return;
198 	}
199 
200 
201 //
202 // print the message
203 //
204 	if ((ent->message) && !(activator->svflags & SVF_MONSTER))
205 	{
206 		safe_centerprintf (activator, "%s", ent->message);
207 		if (ent->noise_index)
208 			gi.sound (activator, CHAN_AUTO, ent->noise_index, 1, ATTN_NORM, 0);
209 		else
210 			gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
211 	}
212 
213 //
214 // kill killtargets
215 //
216 	if (ent->killtarget)
217 	{
218 		t = NULL;
219 		while ((t = G_Find (t, FOFS(targetname), ent->killtarget)))
220 		{
221 			G_FreeEdict (t);
222 			if (!ent->inuse)
223 			{
224 				gi.dprintf("entity was removed while using killtargets\n");
225 				return;
226 			}
227 		}
228 	}
229 
230 //
231 // fire targets
232 //
233 	if (ent->target)
234 	{
235 		t = NULL;
236 		while ((t = G_Find (t, FOFS(targetname), ent->target)))
237 		{
238 			// doors fire area portals in a specific way
239 			if (!Q_strcasecmp(t->classname, "func_areaportal") &&
240 				(!Q_strcasecmp(ent->classname, "func_door") || !Q_strcasecmp(ent->classname, "func_door_rotating")))
241 				continue;
242 
243 			if (t == ent)
244 			{
245 				gi.dprintf ("WARNING: Entity used itself.\n");
246 			}
247 			else
248 			{
249 				if (t->use)
250 					t->use (t, ent, activator);
251 			}
252 			if (!ent->inuse)
253 			{
254 				gi.dprintf("entity was removed while using targets\n");
255 				return;
256 			}
257 		}
258 	}
259 }
260 
261 
262 /*
263 =============
264 TempVector
265 
266 This is just a convenience function
267 for making temporary vectors for function calls
268 =============
269 */
tv(float x,float y,float z)270 float	*tv (float x, float y, float z)
271 {
272 	static	int		index;
273 	static	vec3_t	vecs[8];
274 	float	*v;
275 
276 	// use an array so that multiple tempvectors won't collide
277 	// for a while
278 	v = vecs[index];
279 	index = (index + 1)&7;
280 
281 	v[0] = x;
282 	v[1] = y;
283 	v[2] = z;
284 
285 	return v;
286 }
287 
288 
289 /*
290 =============
291 VectorToString
292 
293 This is just a convenience function
294 for printing vectors
295 =============
296 */
vtos(vec3_t v)297 char	*vtos (vec3_t v)
298 {
299 	static	int		index;
300 	static	char	str[8][32];
301 	char	*s;
302 
303 	// use an array so that multiple vtos won't collide
304 	s = str[index];
305 	index = (index + 1)&7;
306 
307 	Com_sprintf (s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]);
308 
309 	return s;
310 }
311 
312 
313 vec3_t VEC_UP		= {0, -1, 0};
314 vec3_t MOVEDIR_UP	= {0, 0, 1};
315 vec3_t VEC_DOWN		= {0, -2, 0};
316 vec3_t MOVEDIR_DOWN	= {0, 0, -1};
317 
G_SetMovedir(vec3_t angles,vec3_t movedir)318 void G_SetMovedir (vec3_t angles, vec3_t movedir)
319 {
320 	if (VectorCompare (angles, VEC_UP))
321 	{
322 		VectorCopy (MOVEDIR_UP, movedir);
323 	}
324 	else if (VectorCompare (angles, VEC_DOWN))
325 	{
326 		VectorCopy (MOVEDIR_DOWN, movedir);
327 	}
328 	else
329 	{
330 		AngleVectors (angles, movedir, NULL, NULL);
331 	}
332 
333 	VectorClear (angles);
334 }
335 
336 
vectoyaw(vec3_t vec)337 float vectoyaw (vec3_t vec)
338 {
339 	float	yaw;
340 
341 	if (/*vec[YAW] == 0 &&*/ vec[PITCH] == 0)
342 	{
343 		yaw = 0;
344 		if (vec[YAW] > 0)
345 			yaw = 90;
346 		else if (vec[YAW] < 0)
347 			yaw = -90;
348 	}
349 	else
350 	{
351 		yaw = (int) (atan2(vec[YAW], vec[PITCH]) * 180 / M_PI);
352 		if (yaw < 0)
353 			yaw += 360;
354 	}
355 
356 	return yaw;
357 }
358 
359 // void vectoangles (vec3_t value1, vec3_t angles)  Moved to q_shared.c
360 
G_CopyString(const char * in)361 char *G_CopyString (const char *in)
362 {
363 	char	*out;
364 
365 	out = gi.TagMalloc (strlen(in)+1, TAG_LEVEL);
366 	strcpy (out, in);
367 	return out;
368 }
369 
370 
G_InitEdict(edict_t * e)371 void G_InitEdict (edict_t *e)
372 {
373 	e->inuse = true;
374 	e->classname = "noclass";
375 	e->gravity = 1.0;
376 	e->s.number = e - g_edicts;
377 }
378 
379 /*
380 =================
381 G_Spawn
382 
383 Either finds a free edict, or allocates a new one.
384 Try to avoid reusing an entity that was recently freed, because it
385 can cause the client to think the entity morphed into something else
386 instead of being removed and recreated, which can cause interpolated
387 angles and bad trails.
388 =================
389 */
G_Spawn(void)390 edict_t *G_Spawn (void)
391 {
392 	int			i;
393 	edict_t		*e;
394 
395 	e = &g_edicts[g_maxclients->integer+1];
396 	for ( i=g_maxclients->integer+1 ; i<globals.num_edicts ; i++, e++)
397 	{
398 		// the first couple seconds of server time can involve a lot of
399 		// freeing and allocating, so relax the replacement policy
400 		if (!e->inuse && ( e->freetime < 2 || level.time - e->freetime > 0.5 ) )
401 		{
402 			G_InitEdict (e);
403 			return e;
404 		}
405 	}
406 
407 	if (i == game.maxentities)
408 		gi.error ("ED_Alloc: no free edicts");
409 
410 	globals.num_edicts++;
411 	G_InitEdict (e);
412 	return e;
413 }
414 
415 /*
416 =================
417 G_FreeEdict
418 
419 Marks the edict as free
420 =================
421 */
G_FreeEdict(edict_t * ed)422 void G_FreeEdict (edict_t *ed)
423 {
424 	gi.unlinkentity (ed);		// unlink from world
425 
426 	if ((ed - g_edicts) <= (g_maxclients->value + BODY_QUEUE_SIZE))
427 	{
428 //		gi.dprintf("tried to free special edict\n");
429 		return;
430 	}
431 
432 	memset (ed, 0, sizeof(*ed));
433 	ed->classname = "freed";
434 	ed->freetime = level.time;
435 	ed->inuse = false;
436 }
437 
438 
439 /*
440 ============
441 G_TouchTriggers
442 
443 ============
444 */
G_TouchTriggers(edict_t * ent)445 void	G_TouchTriggers (edict_t *ent)
446 {
447 	int			i, num;
448 	edict_t		*touch[MAX_EDICTS], *hit;
449 
450 	// dead things don't activate triggers!
451 	if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0))
452 		return;
453 
454 	num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
455 		, MAX_EDICTS, AREA_TRIGGERS);
456 
457 	// be careful, it is possible to have an entity in this
458 	// list removed before we get to it (killtriggered)
459 	for (i=0 ; i<num ; i++)
460 	{
461 		hit = touch[i];
462 		if (!hit->inuse)
463 			continue;
464 		if (!hit->touch)
465 			continue;
466 		hit->touch (hit, ent, NULL, NULL);
467 	}
468 }
469 
470 /*
471 ============
472 G_TouchSolids
473 
474 Call after linking a new trigger in during gameplay
475 to force all entities it covers to immediately touch it
476 ============
477 */
G_TouchSolids(edict_t * ent)478 void	G_TouchSolids (edict_t *ent)
479 {
480 	int			i, num;
481 	edict_t		*touch[MAX_EDICTS], *hit;
482 
483 	num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
484 		, MAX_EDICTS, AREA_SOLID);
485 
486 	// be careful, it is possible to have an entity in this
487 	// list removed before we get to it (killtriggered)
488 	for (i=0 ; i<num ; i++)
489 	{
490 		hit = touch[i];
491 		if (!hit->inuse)
492 			continue;
493 		if (ent->touch)
494 			ent->touch (hit, ent, NULL, NULL);
495 		if (!ent->inuse)
496 			break;
497 	}
498 }
499 
500 
501 
502 
503 /*
504 ==============================================================================
505 
506 Kill box
507 
508 ==============================================================================
509 */
510 
511 /*
512 =================
513 KillBox
514 
515 Kills all entities that would touch the proposed new positioning
516 of ent.  Ent should be unlinked before calling this!
517 =================
518 */
KillBox(edict_t * ent)519 qboolean KillBox (edict_t *ent)
520 {
521 	trace_t		tr;
522 
523 	while (1)
524 	{
525 		tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID);
526 		if (!tr.ent)
527 			break;
528 
529 		// nail it
530 		T_Damage (tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
531 
532 		// if we didn't kill it, fail
533 		if (tr.ent->solid)
534 			return false;
535 	}
536 
537 	return true;		// all clear
538 }
539 
540 
541 /*
542 =============
543 G_CleanPlayerName
544 
545 Removes escape characters from a player's name and prepares it
546 to be displayed in green through safe_centerprintf.
547 
548 !Note!: dest must be at least PLAYERNAME_SIZE  bytes long.
549   (could be PLAYERNAME_GLYPHS + 1 long, but safer to use max. string length)
550 =============
551 */
G_CleanPlayerName(const char * source,char * dest)552 void G_CleanPlayerName ( const char *source, char *dest )
553 {
554 	const char *pch_src = source;
555 	char *pch_dst = dest;
556 	int count = strlen( pch_src );
557 
558 	memset( pch_dst, 0, PLAYERNAME_SIZE );
559 	while ( count > 0 )
560 	{
561 		if ( Q_IsColorString( pch_src ) )
562 		{
563 			pch_src += 2;
564 			count -= 2;
565 		}
566 		else
567 		{
568 			*pch_dst++ = *pch_src | 0x80;
569 			++pch_src;
570 			--count;
571 		}
572 	}
573 }
574 
575