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