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