|
|
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: // gi.dprintf("TARGET: activating %s\n", ent->target); ! 208: ! 209: // ! 210: // fire targets ! 211: // ! 212: if (ent->target) ! 213: { ! 214: t = NULL; ! 215: while ((t = G_Find (t, FOFS(targetname), ent->target))) ! 216: { ! 217: // doors fire area portals in a specific way ! 218: if (!Q_stricmp(t->classname, "func_areaportal") && ! 219: (!Q_stricmp(ent->classname, "func_door") || !Q_stricmp(ent->classname, "func_door_rotating"))) ! 220: continue; ! 221: ! 222: if (t == ent) ! 223: { ! 224: gi.dprintf ("WARNING: Entity used itself.\n"); ! 225: } ! 226: else ! 227: { ! 228: if (t->use) ! 229: t->use (t, ent, activator); ! 230: } ! 231: if (!ent->inuse) ! 232: { ! 233: gi.dprintf("entity was removed while using targets\n"); ! 234: return; ! 235: } ! 236: } ! 237: } ! 238: } ! 239: ! 240: ! 241: /* ! 242: ============= ! 243: TempVector ! 244: ! 245: This is just a convenience function ! 246: for making temporary vectors for function calls ! 247: ============= ! 248: */ ! 249: float *tv (float x, float y, float z) ! 250: { ! 251: static int index; ! 252: static vec3_t vecs[8]; ! 253: float *v; ! 254: ! 255: // use an array so that multiple tempvectors won't collide ! 256: // for a while ! 257: v = vecs[index]; ! 258: index = (index + 1)&7; ! 259: ! 260: v[0] = x; ! 261: v[1] = y; ! 262: v[2] = z; ! 263: ! 264: return v; ! 265: } ! 266: ! 267: ! 268: /* ! 269: ============= ! 270: VectorToString ! 271: ! 272: This is just a convenience function ! 273: for printing vectors ! 274: ============= ! 275: */ ! 276: char *vtos (vec3_t v) ! 277: { ! 278: static int index; ! 279: static char str[8][32]; ! 280: char *s; ! 281: ! 282: // use an array so that multiple vtos won't collide ! 283: s = str[index]; ! 284: index = (index + 1)&7; ! 285: ! 286: Com_sprintf (s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]); ! 287: ! 288: return s; ! 289: } ! 290: ! 291: ! 292: vec3_t VEC_UP = {0, -1, 0}; ! 293: vec3_t MOVEDIR_UP = {0, 0, 1}; ! 294: vec3_t VEC_DOWN = {0, -2, 0}; ! 295: vec3_t MOVEDIR_DOWN = {0, 0, -1}; ! 296: ! 297: void G_SetMovedir (vec3_t angles, vec3_t movedir) ! 298: { ! 299: if (VectorCompare (angles, VEC_UP)) ! 300: { ! 301: VectorCopy (MOVEDIR_UP, movedir); ! 302: } ! 303: else if (VectorCompare (angles, VEC_DOWN)) ! 304: { ! 305: VectorCopy (MOVEDIR_DOWN, movedir); ! 306: } ! 307: else ! 308: { ! 309: AngleVectors (angles, movedir, NULL, NULL); ! 310: } ! 311: ! 312: VectorClear (angles); ! 313: } ! 314: ! 315: ! 316: float vectoyaw (vec3_t vec) ! 317: { ! 318: float yaw; ! 319: ! 320: if (/* vec[YAW] == 0 && */ vec[PITCH] == 0) ! 321: { ! 322: yaw = 0; ! 323: if (vec[YAW] > 0) ! 324: yaw = 90; ! 325: else if (vec[YAW] < 0) ! 326: yaw = -90; ! 327: } ! 328: else ! 329: { ! 330: yaw = (int) (atan2(vec[YAW], vec[PITCH]) * 180 / M_PI); ! 331: if (yaw < 0) ! 332: yaw += 360; ! 333: } ! 334: ! 335: return yaw; ! 336: } ! 337: ! 338: ! 339: void vectoangles (vec3_t value1, vec3_t angles) ! 340: { ! 341: float forward; ! 342: float yaw, pitch; ! 343: ! 344: if (value1[1] == 0 && value1[0] == 0) ! 345: { ! 346: yaw = 0; ! 347: if (value1[2] > 0) ! 348: pitch = 90; ! 349: else ! 350: pitch = 270; ! 351: } ! 352: else ! 353: { ! 354: if (value1[0]) ! 355: yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI); ! 356: else if (value1[1] > 0) ! 357: yaw = 90; ! 358: else ! 359: yaw = -90; ! 360: if (yaw < 0) ! 361: yaw += 360; ! 362: ! 363: forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]); ! 364: pitch = (int) (atan2(value1[2], forward) * 180 / M_PI); ! 365: if (pitch < 0) ! 366: pitch += 360; ! 367: } ! 368: ! 369: angles[PITCH] = -pitch; ! 370: angles[YAW] = yaw; ! 371: angles[ROLL] = 0; ! 372: } ! 373: ! 374: char *G_CopyString (char *in) ! 375: { ! 376: char *out; ! 377: ! 378: out = gi.TagMalloc (strlen(in)+1, TAG_LEVEL); ! 379: strcpy (out, in); ! 380: return out; ! 381: } ! 382: ! 383: ! 384: void G_InitEdict (edict_t *e) ! 385: { ! 386: e->inuse = true; ! 387: e->classname = "noclass"; ! 388: e->gravity = 1.0; ! 389: e->s.number = e - g_edicts; ! 390: } ! 391: ! 392: /* ! 393: ================= ! 394: G_Spawn ! 395: ! 396: Either finds a free edict, or allocates a new one. ! 397: Try to avoid reusing an entity that was recently freed, because it ! 398: can cause the client to think the entity morphed into something else ! 399: instead of being removed and recreated, which can cause interpolated ! 400: angles and bad trails. ! 401: ================= ! 402: */ ! 403: edict_t *G_Spawn (void) ! 404: { ! 405: int i; ! 406: edict_t *e; ! 407: ! 408: e = &g_edicts[(int)maxclients->value+1]; ! 409: for ( i=maxclients->value+1 ; i<globals.num_edicts ; i++, e++) ! 410: { ! 411: // the first couple seconds of server time can involve a lot of ! 412: // freeing and allocating, so relax the replacement policy ! 413: if (!e->inuse && ( e->freetime < 2 || level.time - e->freetime > 0.5 ) ) ! 414: { ! 415: G_InitEdict (e); ! 416: return e; ! 417: } ! 418: } ! 419: ! 420: if (i == game.maxentities) ! 421: gi.error ("ED_Alloc: no free edicts"); ! 422: ! 423: globals.num_edicts++; ! 424: G_InitEdict (e); ! 425: return e; ! 426: } ! 427: ! 428: /* ! 429: ================= ! 430: G_FreeEdict ! 431: ! 432: Marks the edict as free ! 433: ================= ! 434: */ ! 435: void G_FreeEdict (edict_t *ed) ! 436: { ! 437: gi.unlinkentity (ed); // unlink from world ! 438: ! 439: if ((ed - g_edicts) <= (maxclients->value + BODY_QUEUE_SIZE)) ! 440: { ! 441: // gi.dprintf("tried to free special edict\n"); ! 442: return; ! 443: } ! 444: ! 445: memset (ed, 0, sizeof(*ed)); ! 446: ed->classname = "freed"; ! 447: ed->freetime = level.time; ! 448: ed->inuse = false; ! 449: } ! 450: ! 451: ! 452: /* ! 453: ============ ! 454: G_TouchTriggers ! 455: ! 456: ============ ! 457: */ ! 458: void G_TouchTriggers (edict_t *ent) ! 459: { ! 460: int i, num; ! 461: edict_t *touch[MAX_EDICTS], *hit; ! 462: ! 463: // dead things don't activate triggers! ! 464: if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0)) ! 465: return; ! 466: ! 467: num = gi.BoxEdicts (ent->absmin, ent->absmax, touch ! 468: , MAX_EDICTS, AREA_TRIGGERS); ! 469: ! 470: // be careful, it is possible to have an entity in this ! 471: // list removed before we get to it (killtriggered) ! 472: for (i=0 ; i<num ; i++) ! 473: { ! 474: hit = touch[i]; ! 475: if (!hit->inuse) ! 476: continue; ! 477: if (!hit->touch) ! 478: continue; ! 479: hit->touch (hit, ent, NULL, NULL); ! 480: } ! 481: } ! 482: ! 483: /* ! 484: ============ ! 485: G_TouchSolids ! 486: ! 487: Call after linking a new trigger in during gameplay ! 488: to force all entities it covers to immediately touch it ! 489: ============ ! 490: */ ! 491: void G_TouchSolids (edict_t *ent) ! 492: { ! 493: int i, num; ! 494: edict_t *touch[MAX_EDICTS], *hit; ! 495: ! 496: num = gi.BoxEdicts (ent->absmin, ent->absmax, touch ! 497: , MAX_EDICTS, AREA_SOLID); ! 498: ! 499: // be careful, it is possible to have an entity in this ! 500: // list removed before we get to it (killtriggered) ! 501: for (i=0 ; i<num ; i++) ! 502: { ! 503: hit = touch[i]; ! 504: if (!hit->inuse) ! 505: continue; ! 506: if (ent->touch) ! 507: ent->touch (hit, ent, NULL, NULL); ! 508: if (!ent->inuse) ! 509: break; ! 510: } ! 511: } ! 512: ! 513: ! 514: ! 515: ! 516: /* ! 517: ============================================================================== ! 518: ! 519: Kill box ! 520: ! 521: ============================================================================== ! 522: */ ! 523: ! 524: /* ! 525: ================= ! 526: KillBox ! 527: ! 528: Kills all entities that would touch the proposed new positioning ! 529: of ent. Ent should be unlinked before calling this! ! 530: ================= ! 531: */ ! 532: qboolean KillBox (edict_t *ent) ! 533: { ! 534: trace_t tr; ! 535: ! 536: while (1) ! 537: { ! 538: tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID); ! 539: if (!tr.ent) ! 540: break; ! 541: ! 542: // nail it ! 543: T_Damage (tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG); ! 544: ! 545: // if we didn't kill it, fail ! 546: if (tr.ent->solid) ! 547: return false; ! 548: } ! 549: ! 550: return true; // all clear ! 551: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.