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