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