|
|
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: void G_ProjectSource2 (vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t up, vec3_t result)
14: {
15: result[0] = point[0] + forward[0] * distance[0] + right[0] * distance[1] + up[0] * distance[2];
16: result[1] = point[1] + forward[1] * distance[0] + right[1] * distance[1] + up[1] * distance[2];
17: result[2] = point[2] + forward[2] * distance[0] + right[2] * distance[1] + up[2] * distance[2];
18: }
19:
20: /*
21: =============
22: G_Find
23:
24: Searches all active entities for the next one that holds
25: the matching string at fieldofs (use the FOFS() macro) in the structure.
26:
27: Searches beginning at the edict after from, or the beginning if NULL
28: NULL will be returned if the end of the list is reached.
29:
30: =============
31: */
32: edict_t *G_Find (edict_t *from, int fieldofs, char *match)
33: {
34: char *s;
35:
36: if (!from)
37: from = g_edicts;
38: else
39: from++;
40:
41: for ( ; from < &g_edicts[globals.num_edicts] ; from++)
42: {
43: if (!from->inuse)
44: continue;
45: s = *(char **) ((byte *)from + fieldofs);
46: if (!s)
47: continue;
48: if (!Q_stricmp (s, match))
49: return from;
50: }
51:
52: return NULL;
53: }
54:
55:
56: /*
57: =================
58: findradius
59:
60: Returns entities that have origins within a spherical area
61:
62: findradius (origin, radius)
63: =================
64: */
65: edict_t *findradius (edict_t *from, vec3_t org, float rad)
66: {
67: vec3_t eorg;
68: int j;
69:
70: if (!from)
71: from = g_edicts;
72: else
73: from++;
74: for ( ; from < &g_edicts[globals.num_edicts]; from++)
75: {
76: if (!from->inuse)
77: continue;
78: if (from->solid == SOLID_NOT)
79: continue;
80: for (j=0 ; j<3 ; j++)
81: eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5);
82: if (VectorLength(eorg) > rad)
83: continue;
84: return from;
85: }
86:
87: return NULL;
88: }
89:
90: /*
91: =================
92: findradius2
93:
94: Returns entities that have origins within a spherical area
95:
96: ROGUE - tweaks for performance for tesla specific code
97: only returns entities that can be damaged
98: only returns entities that are SVF_DAMAGEABLE
99:
100: findradius2 (origin, radius)
101: =================
102: */
103: edict_t *findradius2 (edict_t *from, vec3_t org, float rad)
104: {
105: // rad must be positive
106: vec3_t eorg;
107: int j;
108:
109: if (!from)
110: from = g_edicts;
111: else
112: from++;
113: for ( ; from < &g_edicts[globals.num_edicts]; from++)
114: {
115: if (!from->inuse)
116: continue;
117: if (from->solid == SOLID_NOT)
118: continue;
119: if (!from->takedamage)
120: continue;
121: if (!(from->svflags & SVF_DAMAGEABLE))
122: continue;
123: for (j=0 ; j<3 ; j++)
124: eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5);
125: if (VectorLength(eorg) > rad)
126: continue;
127: return from;
128: }
129:
130: return NULL;
131: }
132:
133:
134: /*
135: =============
136: G_PickTarget
137:
138: Searches all active entities for the next one that holds
139: the matching string at fieldofs (use the FOFS() macro) in the structure.
140:
141: Searches beginning at the edict after from, or the beginning if NULL
142: NULL will be returned if the end of the list is reached.
143:
144: =============
145: */
146: #define MAXCHOICES 8
147:
148: edict_t *G_PickTarget (char *targetname)
149: {
150: edict_t *ent = NULL;
151: int num_choices = 0;
152: edict_t *choice[MAXCHOICES];
153:
154: if (!targetname)
155: {
156: gi.dprintf("G_PickTarget called with NULL targetname\n");
157: return NULL;
158: }
159:
160: while(1)
161: {
162: ent = G_Find (ent, FOFS(targetname), targetname);
163: if (!ent)
164: break;
165: choice[num_choices++] = ent;
166: if (num_choices == MAXCHOICES)
167: break;
168: }
169:
170: if (!num_choices)
171: {
172: gi.dprintf("G_PickTarget: target %s not found\n", targetname);
173: return NULL;
174: }
175:
176: return choice[rand() % num_choices];
177: }
178:
179:
180:
181: void Think_Delay (edict_t *ent)
182: {
183: G_UseTargets (ent, ent->activator);
184: G_FreeEdict (ent);
185: }
186:
187: /*
188: ==============================
189: G_UseTargets
190:
191: the global "activator" should be set to the entity that initiated the firing.
192:
193: If self.delay is set, a DelayedUse entity will be created that will actually
194: do the SUB_UseTargets after that many seconds have passed.
195:
196: Centerprints any self.message to the activator.
197:
198: Search for (string)targetname in all entities that
199: match (string)self.target and call their .use function
200:
201: ==============================
202: */
203: void G_UseTargets (edict_t *ent, edict_t *activator)
204: {
205: edict_t *t;
206: edict_t *master;
207: qboolean done = false;
208:
209: //
210: // check for a delay
211: //
212: if (ent->delay)
213: {
214: // create a temp object to fire at a later time
215: t = G_Spawn();
216: t->classname = "DelayedUse";
217: t->nextthink = level.time + ent->delay;
218: t->think = Think_Delay;
219: t->activator = activator;
220: if (!activator)
221: gi.dprintf ("Think_Delay with no activator\n");
222: t->message = ent->message;
223: t->target = ent->target;
224: t->killtarget = ent->killtarget;
225: return;
226: }
227:
228:
229: //
230: // print the message
231: //
232: if ((ent->message) && !(activator->svflags & SVF_MONSTER))
233: {
234: gi.centerprintf (activator, "%s", ent->message);
235: if (ent->noise_index)
236: gi.sound (activator, CHAN_AUTO, ent->noise_index, 1, ATTN_NORM, 0);
237: else
238: gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
239: }
240:
241: //
242: // kill killtargets
243: //
244: if (ent->killtarget)
245: {
246: t = NULL;
247: while ((t = G_Find (t, FOFS(targetname), ent->killtarget)))
248: {
249: // PMM - if this entity is part of a train, cleanly remove it
250: if (t->flags & FL_TEAMSLAVE)
251: {
252: if ((g_showlogic) && (g_showlogic->value))
253: gi.dprintf ("Removing %s from train!\n", t->classname);
254:
255: if (t->teammaster)
256: {
257: master = t->teammaster;
258: while (!done)
259: {
260: if (master->teamchain == t)
261: {
262: master->teamchain = t->teamchain;
263: done = true;
264: }
265: master = master->teamchain;
266: if (!master)
267: {
268: if ((g_showlogic) && (g_showlogic->value))
269: gi.dprintf ("Couldn't find myself in master's chain, ignoring!\n");
270: }
271: }
272: }
273: else
274: {
275: if ((g_showlogic) && (g_showlogic->value))
276: gi.dprintf ("No master to free myself from, ignoring!\n");
277: }
278: }
279: // PMM
280: G_FreeEdict (t);
281: if (!ent->inuse)
282: {
283: gi.dprintf("entity was removed while using killtargets\n");
284: return;
285: }
286: }
287: }
288:
289: //
290: // fire targets
291: //
292: if (ent->target)
293: {
294: t = NULL;
295: while ((t = G_Find (t, FOFS(targetname), ent->target)))
296: {
297: // doors fire area portals in a specific way
298: if (!Q_stricmp(t->classname, "func_areaportal") &&
299: (!Q_stricmp(ent->classname, "func_door") || !Q_stricmp(ent->classname, "func_door_rotating")))
300: continue;
301:
302: if (t == ent)
303: {
304: gi.dprintf ("WARNING: Entity used itself.\n");
305: }
306: else
307: {
308: if (t->use)
309: t->use (t, ent, activator);
310: }
311: if (!ent->inuse)
312: {
313: gi.dprintf("entity was removed while using targets\n");
314: return;
315: }
316: }
317: }
318: }
319:
320:
321: /*
322: =============
323: TempVector
324:
325: This is just a convenience function
326: for making temporary vectors for function calls
327: =============
328: */
329: float *tv (float x, float y, float z)
330: {
331: static int index;
332: static vec3_t vecs[8];
333: float *v;
334:
335: // use an array so that multiple tempvectors won't collide
336: // for a while
337: v = vecs[index];
338: index = (index + 1)&7;
339:
340: v[0] = x;
341: v[1] = y;
342: v[2] = z;
343:
344: return v;
345: }
346:
347:
348: /*
349: =============
350: VectorToString
351:
352: This is just a convenience function
353: for printing vectors
354: =============
355: */
356: char *vtos (vec3_t v)
357: {
358: static int index;
359: static char str[8][32];
360: char *s;
361:
362: // use an array so that multiple vtos won't collide
363: s = str[index];
364: index = (index + 1)&7;
365:
366: Com_sprintf (s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]);
367:
368: return s;
369: }
370:
371:
372: vec3_t VEC_UP = {0, -1, 0};
373: vec3_t MOVEDIR_UP = {0, 0, 1};
374: vec3_t VEC_DOWN = {0, -2, 0};
375: vec3_t MOVEDIR_DOWN = {0, 0, -1};
376:
377: void G_SetMovedir (vec3_t angles, vec3_t movedir)
378: {
379: if (VectorCompare (angles, VEC_UP))
380: {
381: VectorCopy (MOVEDIR_UP, movedir);
382: }
383: else if (VectorCompare (angles, VEC_DOWN))
384: {
385: VectorCopy (MOVEDIR_DOWN, movedir);
386: }
387: else
388: {
389: AngleVectors (angles, movedir, NULL, NULL);
390: }
391:
392: VectorClear (angles);
393: }
394:
395:
396: float vectoyaw (vec3_t vec)
397: {
398: float yaw;
399:
400: // PMM - fixed to correct for pitch of 0
401: if (/*vec[YAW] == 0 &&*/ vec[PITCH] == 0)
402: if (vec[YAW] == 0)
403: yaw = 0;
404: else if (vec[YAW] > 0)
405: yaw = 90;
406: else
407: yaw = 270;
408: else
409: {
410: yaw = (int) (atan2(vec[YAW], vec[PITCH]) * 180 / M_PI);
411: if (yaw < 0)
412: yaw += 360;
413: }
414:
415: return yaw;
416: }
417:
418: float vectoyaw2 (vec3_t vec)
419: {
420: float yaw;
421:
422: // PMM - fixed to correct for pitch of 0
423: if (/*vec[YAW] == 0 &&*/ vec[PITCH] == 0)
424: if (vec[YAW] == 0)
425: yaw = 0;
426: else if (vec[YAW] > 0)
427: yaw = 90;
428: else
429: yaw = 270;
430: else
431: {
432: yaw = (atan2(vec[YAW], vec[PITCH]) * 180 / M_PI);
433: if (yaw < 0)
434: yaw += 360;
435: }
436:
437: return yaw;
438: }
439:
440:
441: void vectoangles (vec3_t value1, vec3_t angles)
442: {
443: float forward;
444: float yaw, pitch;
445:
446: if (value1[1] == 0 && value1[0] == 0)
447: {
448: yaw = 0;
449: if (value1[2] > 0)
450: pitch = 90;
451: else
452: pitch = 270;
453: }
454: else
455: {
456: // PMM - fixed to correct for pitch of 0
457: if (value1[0])
458: yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
459: else if (value1[1] > 0)
460: yaw = 90;
461: else
462: yaw = 270;
463: if (yaw < 0)
464: yaw += 360;
465:
466: forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
467: pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
468: if (pitch < 0)
469: pitch += 360;
470: }
471:
472: angles[PITCH] = -pitch;
473: angles[YAW] = yaw;
474: angles[ROLL] = 0;
475: }
476:
477: void vectoangles2 (vec3_t value1, vec3_t angles)
478: {
479: float forward;
480: float yaw, pitch;
481:
482: if (value1[1] == 0 && value1[0] == 0)
483: {
484: yaw = 0;
485: if (value1[2] > 0)
486: pitch = 90;
487: else
488: pitch = 270;
489: }
490: else
491: {
492: // PMM - fixed to correct for pitch of 0
493: if (value1[0])
494: yaw = (atan2(value1[1], value1[0]) * 180 / M_PI);
495: else if (value1[1] > 0)
496: yaw = 90;
497: else
498: yaw = 270;
499:
500: if (yaw < 0)
501: yaw += 360;
502:
503: forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
504: pitch = (atan2(value1[2], forward) * 180 / M_PI);
505: if (pitch < 0)
506: pitch += 360;
507: }
508:
509: angles[PITCH] = -pitch;
510: angles[YAW] = yaw;
511: angles[ROLL] = 0;
512: }
513:
514: char *G_CopyString (char *in)
515: {
516: char *out;
517:
518: out = gi.TagMalloc (strlen(in)+1, TAG_LEVEL);
519: strcpy (out, in);
520: return out;
521: }
522:
523:
524: void G_InitEdict (edict_t *e)
525: {
526: // ROGUE
527: // FIXME -
528: // this fixes a bug somewhere that is settling "nextthink" for an entity that has
529: // already been released. nextthink is being set to FRAMETIME after level.time,
530: // since freetime = nextthink - 0.1
531: if (e->nextthink)
532: {
533: if ((g_showlogic) && (g_showlogic->value))
534: gi.dprintf ("G_SPAWN: Fixed bad nextthink time\n");
535: e->nextthink = 0;
536: }
537: // ROGUE
538:
539: e->inuse = true;
540: e->classname = "noclass";
541: e->gravity = 1.0;
542: e->s.number = e - g_edicts;
543:
544: //PGM - do this before calling the spawn function so it can be overridden.
545: #ifdef ROGUE_GRAVITY
546: e->gravityVector[0] = 0.0;
547: e->gravityVector[1] = 0.0;
548: e->gravityVector[2] = -1.0;
549: #endif
550: //PGM
551: }
552:
553: /*
554: =================
555: G_Spawn
556:
557: Either finds a free edict, or allocates a new one.
558: Try to avoid reusing an entity that was recently freed, because it
559: can cause the client to think the entity morphed into something else
560: instead of being removed and recreated, which can cause interpolated
561: angles and bad trails.
562: =================
563: */
564: edict_t *G_Spawn (void)
565: {
566: int i;
567: edict_t *e;
568:
569: e = &g_edicts[(int)maxclients->value+1];
570: for ( i=maxclients->value+1 ; i<globals.num_edicts ; i++, e++)
571: {
572: // the first couple seconds of server time can involve a lot of
573: // freeing and allocating, so relax the replacement policy
574: if (!e->inuse && ( e->freetime < 2 || level.time - e->freetime > 0.5 ) )
575: {
576: G_InitEdict (e);
577: return e;
578: }
579: }
580:
581: if (i == game.maxentities)
582: gi.error ("ED_Alloc: no free edicts");
583:
584: globals.num_edicts++;
585: G_InitEdict (e);
586: return e;
587: }
588:
589: /*
590: =================
591: G_FreeEdict
592:
593: Marks the edict as free
594: =================
595: */
596: void G_FreeEdict (edict_t *ed)
597: {
598: gi.unlinkentity (ed); // unlink from world
599:
600: if ((ed - g_edicts) <= (maxclients->value + BODY_QUEUE_SIZE))
601: {
602: // gi.dprintf("tried to free special edict\n");
603: return;
604: }
605:
606: memset (ed, 0, sizeof(*ed));
607: ed->classname = "freed";
608: ed->freetime = level.time;
609: ed->inuse = false;
610: }
611:
612:
613: /*
614: ============
615: G_TouchTriggers
616:
617: ============
618: */
619: void G_TouchTriggers (edict_t *ent)
620: {
621: int i, num;
622: edict_t *touch[MAX_EDICTS], *hit;
623:
624: // dead things don't activate triggers!
625: if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0))
626: return;
627:
628: num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
629: , MAX_EDICTS, AREA_TRIGGERS);
630:
631: // be careful, it is possible to have an entity in this
632: // list removed before we get to it (killtriggered)
633: for (i=0 ; i<num ; i++)
634: {
635: hit = touch[i];
636: if (!hit->inuse)
637: continue;
638: if (!hit->touch)
639: continue;
640: hit->touch (hit, ent, NULL, NULL);
641: }
642: }
643:
644: /*
645: ============
646: G_TouchSolids
647:
648: Call after linking a new trigger in during gameplay
649: to force all entities it covers to immediately touch it
650: ============
651: */
652: void G_TouchSolids (edict_t *ent)
653: {
654: int i, num;
655: edict_t *touch[MAX_EDICTS], *hit;
656:
657: num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
658: , MAX_EDICTS, AREA_SOLID);
659:
660: // be careful, it is possible to have an entity in this
661: // list removed before we get to it (killtriggered)
662: for (i=0 ; i<num ; i++)
663: {
664: hit = touch[i];
665: if (!hit->inuse)
666: continue;
667: if (ent->touch)
668: ent->touch (hit, ent, NULL, NULL);
669: if (!ent->inuse)
670: break;
671: }
672: }
673:
674:
675:
676:
677: /*
678: ==============================================================================
679:
680: Kill box
681:
682: ==============================================================================
683: */
684:
685: /*
686: =================
687: KillBox
688:
689: Kills all entities that would touch the proposed new positioning
690: of ent. Ent should be unlinked before calling this!
691: =================
692: */
693: qboolean KillBox (edict_t *ent)
694: {
695: trace_t tr;
696:
697: while (1)
698: {
699: tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID);
700: if (!tr.ent)
701: break;
702:
703: // nail it
704: T_Damage (tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
705:
706: // if we didn't kill it, fail
707: if (tr.ent->solid)
708: return false;
709: }
710:
711: return true; // all clear
712: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.