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