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