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