|
|
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.