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