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