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