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.numEdicts] ; 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.numEdicts]; 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 (Vec3Length(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 	Q_snprintfz (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 (Vec3Compare (angles, VEC_UP))
317 	{
318 		Vec3Copy (MOVEDIR_UP, movedir);
319 	}
320 	else if (Vec3Compare (angles, VEC_DOWN))
321 	{
322 		Vec3Copy (MOVEDIR_DOWN, movedir);
323 	}
324 	else
325 	{
326 		Angles_Vectors (angles, movedir, NULL, NULL);
327 	}
328 
329 	Vec3Clear (angles);
330 }
331 
332 
G_CopyString(char * in)333 char *G_CopyString (char *in)
334 {
335 	char	*out;
336 
337 	out = gi.TagMalloc (strlen(in)+1, TAG_LEVEL);
338 	strcpy (out, in);
339 	return out;
340 }
341 
342 
G_InitEdict(edict_t * e)343 void G_InitEdict (edict_t *e)
344 {
345 	e->inUse = qTrue;
346 	e->classname = "noclass";
347 	e->gravity = 1.0;
348 	e->s.number = e - g_edicts;
349 }
350 
351 /*
352 =================
353 G_Spawn
354 
355 Either finds a free edict, or allocates a new one.
356 Try to avoid reusing an entity that was recently freed, because it
357 can cause the client to think the entity morphed into something else
358 instead of being removed and recreated, which can cause interpolated
359 angles and bad trails.
360 =================
361 */
G_Spawn(void)362 edict_t *G_Spawn (void)
363 {
364 	int			i;
365 	edict_t		*e;
366 
367 	e = &g_edicts[(int)maxclients->floatVal+1];
368 	for ( i=maxclients->floatVal+1 ; i<globals.numEdicts ; i++, e++)
369 	{
370 		// the first couple seconds of server time can involve a lot of
371 		// freeing and allocating, so relax the replacement policy
372 		if (!e->inUse && ( e->freetime < 2 || level.time - e->freetime > 0.5 ) )
373 		{
374 			G_InitEdict (e);
375 			return e;
376 		}
377 	}
378 
379 	if (i == game.maxentities)
380 		gi.error ("ED_Alloc: no free edicts");
381 
382 	globals.numEdicts++;
383 	G_InitEdict (e);
384 	return e;
385 }
386 
387 /*
388 =================
389 G_FreeEdict
390 
391 Marks the edict as free
392 =================
393 */
G_FreeEdict(edict_t * ed)394 void G_FreeEdict (edict_t *ed)
395 {
396 	gi.unlinkentity (ed);		// unlink from world
397 
398 	if ((ed - g_edicts) <= (maxclients->floatVal + BODY_QUEUE_SIZE))
399 	{
400 //		gi.dprintf("tried to free special edict\n");
401 		return;
402 	}
403 
404 	memset (ed, 0, sizeof(*ed));
405 	ed->classname = "freed";
406 	ed->freetime = level.time;
407 	ed->inUse = qFalse;
408 }
409 
410 
411 /*
412 ============
413 G_TouchTriggers
414 
415 ============
416 */
G_TouchTriggers(edict_t * ent)417 void	G_TouchTriggers (edict_t *ent)
418 {
419 	int			i, num;
420 	edict_t		*touch[MAX_CS_EDICTS], *hit;
421 
422 	// dead things don't activate triggers!
423 	if ((ent->client || (ent->svFlags & SVF_MONSTER)) && (ent->health <= 0))
424 		return;
425 
426 	num = gi.BoxEdicts (ent->absMin, ent->absMax, touch, MAX_CS_EDICTS, AREA_TRIGGERS);
427 
428 	// be careful, it is possible to have an entity in this
429 	// list removed before we get to it (killtriggered)
430 	for (i=0 ; i<num ; i++)
431 	{
432 		hit = touch[i];
433 		if (!hit->inUse)
434 			continue;
435 		if (!hit->touch)
436 			continue;
437 		hit->touch (hit, ent, NULL, NULL);
438 	}
439 }
440 
441 /*
442 ============
443 G_TouchSolids
444 
445 Call after linking a new trigger in during gameplay
446 to force all entities it covers to immediately touch it
447 ============
448 */
G_TouchSolids(edict_t * ent)449 void	G_TouchSolids (edict_t *ent)
450 {
451 	int			i, num;
452 	edict_t		*touch[MAX_CS_EDICTS], *hit;
453 
454 	num = gi.BoxEdicts (ent->absMin, ent->absMax, touch, MAX_CS_EDICTS, AREA_SOLID);
455 
456 	// be careful, it is possible to have an entity in this
457 	// list removed before we get to it (killtriggered)
458 	for (i=0 ; i<num ; i++)
459 	{
460 		hit = touch[i];
461 		if (!hit->inUse)
462 			continue;
463 		if (ent->touch)
464 			ent->touch (hit, ent, NULL, NULL);
465 		if (!ent->inUse)
466 			break;
467 	}
468 }
469 
470 
471 
472 
473 /*
474 ==============================================================================
475 
476 Kill box
477 
478 ==============================================================================
479 */
480 
481 /*
482 =================
483 KillBox
484 
485 Kills all entities that would touch the proposed new positioning
486 of ent.  Ent should be unlinked before calling this!
487 =================
488 */
KillBox(edict_t * ent)489 qBool KillBox (edict_t *ent)
490 {
491 	trace_t		tr;
492 
493 	while (1)
494 	{
495 		tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID);
496 		if (!tr.ent)
497 			break;
498 
499 		// nail it
500 		T_Damage (tr.ent, ent, ent, vec3Origin, ent->s.origin, vec3Origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
501 
502 		// if we didn't kill it, fail
503 		if (tr.ent->solid)
504 			return qFalse;
505 	}
506 
507 	return qTrue;		// all clear
508 }
509