|
|
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:
318: if (vec[YAW] == 0 && vec[PITCH] == 0)
319: yaw = 0;
320: else
321: {
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: {
333: float forward;
334: float yaw, pitch;
335:
336: if (value1[1] == 0 && value1[0] == 0)
337: {
338: yaw = 0;
339: if (value1[2] > 0)
340: pitch = 90;
341: else
342: pitch = 270;
343: }
344: else
345: {
346: yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
347: if (yaw < 0)
348: yaw += 360;
349:
350: forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
351: pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
352: if (pitch < 0)
353: pitch += 360;
354: }
355:
356: angles[PITCH] = -pitch;
357: angles[YAW] = yaw;
358: angles[ROLL] = 0;
359: }
360:
361: char *G_CopyString (char *in)
362: {
363: char *out;
364:
365: out = gi.TagMalloc (strlen(in)+1, TAG_LEVEL);
366: strcpy (out, in);
367: return out;
368: }
369:
370:
371: void G_InitEdict (edict_t *e)
372: {
373: e->inuse = true;
374: e->classname = "noclass";
375: e->gravity = 1.0;
376: e->s.number = e - g_edicts;
377: }
378:
379: /*
380: =================
381: G_Spawn
382:
383: Either finds a free edict, or allocates a new one.
384: Try to avoid reusing an entity that was recently freed, because it
385: can cause the client to think the entity morphed into something else
386: instead of being removed and recreated, which can cause interpolated
387: angles and bad trails.
388: =================
389: */
390: edict_t *G_Spawn (void)
391: {
392: int i;
393: edict_t *e;
394:
395: e = &g_edicts[(int)maxclients->value+1];
396: for ( i=maxclients->value+1 ; i<globals.num_edicts ; i++, e++)
397: {
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: {
402: G_InitEdict (e);
403: return e;
404: }
405: }
406:
407: if (i == game.maxentities)
408: gi.error ("ED_Alloc: no free edicts");
409:
410: globals.num_edicts++;
411: G_InitEdict (e);
412: return e;
413: }
414:
415: /*
416: =================
417: G_FreeEdict
418:
419: Marks the edict as free
420: =================
421: */
422: void G_FreeEdict (edict_t *ed)
423: {
424: gi.unlinkentity (ed); // unlink from world
425:
426: if ((ed - g_edicts) <= (maxclients->value + BODY_QUEUE_SIZE))
427: {
428: // gi.dprintf("tried to free special edict\n");
429: return;
430: }
431:
432: memset (ed, 0, sizeof(*ed));
433: ed->classname = "freed";
434: ed->freetime = level.time;
435: ed->inuse = false;
436: }
437:
438:
439: /*
440: ============
441: G_TouchTriggers
442:
443: ============
444: */
445: void G_TouchTriggers (edict_t *ent)
446: {
447: int i, num;
448: edict_t *touch[MAX_EDICTS], *hit;
449:
450: // dead things don't activate triggers!
451: if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0))
452: return;
453:
454: num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
455: , MAX_EDICTS, AREA_TRIGGERS);
456:
457: // be careful, it is possible to have an entity in this
458: // list removed before we get to it (killtriggered)
459: for (i=0 ; i<num ; i++)
460: {
461: hit = touch[i];
462: if (!hit->inuse)
463: continue;
464: if (!hit->touch)
465: continue;
466: hit->touch (hit, ent, NULL, NULL);
467: }
468: }
469:
470: /*
471: ============
472: G_TouchSolids
473:
474: Call after linking a new trigger in during gameplay
475: to force all entities it covers to immediately touch it
476: ============
477: */
478: void G_TouchSolids (edict_t *ent)
479: {
480: int i, num;
481: edict_t *touch[MAX_EDICTS], *hit;
482:
483: num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
484: , MAX_EDICTS, AREA_SOLID);
485:
486: // be careful, it is possible to have an entity in this
487: // list removed before we get to it (killtriggered)
488: for (i=0 ; i<num ; i++)
489: {
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: Kill box
507:
508: ==============================================================================
509: */
510:
511: /*
512: =================
513: KillBox
514:
515: Kills all entities that would touch the proposed new positioning
516: of ent. Ent should be unlinked before calling this!
517: =================
518: */
519: qboolean KillBox (edict_t *ent)
520: {
521: trace_t tr;
522:
523: while (1)
524: {
525: tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID);
526: if (!tr.ent)
527: break;
528:
529: // nail it
530: T_Damage (tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
531:
532: // if we didn't kill it, fail
533: if (tr.ent->solid)
534: return false;
535: }
536:
537: return true; // all clear
538: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.