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 //
227 // fire targets
228 //
229 	if (ent->target)
230 	{
231 		t = NULL;
232 		while ((t = G_Find (t, FOFS(targetname), ent->target)))
233 		{
234 			// doors fire area portals in a specific way
235 			if (!Q_stricmp(t->classname, "func_areaportal") &&
236 				(!Q_stricmp(ent->classname, "func_door") || !Q_stricmp(ent->classname, "func_door_rotating")))
237 				continue;
238 
239 			if (t == ent)
240 			{
241 				gi.dprintf ("WARNING: Entity used itself.\n");
242 			}
243 			else
244 			{
245 				if (t->use)
246 					t->use (t, ent, activator);
247 			}
248 			if (!ent->inuse)
249 			{
250 				gi.dprintf("entity was removed while using targets\n");
251 				return;
252 			}
253 		}
254 	}
255 }
256 
257 
258 /*
259 =============
260 TempVector
261 
262 This is just a convenience function
263 for making temporary vectors for function calls
264 =============
265 */
tv(float x,float y,float z)266 float	*tv (float x, float y, float z)
267 {
268 	static	int		index;
269 	static	vec3_t	vecs[8];
270 	float	*v;
271 
272 	// use an array so that multiple tempvectors won't collide
273 	// for a while
274 	v = vecs[index];
275 	index = (index + 1)&7;
276 
277 	v[0] = x;
278 	v[1] = y;
279 	v[2] = z;
280 
281 	return v;
282 }
283 
284 
285 /*
286 =============
287 VectorToString
288 
289 This is just a convenience function
290 for printing vectors
291 =============
292 */
vtos(vec3_t v)293 char	*vtos (vec3_t v)
294 {
295 	static	int		index;
296 	static	char	str[8][32];
297 	char	*s;
298 
299 	// use an array so that multiple vtos won't collide
300 	s = str[index];
301 	index = (index + 1)&7;
302 
303 	Com_sprintf (s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]);
304 
305 	return s;
306 }
307 
308 
309 vec3_t VEC_UP		= {0, -1, 0};
310 vec3_t MOVEDIR_UP	= {0, 0, 1};
311 vec3_t VEC_DOWN		= {0, -2, 0};
312 vec3_t MOVEDIR_DOWN	= {0, 0, -1};
313 
G_SetMovedir(vec3_t angles,vec3_t movedir)314 void G_SetMovedir (vec3_t angles, vec3_t movedir)
315 {
316 	if (VectorCompare (angles, VEC_UP))
317 	{
318 		VectorCopy (MOVEDIR_UP, movedir);
319 	}
320 	else if (VectorCompare (angles, VEC_DOWN))
321 	{
322 		VectorCopy (MOVEDIR_DOWN, movedir);
323 	}
324 	else
325 	{
326 		AngleVectors (angles, movedir, NULL, NULL);
327 	}
328 
329 	VectorClear (angles);
330 }
331 
332 
vectoyaw(vec3_t vec)333 float vectoyaw (vec3_t vec)
334 {
335 	float	yaw;
336 
337 	if (/*vec[YAW] == 0 &&*/ vec[PITCH] == 0)
338 	{
339 		yaw = 0;
340 		if (vec[YAW] > 0)
341 			yaw = 90;
342 		else if (vec[YAW] < 0)
343 			yaw = -90;
344 	}
345 	else
346 	{
347 		yaw = (int) (atan2(vec[YAW], vec[PITCH]) * 180 / M_PI);
348 		if (yaw < 0)
349 			yaw += 360;
350 	}
351 
352 	return yaw;
353 }
354 
355 
vectoangles(vec3_t value1,vec3_t angles)356 void vectoangles (vec3_t value1, vec3_t angles)
357 {
358 	float	forward;
359 	float	yaw, pitch;
360 
361 	if (value1[1] == 0 && value1[0] == 0)
362 	{
363 		yaw = 0;
364 		if (value1[2] > 0)
365 			pitch = 90;
366 		else
367 			pitch = 270;
368 	}
369 	else
370 	{
371 		if (value1[0])
372 			yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
373 		else if (value1[1] > 0)
374 			yaw = 90;
375 		else
376 			yaw = -90;
377 		if (yaw < 0)
378 			yaw += 360;
379 
380 		forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
381 		pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
382 		if (pitch < 0)
383 			pitch += 360;
384 	}
385 
386 	angles[PITCH] = -pitch;
387 	angles[YAW] = yaw;
388 	angles[ROLL] = 0;
389 }
390 
G_CopyString(char * in)391 char *G_CopyString (char *in)
392 {
393 	char	*out;
394 
395 	out = gi.TagMalloc (strlen(in)+1, TAG_LEVEL);
396 	strcpy (out, in);
397 	return out;
398 }
399 
400 
G_InitEdict(edict_t * e)401 void G_InitEdict (edict_t *e)
402 {
403 	e->inuse = true;
404 	e->classname = "noclass";
405 	e->gravity = 1.0f;
406 	e->s.number = e - g_edicts;
407 }
408 
409 /*
410 =================
411 G_Spawn
412 
413 Either finds a free edict, or allocates a new one.
414 Try to avoid reusing an entity that was recently freed, because it
415 can cause the client to think the entity morphed into something else
416 instead of being removed and recreated, which can cause interpolated
417 angles and bad trails.
418 =================
419 */
G_Spawn(void)420 edict_t *G_Spawn (void)
421 {
422 	int			i;
423 	edict_t		*e;
424 
425 	e = &g_edicts[(int)maxclients->value+1];
426 	for ( i=maxclients->value+1 ; i<globals.num_edicts ; i++, e++)
427 	{
428 		// the first couple seconds of server time can involve a lot of
429 		// freeing and allocating, so relax the replacement policy
430 		if (!e->inuse && ( e->freetime < 2 || level.time - e->freetime > 0.5 ) )
431 		{
432 			G_InitEdict (e);
433 			return e;
434 		}
435 	}
436 
437 	if (i == game.maxentities)
438 		gi.error ("ED_Alloc: no free edicts");
439 
440 	globals.num_edicts++;
441 	G_InitEdict (e);
442 	return e;
443 }
444 
445 /*
446 =================
447 G_FreeEdict
448 
449 Marks the edict as free
450 =================
451 */
G_FreeEdict(edict_t * ed)452 void G_FreeEdict (edict_t *ed)
453 {
454 	gi.unlinkentity (ed);		// unlink from world
455 
456 	if ((ed - g_edicts) <= (maxclients->value + BODY_QUEUE_SIZE))
457 	{
458 //		gi.dprintf("tried to free special edict\n");
459 		return;
460 	}
461 
462 	memset (ed, 0, sizeof(*ed));
463 	ed->classname = "freed";
464 	ed->freetime = level.time;
465 	ed->inuse = false;
466 }
467 
468 
469 /*
470 ============
471 G_TouchTriggers
472 
473 ============
474 */
G_TouchTriggers(edict_t * ent)475 void	G_TouchTriggers (edict_t *ent)
476 {
477 	int			i, num;
478 	edict_t		*touch[MAX_EDICTS], *hit;
479 
480 	// dead things don't activate triggers!
481 	if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0))
482 		return;
483 
484 	num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
485 		, MAX_EDICTS, AREA_TRIGGERS);
486 
487 	// be careful, it is possible to have an entity in this
488 	// list removed before we get to it (killtriggered)
489 	for (i=0 ; i<num ; i++)
490 	{
491 		hit = touch[i];
492 		if (!hit->inuse)
493 			continue;
494 		if (!hit->touch)
495 			continue;
496 		hit->touch (hit, ent, NULL, NULL);
497 	}
498 }
499 
500 /*
501 ============
502 G_TouchSolids
503 
504 Call after linking a new trigger in during gameplay
505 to force all entities it covers to immediately touch it
506 ============
507 */
G_TouchSolids(edict_t * ent)508 void	G_TouchSolids (edict_t *ent)
509 {
510 	int			i, num;
511 	edict_t		*touch[MAX_EDICTS], *hit;
512 
513 	num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
514 		, MAX_EDICTS, AREA_SOLID);
515 
516 	// be careful, it is possible to have an entity in this
517 	// list removed before we get to it (killtriggered)
518 	for (i=0 ; i<num ; i++)
519 	{
520 		hit = touch[i];
521 		if (!hit->inuse)
522 			continue;
523 		if (ent->touch)
524 			ent->touch (hit, ent, NULL, NULL);
525 		if (!ent->inuse)
526 			break;
527 	}
528 }
529 
530 
531 
532 
533 /*
534 ==============================================================================
535 
536 Kill box
537 
538 ==============================================================================
539 */
540 
541 /*
542 =================
543 KillBox
544 
545 Kills all entities that would touch the proposed new positioning
546 of ent.  Ent should be unlinked before calling this!
547 =================
548 */
KillBox(edict_t * ent)549 qboolean KillBox (edict_t *ent)
550 {
551 	trace_t		tr;
552 
553 	while (1)
554 	{
555 		tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID);
556 		if (!tr.ent)
557 			break;
558 
559 		// nail it
560 		T_Damage (tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
561 
562 		// if we didn't kill it, fail
563 		if (tr.ent->solid)
564 			return false;
565 	}
566 
567 	return true;		// all clear
568 }
569