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