|
|
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: // gi.dprintf("TARGET: activating %s\n", ent->target);
208:
209: //
210: // fire targets
211: //
212: if (ent->target)
213: {
214: t = NULL;
215: while ((t = G_Find (t, FOFS(targetname), ent->target)))
216: {
217: // doors fire area portals in a specific way
218: if (!Q_stricmp(t->classname, "func_areaportal") &&
219: (!Q_stricmp(ent->classname, "func_door") || !Q_stricmp(ent->classname, "func_door_rotating")))
220: continue;
221:
222: if (t == ent)
223: {
224: gi.dprintf ("WARNING: Entity used itself.\n");
225: }
226: else
227: {
228: if (t->use)
229: t->use (t, ent, activator);
230: }
231: if (!ent->inuse)
232: {
233: gi.dprintf("entity was removed while using targets\n");
234: return;
235: }
236: }
237: }
238: }
239:
240:
241: /*
242: =============
243: TempVector
244:
245: This is just a convenience function
246: for making temporary vectors for function calls
247: =============
248: */
249: float *tv (float x, float y, float z)
250: {
251: static int index;
252: static vec3_t vecs[8];
253: float *v;
254:
255: // use an array so that multiple tempvectors won't collide
256: // for a while
257: v = vecs[index];
258: index = (index + 1)&7;
259:
260: v[0] = x;
261: v[1] = y;
262: v[2] = z;
263:
264: return v;
265: }
266:
267:
268: /*
269: =============
270: VectorToString
271:
272: This is just a convenience function
273: for printing vectors
274: =============
275: */
276: char *vtos (vec3_t v)
277: {
278: static int index;
279: static char str[8][32];
280: char *s;
281:
282: // use an array so that multiple vtos won't collide
283: s = str[index];
284: index = (index + 1)&7;
285:
286: Com_sprintf (s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]);
287:
288: return s;
289: }
290:
291:
292: vec3_t VEC_UP = {0, -1, 0};
293: vec3_t MOVEDIR_UP = {0, 0, 1};
294: vec3_t VEC_DOWN = {0, -2, 0};
295: vec3_t MOVEDIR_DOWN = {0, 0, -1};
296:
297: void G_SetMovedir (vec3_t angles, vec3_t movedir)
298: {
299: if (VectorCompare (angles, VEC_UP))
300: {
301: VectorCopy (MOVEDIR_UP, movedir);
302: }
303: else if (VectorCompare (angles, VEC_DOWN))
304: {
305: VectorCopy (MOVEDIR_DOWN, movedir);
306: }
307: else
308: {
309: AngleVectors (angles, movedir, NULL, NULL);
310: }
311:
312: VectorClear (angles);
313: }
314:
315:
316: float vectoyaw (vec3_t vec)
317: {
318: float yaw;
319:
320: if (/* vec[YAW] == 0 && */ vec[PITCH] == 0)
321: {
322: yaw = 0;
323: if (vec[YAW] > 0)
324: yaw = 90;
325: else if (vec[YAW] < 0)
326: yaw = -90;
327: }
328: else
329: {
330: yaw = (int) (atan2(vec[YAW], vec[PITCH]) * 180 / M_PI);
331: if (yaw < 0)
332: yaw += 360;
333: }
334:
335: return yaw;
336: }
337:
338:
339: void vectoangles (vec3_t value1, vec3_t angles)
340: {
341: float forward;
342: float yaw, pitch;
343:
344: if (value1[1] == 0 && value1[0] == 0)
345: {
346: yaw = 0;
347: if (value1[2] > 0)
348: pitch = 90;
349: else
350: pitch = 270;
351: }
352: else
353: {
354: if (value1[0])
355: yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
356: else if (value1[1] > 0)
357: yaw = 90;
358: else
359: yaw = -90;
360: if (yaw < 0)
361: yaw += 360;
362:
363: forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
364: pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
365: if (pitch < 0)
366: pitch += 360;
367: }
368:
369: angles[PITCH] = -pitch;
370: angles[YAW] = yaw;
371: angles[ROLL] = 0;
372: }
373:
374: char *G_CopyString (char *in)
375: {
376: char *out;
377:
378: out = gi.TagMalloc (strlen(in)+1, TAG_LEVEL);
379: strcpy (out, in);
380: return out;
381: }
382:
383:
384: void G_InitEdict (edict_t *e)
385: {
386: e->inuse = true;
387: e->classname = "noclass";
388: e->gravity = 1.0;
389: e->s.number = e - g_edicts;
390: }
391:
392: /*
393: =================
394: G_Spawn
395:
396: Either finds a free edict, or allocates a new one.
397: Try to avoid reusing an entity that was recently freed, because it
398: can cause the client to think the entity morphed into something else
399: instead of being removed and recreated, which can cause interpolated
400: angles and bad trails.
401: =================
402: */
403: edict_t *G_Spawn (void)
404: {
405: int i;
406: edict_t *e;
407:
408: e = &g_edicts[(int)maxclients->value+1];
409: for ( i=maxclients->value+1 ; i<globals.num_edicts ; i++, e++)
410: {
411: // the first couple seconds of server time can involve a lot of
412: // freeing and allocating, so relax the replacement policy
413: if (!e->inuse && ( e->freetime < 2 || level.time - e->freetime > 0.5 ) )
414: {
415: G_InitEdict (e);
416: return e;
417: }
418: }
419:
420: if (i == game.maxentities)
421: gi.error ("ED_Alloc: no free edicts");
422:
423: globals.num_edicts++;
424: G_InitEdict (e);
425: return e;
426: }
427:
428: /*
429: =================
430: G_FreeEdict
431:
432: Marks the edict as free
433: =================
434: */
435: void G_FreeEdict (edict_t *ed)
436: {
437: gi.unlinkentity (ed); // unlink from world
438:
439: if ((ed - g_edicts) <= (maxclients->value + BODY_QUEUE_SIZE))
440: {
441: // gi.dprintf("tried to free special edict\n");
442: return;
443: }
444:
445: memset (ed, 0, sizeof(*ed));
446: ed->classname = "freed";
447: ed->freetime = level.time;
448: ed->inuse = false;
449: }
450:
451:
452: /*
453: ============
454: G_TouchTriggers
455:
456: ============
457: */
458: void G_TouchTriggers (edict_t *ent)
459: {
460: int i, num;
461: edict_t *touch[MAX_EDICTS], *hit;
462:
463: // dead things don't activate triggers!
464: if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0))
465: return;
466:
467: num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
468: , MAX_EDICTS, AREA_TRIGGERS);
469:
470: // be careful, it is possible to have an entity in this
471: // list removed before we get to it (killtriggered)
472: for (i=0 ; i<num ; i++)
473: {
474: hit = touch[i];
475: if (!hit->inuse)
476: continue;
477: if (!hit->touch)
478: continue;
479: hit->touch (hit, ent, NULL, NULL);
480: }
481: }
482:
483: /*
484: ============
485: G_TouchSolids
486:
487: Call after linking a new trigger in during gameplay
488: to force all entities it covers to immediately touch it
489: ============
490: */
491: void G_TouchSolids (edict_t *ent)
492: {
493: int i, num;
494: edict_t *touch[MAX_EDICTS], *hit;
495:
496: num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
497: , MAX_EDICTS, AREA_SOLID);
498:
499: // be careful, it is possible to have an entity in this
500: // list removed before we get to it (killtriggered)
501: for (i=0 ; i<num ; i++)
502: {
503: hit = touch[i];
504: if (!hit->inuse)
505: continue;
506: if (ent->touch)
507: ent->touch (hit, ent, NULL, NULL);
508: if (!ent->inuse)
509: break;
510: }
511: }
512:
513:
514:
515:
516: /*
517: ==============================================================================
518:
519: Kill box
520:
521: ==============================================================================
522: */
523:
524: /*
525: =================
526: KillBox
527:
528: Kills all entities that would touch the proposed new positioning
529: of ent. Ent should be unlinked before calling this!
530: =================
531: */
532: qboolean KillBox (edict_t *ent)
533: {
534: trace_t tr;
535:
536: while (1)
537: {
538: tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID);
539: if (!tr.ent)
540: break;
541:
542: // nail it
543: T_Damage (tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
544:
545: // if we didn't kill it, fail
546: if (tr.ent->solid)
547: return false;
548: }
549:
550: return true; // all clear
551: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.