|
|
1.1 ! root 1: // g_misc.c ! 2: ! 3: #include "g_local.h" ! 4: ! 5: ! 6: /*QUAKED func_group (0 0 0) ? ! 7: Used to group brushes together just for editor convenience. ! 8: */ ! 9: ! 10: //===================================================== ! 11: ! 12: void Use_Areaportal (edict_t *ent, edict_t *other, edict_t *activator) ! 13: { ! 14: ent->count ^= 1; // toggle state ! 15: // gi.dprintf ("portalstate: %i = %i\n", ent->style, ent->count); ! 16: gi.SetAreaPortalState (ent->style, ent->count); ! 17: } ! 18: ! 19: /*QUAKED func_areaportal (0 0 0) ? ! 20: ! 21: This is a non-visible object that divides the world into ! 22: areas that are seperated when this portal is not activated. ! 23: Usually enclosed in the middle of a door. ! 24: */ ! 25: void SP_func_areaportal (edict_t *ent) ! 26: { ! 27: ent->use = Use_Areaportal; ! 28: ent->count = 0; // always start closed; ! 29: } ! 30: ! 31: //===================================================== ! 32: ! 33: ! 34: /* ! 35: ================= ! 36: Misc functions ! 37: ================= ! 38: */ ! 39: void VelocityForDamage (int damage, vec3_t v) ! 40: { ! 41: v[0] = 100.0 * crandom(); ! 42: v[1] = 100.0 * crandom(); ! 43: v[2] = 200.0 + 100.0 * random(); ! 44: ! 45: if (damage < 50) ! 46: VectorScale (v, 0.7, v); ! 47: else ! 48: VectorScale (v, 1.2, v); ! 49: } ! 50: ! 51: void ClipGibVelocity (edict_t *ent) ! 52: { ! 53: if (ent->velocity[0] < -300) ! 54: ent->velocity[0] = -300; ! 55: else if (ent->velocity[0] > 300) ! 56: ent->velocity[0] = 300; ! 57: if (ent->velocity[1] < -300) ! 58: ent->velocity[1] = -300; ! 59: else if (ent->velocity[1] > 300) ! 60: ent->velocity[1] = 300; ! 61: if (ent->velocity[2] < 200) ! 62: ent->velocity[2] = 200; // always some upwards ! 63: else if (ent->velocity[2] > 500) ! 64: ent->velocity[2] = 500; ! 65: } ! 66: ! 67: ! 68: /* ! 69: ================= ! 70: gibs ! 71: ================= ! 72: */ ! 73: void gib_think (edict_t *self) ! 74: { ! 75: self->s.frame++; ! 76: self->nextthink = level.time + FRAMETIME; ! 77: ! 78: if (self->s.frame == 10) ! 79: { ! 80: self->think = G_FreeEdict; ! 81: self->nextthink = level.time + 8 + random()*10; ! 82: } ! 83: } ! 84: ! 85: void gib_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) ! 86: { ! 87: vec3_t normal_angles, right; ! 88: ! 89: if (!self->groundentity) ! 90: return; ! 91: ! 92: self->touch = NULL; ! 93: ! 94: if (plane) ! 95: { ! 96: gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/fhit3.wav"), 1, ATTN_NORM, 0); ! 97: ! 98: vectoangles (plane->normal, normal_angles); ! 99: AngleVectors (normal_angles, NULL, right, NULL); ! 100: vectoangles (right, self->s.angles); ! 101: ! 102: if (self->s.modelindex == sm_meat_index) ! 103: { ! 104: self->s.frame++; ! 105: self->think = gib_think; ! 106: self->nextthink = level.time + FRAMETIME; ! 107: } ! 108: } ! 109: } ! 110: ! 111: void gib_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) ! 112: { ! 113: G_FreeEdict (self); ! 114: } ! 115: ! 116: void ThrowGib (edict_t *self, char *gibname, int damage, int type) ! 117: { ! 118: edict_t *gib; ! 119: vec3_t vd; ! 120: vec3_t origin; ! 121: vec3_t size; ! 122: float vscale; ! 123: ! 124: gib = G_Spawn(); ! 125: ! 126: VectorScale (self->size, 0.5, size); ! 127: VectorAdd (self->absmin, size, origin); ! 128: gib->s.origin[0] = origin[0] + crandom() * size[0]; ! 129: gib->s.origin[1] = origin[1] + crandom() * size[1]; ! 130: gib->s.origin[2] = origin[2] + crandom() * size[2]; ! 131: ! 132: gi.setmodel (gib, gibname); ! 133: gib->solid = SOLID_NOT; ! 134: gib->s.effects |= EF_GIB; ! 135: gib->flags |= FL_NO_KNOCKBACK; ! 136: gib->takedamage = DAMAGE_YES; ! 137: gib->die = gib_die; ! 138: ! 139: if (type == GIB_ORGANIC) ! 140: { ! 141: gib->movetype = MOVETYPE_TOSS; ! 142: gib->touch = gib_touch; ! 143: vscale = 0.5; ! 144: } ! 145: else ! 146: { ! 147: gib->movetype = MOVETYPE_BOUNCE; ! 148: vscale = 1.0; ! 149: } ! 150: ! 151: VelocityForDamage (damage, vd); ! 152: VectorMA (self->velocity, vscale, vd, gib->velocity); ! 153: ClipGibVelocity (gib); ! 154: gib->avelocity[0] = random()*600; ! 155: gib->avelocity[1] = random()*600; ! 156: gib->avelocity[2] = random()*600; ! 157: ! 158: gib->think = G_FreeEdict; ! 159: gib->nextthink = level.time + 10 + random()*10; ! 160: ! 161: gi.linkentity (gib); ! 162: } ! 163: ! 164: void ThrowHead (edict_t *self, char *gibname, int damage, int type) ! 165: { ! 166: vec3_t vd; ! 167: float vscale; ! 168: ! 169: self->s.skinnum = 0; ! 170: self->s.frame = 0; ! 171: VectorClear (self->mins); ! 172: VectorClear (self->maxs); ! 173: ! 174: self->s.modelindex2 = 0; ! 175: gi.setmodel (self, gibname); ! 176: self->solid = SOLID_NOT; ! 177: self->s.effects |= EF_GIB; ! 178: self->s.effects &= ~EF_FLIES; ! 179: self->s.sound = 0; ! 180: self->flags |= FL_NO_KNOCKBACK; ! 181: self->svflags &= ~SVF_MONSTER; ! 182: self->takedamage = DAMAGE_YES; ! 183: self->die = gib_die; ! 184: ! 185: if (type == GIB_ORGANIC) ! 186: { ! 187: self->movetype = MOVETYPE_TOSS; ! 188: self->touch = gib_touch; ! 189: vscale = 0.5; ! 190: } ! 191: else ! 192: { ! 193: self->movetype = MOVETYPE_BOUNCE; ! 194: vscale = 1.0; ! 195: } ! 196: ! 197: VelocityForDamage (damage, vd); ! 198: VectorMA (self->velocity, vscale, vd, self->velocity); ! 199: ClipGibVelocity (self); ! 200: ! 201: self->avelocity[YAW] = crandom()*600; ! 202: ! 203: self->think = G_FreeEdict; ! 204: self->nextthink = level.time + 10 + random()*10; ! 205: ! 206: gi.linkentity (self); ! 207: } ! 208: ! 209: ! 210: void ThrowClientHead (edict_t *self, int damage) ! 211: { ! 212: vec3_t vd; ! 213: char *gibname; ! 214: ! 215: if (rand()&1) ! 216: { ! 217: gibname = "models/objects/gibs/head2/tris.md2"; ! 218: self->s.skinnum = 1; // second skin is player ! 219: } ! 220: else ! 221: { ! 222: gibname = "models/objects/gibs/skull/tris.md2"; ! 223: self->s.skinnum = 0; ! 224: } ! 225: ! 226: self->s.origin[2] += 32; ! 227: self->s.frame = 0; ! 228: gi.setmodel (self, gibname); ! 229: VectorSet (self->mins, -16, -16, 0); ! 230: VectorSet (self->maxs, 16, 16, 16); ! 231: ! 232: self->takedamage = DAMAGE_NO; ! 233: self->solid = SOLID_NOT; ! 234: self->s.effects = EF_GIB; ! 235: self->s.sound = 0; ! 236: self->flags |= FL_NO_KNOCKBACK; ! 237: ! 238: self->movetype = MOVETYPE_BOUNCE; ! 239: VelocityForDamage (damage, vd); ! 240: VectorAdd (self->velocity, vd, self->velocity); ! 241: ! 242: if (self->client) // bodies in the queue don't have a client anymore ! 243: { ! 244: self->client->anim_priority = ANIM_DEATH; ! 245: self->client->anim_end = self->s.frame; ! 246: } ! 247: ! 248: gi.linkentity (self); ! 249: } ! 250: ! 251: ! 252: /* ! 253: ================= ! 254: debris ! 255: ================= ! 256: */ ! 257: void debris_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) ! 258: { ! 259: G_FreeEdict (self); ! 260: } ! 261: ! 262: void ThrowDebris (edict_t *self, char *modelname, float speed, vec3_t origin) ! 263: { ! 264: edict_t *chunk; ! 265: vec3_t v; ! 266: ! 267: chunk = G_Spawn(); ! 268: VectorCopy (origin, chunk->s.origin); ! 269: gi.setmodel (chunk, modelname); ! 270: v[0] = 100 * crandom(); ! 271: v[1] = 100 * crandom(); ! 272: v[2] = 100 + 100 * crandom(); ! 273: VectorMA (self->velocity, speed, v, chunk->velocity); ! 274: chunk->movetype = MOVETYPE_BOUNCE; ! 275: chunk->solid = SOLID_NOT; ! 276: chunk->avelocity[0] = random()*600; ! 277: chunk->avelocity[1] = random()*600; ! 278: chunk->avelocity[2] = random()*600; ! 279: chunk->think = G_FreeEdict; ! 280: chunk->nextthink = level.time + 5 + random()*5; ! 281: chunk->s.frame = 0; ! 282: chunk->flags = 0; ! 283: chunk->classname = "debris"; ! 284: chunk->takedamage = DAMAGE_YES; ! 285: chunk->die = debris_die; ! 286: gi.linkentity (chunk); ! 287: } ! 288: ! 289: ! 290: void BecomeExplosion1 (edict_t *self) ! 291: { ! 292: gi.WriteByte (svc_temp_entity); ! 293: gi.WriteByte (TE_EXPLOSION1); ! 294: gi.WritePosition (self->s.origin); ! 295: gi.multicast (self->s.origin, MULTICAST_PVS); ! 296: ! 297: G_FreeEdict (self); ! 298: } ! 299: ! 300: ! 301: void BecomeExplosion2 (edict_t *self) ! 302: { ! 303: gi.WriteByte (svc_temp_entity); ! 304: gi.WriteByte (TE_EXPLOSION2); ! 305: gi.WritePosition (self->s.origin); ! 306: gi.multicast (self->s.origin, MULTICAST_PVS); ! 307: ! 308: G_FreeEdict (self); ! 309: } ! 310: ! 311: ! 312: /*QUAKED path_corner (.5 .3 0) (-8 -8 -8) (8 8 8) TELEPORT ! 313: Target: next path corner ! 314: Pathtarget: gets used when an entity that has ! 315: this path_corner targeted touches it ! 316: */ ! 317: ! 318: void path_corner_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) ! 319: { ! 320: vec3_t v; ! 321: edict_t *next; ! 322: ! 323: if (other->movetarget != self) ! 324: return; ! 325: ! 326: if (other->enemy) ! 327: return; ! 328: ! 329: if (self->pathtarget) ! 330: { ! 331: char *savetarget; ! 332: ! 333: savetarget = self->target; ! 334: self->target = self->pathtarget; ! 335: G_UseTargets (self, other); ! 336: self->target = savetarget; ! 337: } ! 338: ! 339: if (self->target) ! 340: next = G_PickTarget(self->target); ! 341: else ! 342: next = NULL; ! 343: ! 344: if ((next) && (next->spawnflags & 1)) ! 345: { ! 346: VectorCopy (next->s.origin, v); ! 347: v[2] += next->mins[2]; ! 348: v[2] -= other->mins[2]; ! 349: VectorCopy (v, other->s.origin); ! 350: next = G_PickTarget(next->target); ! 351: } ! 352: ! 353: other->goalentity = other->movetarget = next; ! 354: ! 355: if (self->wait) ! 356: { ! 357: other->monsterinfo.pausetime = level.time + self->wait; ! 358: other->monsterinfo.stand (other); ! 359: return; ! 360: } ! 361: ! 362: if (!other->movetarget) ! 363: { ! 364: other->monsterinfo.pausetime = level.time + 100000000; ! 365: other->monsterinfo.stand (other); ! 366: } ! 367: else ! 368: { ! 369: VectorSubtract (other->goalentity->s.origin, other->s.origin, v); ! 370: other->ideal_yaw = vectoyaw (v); ! 371: } ! 372: } ! 373: ! 374: void SP_path_corner (edict_t *self) ! 375: { ! 376: if (!self->targetname) ! 377: { ! 378: gi.dprintf ("path_corner with no targetname at %s\n", vtos(self->s.origin)); ! 379: G_FreeEdict (self); ! 380: return; ! 381: } ! 382: ! 383: self->solid = SOLID_TRIGGER; ! 384: self->touch = path_corner_touch; ! 385: VectorSet (self->mins, -8, -8, -8); ! 386: VectorSet (self->maxs, 8, 8, 8); ! 387: self->svflags |= SVF_NOCLIENT; ! 388: gi.linkentity (self); ! 389: } ! 390: ! 391: ! 392: /*QUAKED point_combat (0.5 0.3 0) (-8 -8 -8) (8 8 8) Hold ! 393: Makes this the target of a monster and it will head here ! 394: when first activated before going after the activator. If ! 395: hold is selected, it will stay here. ! 396: */ ! 397: void point_combat_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) ! 398: { ! 399: edict_t *activator; ! 400: ! 401: if (other->movetarget != self) ! 402: return; ! 403: ! 404: if (self->target) ! 405: { ! 406: other->target = self->target; ! 407: other->goalentity = other->movetarget = G_PickTarget(other->target); ! 408: if (!other->goalentity) ! 409: { ! 410: gi.dprintf("%s at %s target %s does not exist\n", self->classname, vtos(self->s.origin), self->target); ! 411: other->movetarget = self; ! 412: } ! 413: self->target = NULL; ! 414: } ! 415: else if ((self->spawnflags & 1) && !(other->flags & (FL_SWIM|FL_FLY))) ! 416: { ! 417: other->monsterinfo.pausetime = level.time + 100000000; ! 418: other->monsterinfo.aiflags |= AI_STAND_GROUND; ! 419: other->monsterinfo.stand (other); ! 420: } ! 421: ! 422: if (other->movetarget == self) ! 423: { ! 424: other->target = NULL; ! 425: other->movetarget = NULL; ! 426: other->goalentity = other->enemy; ! 427: other->monsterinfo.aiflags &= ~AI_COMBAT_POINT; ! 428: } ! 429: ! 430: if (self->pathtarget) ! 431: { ! 432: char *savetarget; ! 433: ! 434: savetarget = self->target; ! 435: self->target = self->pathtarget; ! 436: if (other->enemy && other->enemy->client) ! 437: activator = other->enemy; ! 438: else if (other->oldenemy && other->oldenemy->client) ! 439: activator = other->oldenemy; ! 440: else if (other->activator && other->activator->client) ! 441: activator = other->activator; ! 442: else ! 443: activator = other; ! 444: G_UseTargets (self, activator); ! 445: self->target = savetarget; ! 446: } ! 447: } ! 448: ! 449: void SP_point_combat (edict_t *self) ! 450: { ! 451: if (deathmatch->value) ! 452: { ! 453: G_FreeEdict (self); ! 454: return; ! 455: } ! 456: self->solid = SOLID_TRIGGER; ! 457: self->touch = point_combat_touch; ! 458: VectorSet (self->mins, -8, -8, -16); ! 459: VectorSet (self->maxs, 8, 8, 16); ! 460: self->svflags = SVF_NOCLIENT; ! 461: gi.linkentity (self); ! 462: }; ! 463: ! 464: ! 465: /*QUAKED viewthing (0 .5 .8) (-8 -8 -8) (8 8 8) ! 466: Just for the debugging level. Don't use ! 467: */ ! 468: void TH_viewthing(edict_t *ent) ! 469: { ! 470: ent->s.frame = (ent->s.frame + 1) % 7; ! 471: ent->nextthink = level.time + FRAMETIME; ! 472: } ! 473: ! 474: void SP_viewthing(edict_t *ent) ! 475: { ! 476: gi.dprintf ("viewthing spawned\n"); ! 477: ! 478: ent->movetype = MOVETYPE_NONE; ! 479: ent->solid = SOLID_BBOX; ! 480: ent->s.renderfx = RF_FRAMELERP; ! 481: VectorSet (ent->mins, -16, -16, -24); ! 482: VectorSet (ent->maxs, 16, 16, 32); ! 483: ent->s.modelindex = gi.modelindex ("models/objects/banner/tris.md2"); ! 484: gi.linkentity (ent); ! 485: ent->nextthink = level.time + 0.5; ! 486: ent->think = TH_viewthing; ! 487: return; ! 488: } ! 489: ! 490: ! 491: /*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4) ! 492: Used as a positional target for spotlights, etc. ! 493: */ ! 494: void SP_info_null (edict_t *self) ! 495: { ! 496: G_FreeEdict (self); ! 497: }; ! 498: ! 499: ! 500: /*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4) ! 501: Used as a positional target for lightning. ! 502: */ ! 503: void SP_info_notnull (edict_t *self) ! 504: { ! 505: VectorCopy (self->s.origin, self->absmin); ! 506: VectorCopy (self->s.origin, self->absmax); ! 507: }; ! 508: ! 509: ! 510: /*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) START_OFF ! 511: Non-displayed light. ! 512: Default light value is 300. ! 513: Default style is 0. ! 514: If targeted, will toggle between on and off. ! 515: Default _cone value is 10 (used to set size of light for spotlights) ! 516: */ ! 517: ! 518: #define START_OFF 1 ! 519: ! 520: static void light_use (edict_t *self, edict_t *other, edict_t *activator) ! 521: { ! 522: if (self->spawnflags & START_OFF) ! 523: { ! 524: gi.configstring (CS_LIGHTS+self->style, "m"); ! 525: self->spawnflags &= ~START_OFF; ! 526: } ! 527: else ! 528: { ! 529: gi.configstring (CS_LIGHTS+self->style, "a"); ! 530: self->spawnflags |= START_OFF; ! 531: } ! 532: } ! 533: ! 534: void SP_light (edict_t *self) ! 535: { ! 536: // no targeted lights in deathmatch, because they cause global messages ! 537: if (!self->targetname || deathmatch->value) ! 538: { ! 539: G_FreeEdict (self); ! 540: return; ! 541: } ! 542: ! 543: if (self->style >= 32) ! 544: { ! 545: self->use = light_use; ! 546: if (self->spawnflags & START_OFF) ! 547: gi.configstring (CS_LIGHTS+self->style, "a"); ! 548: else ! 549: gi.configstring (CS_LIGHTS+self->style, "m"); ! 550: } ! 551: } ! 552: ! 553: ! 554: /*QUAKED func_wall (0 .5 .8) ? TRIGGER_SPAWN TOGGLE START_ON ANIMATED ANIMATED_FAST ! 555: This is just a solid wall if not inhibited ! 556: ! 557: TRIGGER_SPAWN the wall will not be present until triggered ! 558: it will then blink in to existance; it will ! 559: kill anything that was in it's way ! 560: ! 561: TOGGLE only valid for TRIGGER_SPAWN walls ! 562: this allows the wall to be turned on and off ! 563: ! 564: START_ON only valid for TRIGGER_SPAWN walls ! 565: the wall will initially be present ! 566: */ ! 567: ! 568: void func_wall_use (edict_t *self, edict_t *other, edict_t *activator) ! 569: { ! 570: if (self->solid == SOLID_NOT) ! 571: { ! 572: self->solid = SOLID_BSP; ! 573: self->svflags &= ~SVF_NOCLIENT; ! 574: KillBox (self); ! 575: } ! 576: else ! 577: { ! 578: self->solid = SOLID_NOT; ! 579: self->svflags |= SVF_NOCLIENT; ! 580: } ! 581: gi.linkentity (self); ! 582: ! 583: if (!(self->spawnflags & 2)) ! 584: self->use = NULL; ! 585: } ! 586: ! 587: void SP_func_wall (edict_t *self) ! 588: { ! 589: self->movetype = MOVETYPE_PUSH; ! 590: gi.setmodel (self, self->model); ! 591: ! 592: if (self->spawnflags & 8) ! 593: self->s.effects |= EF_ANIM_ALL; ! 594: if (self->spawnflags & 16) ! 595: self->s.effects |= EF_ANIM_ALLFAST; ! 596: ! 597: // just a wall ! 598: if ((self->spawnflags & 7) == 0) ! 599: { ! 600: self->solid = SOLID_BSP; ! 601: gi.linkentity (self); ! 602: return; ! 603: } ! 604: ! 605: // it must be TRIGGER_SPAWN ! 606: if (!(self->spawnflags & 1)) ! 607: { ! 608: // gi.dprintf("func_wall missing TRIGGER_SPAWN\n"); ! 609: self->spawnflags |= 1; ! 610: } ! 611: ! 612: // yell if the spawnflags are odd ! 613: if (self->spawnflags & 4) ! 614: { ! 615: if (!(self->spawnflags & 2)) ! 616: { ! 617: gi.dprintf("func_wall START_ON without TOGGLE\n"); ! 618: self->spawnflags |= 2; ! 619: } ! 620: } ! 621: ! 622: self->use = func_wall_use; ! 623: if (self->spawnflags & 4) ! 624: { ! 625: self->solid = SOLID_BSP; ! 626: } ! 627: else ! 628: { ! 629: self->solid = SOLID_NOT; ! 630: self->svflags |= SVF_NOCLIENT; ! 631: } ! 632: gi.linkentity (self); ! 633: } ! 634: ! 635: ! 636: /*QUAKED func_object (0 .5 .8) ? TRIGGER_SPAWN ANIMATED ANIMATED_FAST ! 637: This is solid bmodel that will fall if it's support it removed. ! 638: */ ! 639: ! 640: void func_object_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) ! 641: { ! 642: // only squash thing we fall on top of ! 643: if (!plane) ! 644: return; ! 645: if (plane->normal[2] < 1.0) ! 646: return; ! 647: if (other->takedamage == DAMAGE_NO) ! 648: return; ! 649: T_Damage (other, self, self, vec3_origin, self->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH); ! 650: } ! 651: ! 652: void func_object_release (edict_t *self) ! 653: { ! 654: self->movetype = MOVETYPE_TOSS; ! 655: self->touch = func_object_touch; ! 656: } ! 657: ! 658: void func_object_use (edict_t *self, edict_t *other, edict_t *activator) ! 659: { ! 660: self->solid = SOLID_BSP; ! 661: self->svflags &= ~SVF_NOCLIENT; ! 662: self->use = NULL; ! 663: KillBox (self); ! 664: func_object_release (self); ! 665: } ! 666: ! 667: void SP_func_object (edict_t *self) ! 668: { ! 669: gi.setmodel (self, self->model); ! 670: ! 671: self->mins[0] += 1; ! 672: self->mins[1] += 1; ! 673: self->mins[2] += 1; ! 674: self->maxs[0] -= 1; ! 675: self->maxs[1] -= 1; ! 676: self->maxs[2] -= 1; ! 677: ! 678: if (!self->dmg) ! 679: self->dmg = 100; ! 680: ! 681: if (self->spawnflags == 0) ! 682: { ! 683: self->solid = SOLID_BSP; ! 684: self->movetype = MOVETYPE_PUSH; ! 685: self->think = func_object_release; ! 686: self->nextthink = level.time + 2 * FRAMETIME; ! 687: } ! 688: else ! 689: { ! 690: self->solid = SOLID_NOT; ! 691: self->movetype = MOVETYPE_PUSH; ! 692: self->use = func_object_use; ! 693: self->svflags |= SVF_NOCLIENT; ! 694: } ! 695: ! 696: if (self->spawnflags & 2) ! 697: self->s.effects |= EF_ANIM_ALL; ! 698: if (self->spawnflags & 4) ! 699: self->s.effects |= EF_ANIM_ALLFAST; ! 700: ! 701: self->clipmask = MASK_MONSTERSOLID; ! 702: ! 703: gi.linkentity (self); ! 704: } ! 705: ! 706: ! 707: /*QUAKED func_explosive (0 .5 .8) ? Trigger_Spawn ANIMATED ANIMATED_FAST ! 708: Any brush that you want to explode or break apart. If you want an ! 709: ex0plosion, set dmg and it will do a radius explosion of that amount ! 710: at the center of the bursh. ! 711: ! 712: If targeted it will not be shootable. ! 713: ! 714: health defaults to 100. ! 715: ! 716: mass defaults to 75. This determines how much debris is emitted when ! 717: it explodes. You get one large chunk per 100 of mass (up to 8) and ! 718: one small chunk per 25 of mass (up to 16). So 800 gives the most. ! 719: */ ! 720: void func_explosive_explode (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) ! 721: { ! 722: vec3_t origin; ! 723: vec3_t chunkorigin; ! 724: vec3_t size; ! 725: int count; ! 726: int mass; ! 727: ! 728: // bmodel origins are (0 0 0), we need to adjust that here ! 729: VectorScale (self->size, 0.5, size); ! 730: VectorAdd (self->absmin, size, origin); ! 731: VectorCopy (origin, self->s.origin); ! 732: ! 733: self->takedamage = DAMAGE_NO; ! 734: ! 735: if (self->dmg) ! 736: T_RadiusDamage (self, attacker, self->dmg, NULL, self->dmg+40, MOD_EXPLOSIVE); ! 737: ! 738: VectorSubtract (self->s.origin, inflictor->s.origin, self->velocity); ! 739: VectorNormalize (self->velocity); ! 740: VectorScale (self->velocity, 150, self->velocity); ! 741: ! 742: // start chunks towards the center ! 743: VectorScale (size, 0.5, size); ! 744: ! 745: mass = self->mass; ! 746: if (!mass) ! 747: mass = 75; ! 748: ! 749: // big chunks ! 750: if (mass >= 100) ! 751: { ! 752: count = mass / 100; ! 753: if (count > 8) ! 754: count = 8; ! 755: while(count--) ! 756: { ! 757: chunkorigin[0] = origin[0] + crandom() * size[0]; ! 758: chunkorigin[1] = origin[1] + crandom() * size[1]; ! 759: chunkorigin[2] = origin[2] + crandom() * size[2]; ! 760: ThrowDebris (self, "models/objects/debris1/tris.md2", 1, chunkorigin); ! 761: } ! 762: } ! 763: ! 764: // small chunks ! 765: count = mass / 25; ! 766: if (count > 16) ! 767: count = 16; ! 768: while(count--) ! 769: { ! 770: chunkorigin[0] = origin[0] + crandom() * size[0]; ! 771: chunkorigin[1] = origin[1] + crandom() * size[1]; ! 772: chunkorigin[2] = origin[2] + crandom() * size[2]; ! 773: ThrowDebris (self, "models/objects/debris2/tris.md2", 2, chunkorigin); ! 774: } ! 775: ! 776: G_UseTargets (self, attacker); ! 777: ! 778: if (self->dmg) ! 779: BecomeExplosion1 (self); ! 780: else ! 781: G_FreeEdict (self); ! 782: } ! 783: ! 784: void func_explosive_use(edict_t *self, edict_t *other, edict_t *activator) ! 785: { ! 786: func_explosive_explode (self, self, other, self->health, vec3_origin); ! 787: } ! 788: ! 789: void func_explosive_spawn (edict_t *self, edict_t *other, edict_t *activator) ! 790: { ! 791: self->solid = SOLID_BSP; ! 792: self->svflags &= ~SVF_NOCLIENT; ! 793: self->use = NULL; ! 794: KillBox (self); ! 795: gi.linkentity (self); ! 796: } ! 797: ! 798: void SP_func_explosive (edict_t *self) ! 799: { ! 800: if (deathmatch->value) ! 801: { // auto-remove for deathmatch ! 802: G_FreeEdict (self); ! 803: return; ! 804: } ! 805: ! 806: self->movetype = MOVETYPE_PUSH; ! 807: ! 808: gi.modelindex ("models/objects/debris1/tris.md2"); ! 809: gi.modelindex ("models/objects/debris2/tris.md2"); ! 810: ! 811: gi.setmodel (self, self->model); ! 812: ! 813: if (self->spawnflags & 1) ! 814: { ! 815: self->svflags |= SVF_NOCLIENT; ! 816: self->solid = SOLID_NOT; ! 817: self->use = func_explosive_spawn; ! 818: } ! 819: else ! 820: { ! 821: self->solid = SOLID_BSP; ! 822: if (self->targetname) ! 823: self->use = func_explosive_use; ! 824: } ! 825: ! 826: if (self->spawnflags & 2) ! 827: self->s.effects |= EF_ANIM_ALL; ! 828: if (self->spawnflags & 4) ! 829: self->s.effects |= EF_ANIM_ALLFAST; ! 830: ! 831: if (self->use != func_explosive_use) ! 832: { ! 833: if (!self->health) ! 834: self->health = 100; ! 835: self->die = func_explosive_explode; ! 836: self->takedamage = DAMAGE_YES; ! 837: } ! 838: ! 839: gi.linkentity (self); ! 840: } ! 841: ! 842: ! 843: /*QUAKED misc_explobox (0 .5 .8) (-16 -16 0) (16 16 40) ! 844: Large exploding box. You can override its mass (100), ! 845: health (80), and dmg (150). ! 846: */ ! 847: ! 848: void barrel_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) ! 849: ! 850: { ! 851: float ratio; ! 852: vec3_t v; ! 853: ! 854: if ((!other->groundentity) || (other->groundentity == self)) ! 855: return; ! 856: ! 857: ratio = (float)other->mass / (float)self->mass; ! 858: VectorSubtract (self->s.origin, other->s.origin, v); ! 859: M_walkmove (self, vectoyaw(v), 20 * ratio * FRAMETIME); ! 860: } ! 861: ! 862: void barrel_explode (edict_t *self) ! 863: { ! 864: vec3_t org; ! 865: float spd; ! 866: vec3_t save; ! 867: ! 868: T_RadiusDamage (self, self->activator, self->dmg, NULL, self->dmg+40, MOD_BARREL); ! 869: ! 870: VectorCopy (self->s.origin, save); ! 871: VectorMA (self->absmin, 0.5, self->size, self->s.origin); ! 872: ! 873: // a few big chunks ! 874: spd = 1.5 * (float)self->dmg / 200.0; ! 875: org[0] = self->s.origin[0] + crandom() * self->size[0]; ! 876: org[1] = self->s.origin[1] + crandom() * self->size[1]; ! 877: org[2] = self->s.origin[2] + crandom() * self->size[2]; ! 878: ThrowDebris (self, "models/objects/debris1/tris.md2", spd, org); ! 879: org[0] = self->s.origin[0] + crandom() * self->size[0]; ! 880: org[1] = self->s.origin[1] + crandom() * self->size[1]; ! 881: org[2] = self->s.origin[2] + crandom() * self->size[2]; ! 882: ThrowDebris (self, "models/objects/debris1/tris.md2", spd, org); ! 883: ! 884: // bottom corners ! 885: spd = 1.75 * (float)self->dmg / 200.0; ! 886: VectorCopy (self->absmin, org); ! 887: ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org); ! 888: VectorCopy (self->absmin, org); ! 889: org[0] += self->size[0]; ! 890: ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org); ! 891: VectorCopy (self->absmin, org); ! 892: org[1] += self->size[1]; ! 893: ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org); ! 894: VectorCopy (self->absmin, org); ! 895: org[0] += self->size[0]; ! 896: org[1] += self->size[1]; ! 897: ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org); ! 898: ! 899: // a bunch of little chunks ! 900: spd = 2 * self->dmg / 200; ! 901: org[0] = self->s.origin[0] + crandom() * self->size[0]; ! 902: org[1] = self->s.origin[1] + crandom() * self->size[1]; ! 903: org[2] = self->s.origin[2] + crandom() * self->size[2]; ! 904: ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org); ! 905: org[0] = self->s.origin[0] + crandom() * self->size[0]; ! 906: org[1] = self->s.origin[1] + crandom() * self->size[1]; ! 907: org[2] = self->s.origin[2] + crandom() * self->size[2]; ! 908: ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org); ! 909: org[0] = self->s.origin[0] + crandom() * self->size[0]; ! 910: org[1] = self->s.origin[1] + crandom() * self->size[1]; ! 911: org[2] = self->s.origin[2] + crandom() * self->size[2]; ! 912: ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org); ! 913: org[0] = self->s.origin[0] + crandom() * self->size[0]; ! 914: org[1] = self->s.origin[1] + crandom() * self->size[1]; ! 915: org[2] = self->s.origin[2] + crandom() * self->size[2]; ! 916: ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org); ! 917: org[0] = self->s.origin[0] + crandom() * self->size[0]; ! 918: org[1] = self->s.origin[1] + crandom() * self->size[1]; ! 919: org[2] = self->s.origin[2] + crandom() * self->size[2]; ! 920: ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org); ! 921: org[0] = self->s.origin[0] + crandom() * self->size[0]; ! 922: org[1] = self->s.origin[1] + crandom() * self->size[1]; ! 923: org[2] = self->s.origin[2] + crandom() * self->size[2]; ! 924: ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org); ! 925: org[0] = self->s.origin[0] + crandom() * self->size[0]; ! 926: org[1] = self->s.origin[1] + crandom() * self->size[1]; ! 927: org[2] = self->s.origin[2] + crandom() * self->size[2]; ! 928: ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org); ! 929: org[0] = self->s.origin[0] + crandom() * self->size[0]; ! 930: org[1] = self->s.origin[1] + crandom() * self->size[1]; ! 931: org[2] = self->s.origin[2] + crandom() * self->size[2]; ! 932: ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org); ! 933: ! 934: VectorCopy (save, self->s.origin); ! 935: if (self->groundentity) ! 936: BecomeExplosion2 (self); ! 937: else ! 938: BecomeExplosion1 (self); ! 939: } ! 940: ! 941: void barrel_delay (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) ! 942: { ! 943: self->takedamage = DAMAGE_NO; ! 944: self->nextthink = level.time + 2 * FRAMETIME; ! 945: self->think = barrel_explode; ! 946: self->activator = attacker; ! 947: } ! 948: ! 949: void SP_misc_explobox (edict_t *self) ! 950: { ! 951: if (deathmatch->value) ! 952: { // auto-remove for deathmatch ! 953: G_FreeEdict (self); ! 954: return; ! 955: } ! 956: ! 957: gi.modelindex ("models/objects/debris1/tris.md2"); ! 958: gi.modelindex ("models/objects/debris2/tris.md2"); ! 959: gi.modelindex ("models/objects/debris3/tris.md2"); ! 960: ! 961: self->solid = SOLID_BBOX; ! 962: self->movetype = MOVETYPE_STEP; ! 963: ! 964: self->model = "models/objects/barrels/tris.md2"; ! 965: self->s.modelindex = gi.modelindex (self->model); ! 966: VectorSet (self->mins, -16, -16, 0); ! 967: VectorSet (self->maxs, 16, 16, 40); ! 968: ! 969: if (!self->mass) ! 970: self->mass = 400; ! 971: if (!self->health) ! 972: self->health = 10; ! 973: if (!self->dmg) ! 974: self->dmg = 150; ! 975: ! 976: self->die = barrel_delay; ! 977: self->takedamage = DAMAGE_YES; ! 978: self->monsterinfo.aiflags = AI_NOSTEP; ! 979: ! 980: self->touch = barrel_touch; ! 981: ! 982: self->think = M_droptofloor; ! 983: self->nextthink = level.time + 2 * FRAMETIME; ! 984: ! 985: gi.linkentity (self); ! 986: } ! 987: ! 988: ! 989: // ! 990: // miscellaneous specialty items ! 991: // ! 992: ! 993: /*QUAKED misc_blackhole (1 .5 0) (-8 -8 -8) (8 8 8) ! 994: */ ! 995: ! 996: void misc_blackhole_use (edict_t *ent, edict_t *other, edict_t *activator) ! 997: { ! 998: /* ! 999: gi.WriteByte (svc_temp_entity); ! 1000: gi.WriteByte (TE_BOSSTPORT); ! 1001: gi.WritePosition (ent->s.origin); ! 1002: gi.multicast (ent->s.origin, MULTICAST_PVS); ! 1003: */ ! 1004: G_FreeEdict (ent); ! 1005: } ! 1006: ! 1007: void misc_blackhole_think (edict_t *self) ! 1008: { ! 1009: if (++self->s.frame < 19) ! 1010: self->nextthink = level.time + FRAMETIME; ! 1011: else ! 1012: { ! 1013: self->s.frame = 0; ! 1014: self->nextthink = level.time + FRAMETIME; ! 1015: } ! 1016: } ! 1017: ! 1018: void SP_misc_blackhole (edict_t *ent) ! 1019: { ! 1020: ent->movetype = MOVETYPE_NONE; ! 1021: ent->solid = SOLID_NOT; ! 1022: VectorSet (ent->mins, -64, -64, 0); ! 1023: VectorSet (ent->maxs, 64, 64, 8); ! 1024: ent->s.modelindex = gi.modelindex ("models/objects/black/tris.md2"); ! 1025: ent->s.renderfx = RF_TRANSLUCENT; ! 1026: ent->use = misc_blackhole_use; ! 1027: ent->think = misc_blackhole_think; ! 1028: ent->nextthink = level.time + 2 * FRAMETIME; ! 1029: gi.linkentity (ent); ! 1030: } ! 1031: ! 1032: /*QUAKED misc_eastertank (1 .5 0) (-32 -32 -16) (32 32 32) ! 1033: */ ! 1034: ! 1035: void misc_eastertank_think (edict_t *self) ! 1036: { ! 1037: if (++self->s.frame < 293) ! 1038: self->nextthink = level.time + FRAMETIME; ! 1039: else ! 1040: { ! 1041: self->s.frame = 254; ! 1042: self->nextthink = level.time + FRAMETIME; ! 1043: } ! 1044: } ! 1045: ! 1046: void SP_misc_eastertank (edict_t *ent) ! 1047: { ! 1048: ent->movetype = MOVETYPE_NONE; ! 1049: ent->solid = SOLID_BBOX; ! 1050: VectorSet (ent->mins, -32, -32, -16); ! 1051: VectorSet (ent->maxs, 32, 32, 32); ! 1052: ent->s.modelindex = gi.modelindex ("models/monsters/tank/tris.md2"); ! 1053: ent->s.frame = 254; ! 1054: ent->think = misc_eastertank_think; ! 1055: ent->nextthink = level.time + 2 * FRAMETIME; ! 1056: gi.linkentity (ent); ! 1057: } ! 1058: ! 1059: /*QUAKED misc_easterchick (1 .5 0) (-32 -32 0) (32 32 32) ! 1060: */ ! 1061: ! 1062: ! 1063: void misc_easterchick_think (edict_t *self) ! 1064: { ! 1065: if (++self->s.frame < 247) ! 1066: self->nextthink = level.time + FRAMETIME; ! 1067: else ! 1068: { ! 1069: self->s.frame = 208; ! 1070: self->nextthink = level.time + FRAMETIME; ! 1071: } ! 1072: } ! 1073: ! 1074: void SP_misc_easterchick (edict_t *ent) ! 1075: { ! 1076: ent->movetype = MOVETYPE_NONE; ! 1077: ent->solid = SOLID_BBOX; ! 1078: VectorSet (ent->mins, -32, -32, 0); ! 1079: VectorSet (ent->maxs, 32, 32, 32); ! 1080: ent->s.modelindex = gi.modelindex ("models/monsters/bitch/tris.md2"); ! 1081: ent->s.frame = 208; ! 1082: ent->think = misc_easterchick_think; ! 1083: ent->nextthink = level.time + 2 * FRAMETIME; ! 1084: gi.linkentity (ent); ! 1085: } ! 1086: ! 1087: /*QUAKED misc_easterchick2 (1 .5 0) (-32 -32 0) (32 32 32) ! 1088: */ ! 1089: ! 1090: ! 1091: void misc_easterchick2_think (edict_t *self) ! 1092: { ! 1093: if (++self->s.frame < 287) ! 1094: self->nextthink = level.time + FRAMETIME; ! 1095: else ! 1096: { ! 1097: self->s.frame = 248; ! 1098: self->nextthink = level.time + FRAMETIME; ! 1099: } ! 1100: } ! 1101: ! 1102: void SP_misc_easterchick2 (edict_t *ent) ! 1103: { ! 1104: ent->movetype = MOVETYPE_NONE; ! 1105: ent->solid = SOLID_BBOX; ! 1106: VectorSet (ent->mins, -32, -32, 0); ! 1107: VectorSet (ent->maxs, 32, 32, 32); ! 1108: ent->s.modelindex = gi.modelindex ("models/monsters/bitch/tris.md2"); ! 1109: ent->s.frame = 248; ! 1110: ent->think = misc_easterchick2_think; ! 1111: ent->nextthink = level.time + 2 * FRAMETIME; ! 1112: gi.linkentity (ent); ! 1113: } ! 1114: ! 1115: ! 1116: /*QUAKED monster_commander_body (1 .5 0) (-32 -32 0) (32 32 48) ! 1117: Not really a monster, this is the Tank Commander's decapitated body. ! 1118: There should be a item_commander_head that has this as it's target. ! 1119: */ ! 1120: ! 1121: void commander_body_think (edict_t *self) ! 1122: { ! 1123: if (++self->s.frame < 24) ! 1124: self->nextthink = level.time + FRAMETIME; ! 1125: else ! 1126: self->nextthink = 0; ! 1127: ! 1128: if (self->s.frame == 22) ! 1129: gi.sound (self, CHAN_BODY, gi.soundindex ("tank/thud.wav"), 1, ATTN_NORM, 0); ! 1130: } ! 1131: ! 1132: void commander_body_use (edict_t *self, edict_t *other, edict_t *activator) ! 1133: { ! 1134: self->think = commander_body_think; ! 1135: self->nextthink = level.time + FRAMETIME; ! 1136: gi.sound (self, CHAN_BODY, gi.soundindex ("tank/pain.wav"), 1, ATTN_NORM, 0); ! 1137: } ! 1138: ! 1139: void commander_body_drop (edict_t *self) ! 1140: { ! 1141: self->movetype = MOVETYPE_TOSS; ! 1142: self->s.origin[2] += 2; ! 1143: } ! 1144: ! 1145: void SP_monster_commander_body (edict_t *self) ! 1146: { ! 1147: self->movetype = MOVETYPE_NONE; ! 1148: self->solid = SOLID_BBOX; ! 1149: self->model = "models/monsters/commandr/tris.md2"; ! 1150: self->s.modelindex = gi.modelindex (self->model); ! 1151: VectorSet (self->mins, -32, -32, 0); ! 1152: VectorSet (self->maxs, 32, 32, 48); ! 1153: self->use = commander_body_use; ! 1154: self->takedamage = DAMAGE_YES; ! 1155: self->flags = FL_GODMODE; ! 1156: self->s.renderfx |= RF_FRAMELERP; ! 1157: gi.linkentity (self); ! 1158: ! 1159: gi.soundindex ("tank/thud.wav"); ! 1160: gi.soundindex ("tank/pain.wav"); ! 1161: ! 1162: self->think = commander_body_drop; ! 1163: self->nextthink = level.time + 5 * FRAMETIME; ! 1164: } ! 1165: ! 1166: ! 1167: /*QUAKED misc_banner (1 .5 0) (-4 -4 -4) (4 4 4) ! 1168: The origin is the bottom of the banner. ! 1169: The banner is 128 tall. ! 1170: */ ! 1171: void misc_banner_think (edict_t *ent) ! 1172: { ! 1173: ent->s.frame = (ent->s.frame + 1) % 16; ! 1174: ent->nextthink = level.time + FRAMETIME; ! 1175: } ! 1176: ! 1177: void SP_misc_banner (edict_t *ent) ! 1178: { ! 1179: ent->movetype = MOVETYPE_NONE; ! 1180: ent->solid = SOLID_NOT; ! 1181: ent->s.modelindex = gi.modelindex ("models/objects/banner/tris.md2"); ! 1182: ent->s.frame = rand() % 16; ! 1183: gi.linkentity (ent); ! 1184: ! 1185: ent->think = misc_banner_think; ! 1186: ent->nextthink = level.time + FRAMETIME; ! 1187: } ! 1188: ! 1189: /*QUAKED misc_deadsoldier (1 .5 0) (-16 -16 0) (16 16 16) ON_BACK ON_STOMACH BACK_DECAP FETAL_POS SIT_DECAP IMPALED ! 1190: This is the dead player model. Comes in 6 exciting different poses! ! 1191: */ ! 1192: void misc_deadsoldier_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) ! 1193: { ! 1194: int n; ! 1195: ! 1196: if (self->health > -80) ! 1197: return; ! 1198: ! 1199: gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0); ! 1200: for (n= 0; n < 4; n++) ! 1201: ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); ! 1202: ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC); ! 1203: } ! 1204: ! 1205: void SP_misc_deadsoldier (edict_t *ent) ! 1206: { ! 1207: if (deathmatch->value) ! 1208: { // auto-remove for deathmatch ! 1209: G_FreeEdict (ent); ! 1210: return; ! 1211: } ! 1212: ! 1213: ent->movetype = MOVETYPE_NONE; ! 1214: ent->solid = SOLID_BBOX; ! 1215: ent->s.modelindex=gi.modelindex ("models/deadbods/dude/tris.md2"); ! 1216: ! 1217: // Defaults to frame 0 ! 1218: if (ent->spawnflags & 2) ! 1219: ent->s.frame = 1; ! 1220: else if (ent->spawnflags & 4) ! 1221: ent->s.frame = 2; ! 1222: else if (ent->spawnflags & 8) ! 1223: ent->s.frame = 3; ! 1224: else if (ent->spawnflags & 16) ! 1225: ent->s.frame = 4; ! 1226: else if (ent->spawnflags & 32) ! 1227: ent->s.frame = 5; ! 1228: else ! 1229: ent->s.frame = 0; ! 1230: ! 1231: VectorSet (ent->mins, -16, -16, 0); ! 1232: VectorSet (ent->maxs, 16, 16, 16); ! 1233: ent->deadflag = DEAD_DEAD; ! 1234: ent->takedamage = DAMAGE_YES; ! 1235: ent->svflags |= SVF_MONSTER|SVF_DEADMONSTER; ! 1236: ent->die = misc_deadsoldier_die; ! 1237: ent->monsterinfo.aiflags |= AI_GOOD_GUY; ! 1238: ! 1239: gi.linkentity (ent); ! 1240: } ! 1241: ! 1242: /*QUAKED misc_viper (1 .5 0) (-16 -16 0) (16 16 32) ! 1243: This is the Viper for the flyby bombing. ! 1244: It is trigger_spawned, so you must have something use it for it to show up. ! 1245: There must be a path for it to follow once it is activated. ! 1246: ! 1247: "speed" How fast the Viper should fly ! 1248: */ ! 1249: ! 1250: extern void train_use (edict_t *self, edict_t *other, edict_t *activator); ! 1251: extern void func_train_find (edict_t *self); ! 1252: ! 1253: void misc_viper_use (edict_t *self, edict_t *other, edict_t *activator) ! 1254: { ! 1255: self->svflags &= ~SVF_NOCLIENT; ! 1256: self->use = train_use; ! 1257: train_use (self, other, activator); ! 1258: } ! 1259: ! 1260: void SP_misc_viper (edict_t *ent) ! 1261: { ! 1262: if (!ent->target) ! 1263: { ! 1264: gi.dprintf ("misc_viper without a target at %s\n", vtos(ent->absmin)); ! 1265: G_FreeEdict (ent); ! 1266: return; ! 1267: } ! 1268: ! 1269: if (!ent->speed) ! 1270: ent->speed = 300; ! 1271: ! 1272: ent->movetype = MOVETYPE_PUSH; ! 1273: ent->solid = SOLID_NOT; ! 1274: ent->s.modelindex = gi.modelindex ("models/ships/viper/tris.md2"); ! 1275: VectorSet (ent->mins, -16, -16, 0); ! 1276: VectorSet (ent->maxs, 16, 16, 32); ! 1277: ! 1278: ent->think = func_train_find; ! 1279: ent->nextthink = level.time + FRAMETIME; ! 1280: ent->use = misc_viper_use; ! 1281: ent->svflags |= SVF_NOCLIENT; ! 1282: ent->moveinfo.accel = ent->moveinfo.decel = ent->moveinfo.speed = ent->speed; ! 1283: ! 1284: gi.linkentity (ent); ! 1285: } ! 1286: ! 1287: ! 1288: /*QUAKED misc_bigviper (1 .5 0) (-176 -120 -24) (176 120 72) ! 1289: This is a large stationary viper as seen in Paul's intro ! 1290: */ ! 1291: void SP_misc_bigviper (edict_t *ent) ! 1292: { ! 1293: ent->movetype = MOVETYPE_NONE; ! 1294: ent->solid = SOLID_BBOX; ! 1295: VectorSet (ent->mins, -176, -120, -24); ! 1296: VectorSet (ent->maxs, 176, 120, 72); ! 1297: ent->s.modelindex = gi.modelindex ("models/ships/bigviper/tris.md2"); ! 1298: gi.linkentity (ent); ! 1299: } ! 1300: ! 1301: ! 1302: /*QUAKED misc_viper_bomb (1 0 0) (-8 -8 -8) (8 8 8) ! 1303: "dmg" how much boom should the bomb make? ! 1304: */ ! 1305: void misc_viper_bomb_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) ! 1306: { ! 1307: G_UseTargets (self, self->activator); ! 1308: ! 1309: self->s.origin[2] = self->absmin[2] + 1; ! 1310: T_RadiusDamage (self, self, self->dmg, NULL, self->dmg+40, MOD_BOMB); ! 1311: BecomeExplosion2 (self); ! 1312: } ! 1313: ! 1314: void misc_viper_bomb_prethink (edict_t *self) ! 1315: { ! 1316: vec3_t v; ! 1317: float diff; ! 1318: ! 1319: self->groundentity = NULL; ! 1320: ! 1321: diff = self->timestamp - level.time; ! 1322: if (diff < -1.0) ! 1323: diff = -1.0; ! 1324: ! 1325: VectorScale (self->moveinfo.dir, 1.0 + diff, v); ! 1326: v[2] = diff; ! 1327: ! 1328: diff = self->s.angles[2]; ! 1329: vectoangles (v, self->s.angles); ! 1330: self->s.angles[2] = diff + 10; ! 1331: } ! 1332: ! 1333: void misc_viper_bomb_use (edict_t *self, edict_t *other, edict_t *activator) ! 1334: { ! 1335: edict_t *viper; ! 1336: ! 1337: self->solid = SOLID_BBOX; ! 1338: self->svflags &= ~SVF_NOCLIENT; ! 1339: self->s.effects |= EF_ROCKET; ! 1340: self->use = NULL; ! 1341: self->movetype = MOVETYPE_TOSS; ! 1342: self->prethink = misc_viper_bomb_prethink; ! 1343: self->touch = misc_viper_bomb_touch; ! 1344: self->activator = activator; ! 1345: ! 1346: viper = G_Find (NULL, FOFS(classname), "misc_viper"); ! 1347: VectorScale (viper->moveinfo.dir, viper->moveinfo.speed, self->velocity); ! 1348: ! 1349: self->timestamp = level.time; ! 1350: VectorCopy (viper->moveinfo.dir, self->moveinfo.dir); ! 1351: } ! 1352: ! 1353: void SP_misc_viper_bomb (edict_t *self) ! 1354: { ! 1355: self->movetype = MOVETYPE_NONE; ! 1356: self->solid = SOLID_NOT; ! 1357: VectorSet (self->mins, -8, -8, -8); ! 1358: VectorSet (self->maxs, 8, 8, 8); ! 1359: ! 1360: self->s.modelindex = gi.modelindex ("models/objects/bomb/tris.md2"); ! 1361: ! 1362: if (!self->dmg) ! 1363: self->dmg = 1000; ! 1364: ! 1365: self->use = misc_viper_bomb_use; ! 1366: self->svflags |= SVF_NOCLIENT; ! 1367: ! 1368: gi.linkentity (self); ! 1369: } ! 1370: ! 1371: ! 1372: /*QUAKED misc_strogg_ship (1 .5 0) (-16 -16 0) (16 16 32) ! 1373: This is a Storgg ship for the flybys. ! 1374: It is trigger_spawned, so you must have something use it for it to show up. ! 1375: There must be a path for it to follow once it is activated. ! 1376: ! 1377: "speed" How fast it should fly ! 1378: */ ! 1379: ! 1380: extern void train_use (edict_t *self, edict_t *other, edict_t *activator); ! 1381: extern void func_train_find (edict_t *self); ! 1382: ! 1383: void misc_strogg_ship_use (edict_t *self, edict_t *other, edict_t *activator) ! 1384: { ! 1385: self->svflags &= ~SVF_NOCLIENT; ! 1386: self->use = train_use; ! 1387: train_use (self, other, activator); ! 1388: } ! 1389: ! 1390: void SP_misc_strogg_ship (edict_t *ent) ! 1391: { ! 1392: if (!ent->target) ! 1393: { ! 1394: gi.dprintf ("%s without a target at %s\n", ent->classname, vtos(ent->absmin)); ! 1395: G_FreeEdict (ent); ! 1396: return; ! 1397: } ! 1398: ! 1399: if (!ent->speed) ! 1400: ent->speed = 300; ! 1401: ! 1402: ent->movetype = MOVETYPE_PUSH; ! 1403: ent->solid = SOLID_NOT; ! 1404: ent->s.modelindex = gi.modelindex ("models/ships/strogg1/tris.md2"); ! 1405: VectorSet (ent->mins, -16, -16, 0); ! 1406: VectorSet (ent->maxs, 16, 16, 32); ! 1407: ! 1408: ent->think = func_train_find; ! 1409: ent->nextthink = level.time + FRAMETIME; ! 1410: ent->use = misc_strogg_ship_use; ! 1411: ent->svflags |= SVF_NOCLIENT; ! 1412: ent->moveinfo.accel = ent->moveinfo.decel = ent->moveinfo.speed = ent->speed; ! 1413: ! 1414: gi.linkentity (ent); ! 1415: } ! 1416: ! 1417: ! 1418: /*QUAKED misc_satellite_dish (1 .5 0) (-64 -64 0) (64 64 128) ! 1419: */ ! 1420: void misc_satellite_dish_think (edict_t *self) ! 1421: { ! 1422: self->s.frame++; ! 1423: if (self->s.frame < 38) ! 1424: self->nextthink = level.time + FRAMETIME; ! 1425: } ! 1426: ! 1427: void misc_satellite_dish_use (edict_t *self, edict_t *other, edict_t *activator) ! 1428: { ! 1429: self->s.frame = 0; ! 1430: self->think = misc_satellite_dish_think; ! 1431: self->nextthink = level.time + FRAMETIME; ! 1432: } ! 1433: ! 1434: void SP_misc_satellite_dish (edict_t *ent) ! 1435: { ! 1436: ent->movetype = MOVETYPE_NONE; ! 1437: ent->solid = SOLID_BBOX; ! 1438: VectorSet (ent->mins, -64, -64, 0); ! 1439: VectorSet (ent->maxs, 64, 64, 128); ! 1440: ent->s.modelindex = gi.modelindex ("models/objects/satellite/tris.md2"); ! 1441: ent->use = misc_satellite_dish_use; ! 1442: gi.linkentity (ent); ! 1443: } ! 1444: ! 1445: ! 1446: /*QUAKED light_mine1 (0 1 0) (-2 -2 -12) (2 2 12) ! 1447: */ ! 1448: void SP_light_mine1 (edict_t *ent) ! 1449: { ! 1450: ent->movetype = MOVETYPE_NONE; ! 1451: ent->solid = SOLID_BBOX; ! 1452: ent->s.modelindex = gi.modelindex ("models/objects/minelite/light1/tris.md2"); ! 1453: gi.linkentity (ent); ! 1454: } ! 1455: ! 1456: ! 1457: /*QUAKED light_mine2 (0 1 0) (-2 -2 -12) (2 2 12) ! 1458: */ ! 1459: void SP_light_mine2 (edict_t *ent) ! 1460: { ! 1461: ent->movetype = MOVETYPE_NONE; ! 1462: ent->solid = SOLID_BBOX; ! 1463: ent->s.modelindex = gi.modelindex ("models/objects/minelite/light2/tris.md2"); ! 1464: gi.linkentity (ent); ! 1465: } ! 1466: ! 1467: ! 1468: /*QUAKED misc_gib_arm (1 0 0) (-8 -8 -8) (8 8 8) ! 1469: Intended for use with the target_spawner ! 1470: */ ! 1471: void SP_misc_gib_arm (edict_t *ent) ! 1472: { ! 1473: gi.setmodel (ent, "models/objects/gibs/arm/tris.md2"); ! 1474: ent->solid = SOLID_NOT; ! 1475: ent->s.effects |= EF_GIB; ! 1476: ent->takedamage = DAMAGE_YES; ! 1477: ent->die = gib_die; ! 1478: ent->movetype = MOVETYPE_TOSS; ! 1479: ent->svflags |= SVF_MONSTER; ! 1480: ent->deadflag = DEAD_DEAD; ! 1481: ent->avelocity[0] = random()*200; ! 1482: ent->avelocity[1] = random()*200; ! 1483: ent->avelocity[2] = random()*200; ! 1484: ent->think = G_FreeEdict; ! 1485: ent->nextthink = level.time + 30; ! 1486: gi.linkentity (ent); ! 1487: } ! 1488: ! 1489: /*QUAKED misc_gib_leg (1 0 0) (-8 -8 -8) (8 8 8) ! 1490: Intended for use with the target_spawner ! 1491: */ ! 1492: void SP_misc_gib_leg (edict_t *ent) ! 1493: { ! 1494: gi.setmodel (ent, "models/objects/gibs/leg/tris.md2"); ! 1495: ent->solid = SOLID_NOT; ! 1496: ent->s.effects |= EF_GIB; ! 1497: ent->takedamage = DAMAGE_YES; ! 1498: ent->die = gib_die; ! 1499: ent->movetype = MOVETYPE_TOSS; ! 1500: ent->svflags |= SVF_MONSTER; ! 1501: ent->deadflag = DEAD_DEAD; ! 1502: ent->avelocity[0] = random()*200; ! 1503: ent->avelocity[1] = random()*200; ! 1504: ent->avelocity[2] = random()*200; ! 1505: ent->think = G_FreeEdict; ! 1506: ent->nextthink = level.time + 30; ! 1507: gi.linkentity (ent); ! 1508: } ! 1509: ! 1510: /*QUAKED misc_gib_head (1 0 0) (-8 -8 -8) (8 8 8) ! 1511: Intended for use with the target_spawner ! 1512: */ ! 1513: void SP_misc_gib_head (edict_t *ent) ! 1514: { ! 1515: gi.setmodel (ent, "models/objects/gibs/head/tris.md2"); ! 1516: ent->solid = SOLID_NOT; ! 1517: ent->s.effects |= EF_GIB; ! 1518: ent->takedamage = DAMAGE_YES; ! 1519: ent->die = gib_die; ! 1520: ent->movetype = MOVETYPE_TOSS; ! 1521: ent->svflags |= SVF_MONSTER; ! 1522: ent->deadflag = DEAD_DEAD; ! 1523: ent->avelocity[0] = random()*200; ! 1524: ent->avelocity[1] = random()*200; ! 1525: ent->avelocity[2] = random()*200; ! 1526: ent->think = G_FreeEdict; ! 1527: ent->nextthink = level.time + 30; ! 1528: gi.linkentity (ent); ! 1529: } ! 1530: ! 1531: //===================================================== ! 1532: ! 1533: /*QUAKED target_character (0 0 1) ? ! 1534: used with target_string (must be on same "team") ! 1535: "count" is position in the string (starts at 1) ! 1536: */ ! 1537: ! 1538: void SP_target_character (edict_t *self) ! 1539: { ! 1540: self->movetype = MOVETYPE_PUSH; ! 1541: gi.setmodel (self, self->model); ! 1542: self->solid = SOLID_BSP; ! 1543: self->s.frame = 12; ! 1544: gi.linkentity (self); ! 1545: return; ! 1546: } ! 1547: ! 1548: ! 1549: /*QUAKED target_string (0 0 1) (-8 -8 -8) (8 8 8) ! 1550: */ ! 1551: ! 1552: void target_string_use (edict_t *self, edict_t *other, edict_t *activator) ! 1553: { ! 1554: edict_t *e; ! 1555: int n, l; ! 1556: char c; ! 1557: ! 1558: l = strlen(self->message); ! 1559: for (e = self->teammaster; e; e = e->teamchain) ! 1560: { ! 1561: if (!e->count) ! 1562: continue; ! 1563: n = e->count - 1; ! 1564: if (n > l) ! 1565: { ! 1566: e->s.frame = 12; ! 1567: continue; ! 1568: } ! 1569: ! 1570: c = self->message[n]; ! 1571: if (c >= '0' && c <= '9') ! 1572: e->s.frame = c - '0'; ! 1573: else if (c == '-') ! 1574: e->s.frame = 10; ! 1575: else if (c == ':') ! 1576: e->s.frame = 11; ! 1577: else ! 1578: e->s.frame = 12; ! 1579: } ! 1580: } ! 1581: ! 1582: void SP_target_string (edict_t *self) ! 1583: { ! 1584: if (!self->message) ! 1585: self->message = ""; ! 1586: self->use = target_string_use; ! 1587: } ! 1588: ! 1589: ! 1590: /*QUAKED func_clock (0 0 1) (-8 -8 -8) (8 8 8) TIMER_UP TIMER_DOWN START_OFF MULTI_USE ! 1591: target a target_string with this ! 1592: ! 1593: The default is to be a time of day clock ! 1594: ! 1595: TIMER_UP and TIMER_DOWN run for "count" seconds and the fire "pathtarget" ! 1596: If START_OFF, this entity must be used before it starts ! 1597: ! 1598: "style" 0 "xx" ! 1599: 1 "xx:xx" ! 1600: 2 "xx:xx:xx" ! 1601: */ ! 1602: ! 1603: #define CLOCK_MESSAGE_SIZE 16 ! 1604: ! 1605: // don't let field width of any clock messages change, or it ! 1606: // could cause an overwrite after a game load ! 1607: ! 1608: static void func_clock_reset (edict_t *self) ! 1609: { ! 1610: self->activator = NULL; ! 1611: if (self->spawnflags & 1) ! 1612: { ! 1613: self->health = 0; ! 1614: self->wait = self->count; ! 1615: } ! 1616: else if (self->spawnflags & 2) ! 1617: { ! 1618: self->health = self->count; ! 1619: self->wait = 0; ! 1620: } ! 1621: } ! 1622: ! 1623: static void func_clock_format_countdown (edict_t *self) ! 1624: { ! 1625: if (self->style == 0) ! 1626: { ! 1627: Com_sprintf (self->message, CLOCK_MESSAGE_SIZE, "%2i", self->health); ! 1628: return; ! 1629: } ! 1630: ! 1631: if (self->style == 1) ! 1632: { ! 1633: Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i", self->health / 60, self->health % 60); ! 1634: if (self->message[3] == ' ') ! 1635: self->message[3] = '0'; ! 1636: return; ! 1637: } ! 1638: ! 1639: if (self->style == 2) ! 1640: { ! 1641: Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", self->health / 3600, (self->health - (self->health / 3600) * 3600) / 60, self->health % 60); ! 1642: if (self->message[3] == ' ') ! 1643: self->message[3] = '0'; ! 1644: if (self->message[6] == ' ') ! 1645: self->message[6] = '0'; ! 1646: return; ! 1647: } ! 1648: } ! 1649: ! 1650: void func_clock_think (edict_t *self) ! 1651: { ! 1652: if (!self->enemy) ! 1653: { ! 1654: self->enemy = G_Find (NULL, FOFS(targetname), self->target); ! 1655: if (!self->enemy) ! 1656: return; ! 1657: } ! 1658: ! 1659: if (self->spawnflags & 1) ! 1660: { ! 1661: func_clock_format_countdown (self); ! 1662: self->health++; ! 1663: } ! 1664: else if (self->spawnflags & 2) ! 1665: { ! 1666: func_clock_format_countdown (self); ! 1667: self->health--; ! 1668: } ! 1669: else ! 1670: { ! 1671: struct tm *ltime; ! 1672: time_t gmtime; ! 1673: ! 1674: time(&gmtime); ! 1675: ltime = localtime(&gmtime); ! 1676: Com_sprintf (self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", ltime->tm_hour, ltime->tm_min, ltime->tm_sec); ! 1677: if (self->message[3] == ' ') ! 1678: self->message[3] = '0'; ! 1679: if (self->message[6] == ' ') ! 1680: self->message[6] = '0'; ! 1681: } ! 1682: ! 1683: self->enemy->message = self->message; ! 1684: self->enemy->use (self->enemy, self, self); ! 1685: ! 1686: if (((self->spawnflags & 1) && (self->health > self->wait)) || ! 1687: ((self->spawnflags & 2) && (self->health < self->wait))) ! 1688: { ! 1689: if (self->pathtarget) ! 1690: { ! 1691: char *savetarget; ! 1692: char *savemessage; ! 1693: ! 1694: savetarget = self->target; ! 1695: savemessage = self->message; ! 1696: self->target = self->pathtarget; ! 1697: self->message = NULL; ! 1698: G_UseTargets (self, self->activator); ! 1699: self->target = savetarget; ! 1700: self->message = savemessage; ! 1701: } ! 1702: ! 1703: if (!(self->spawnflags & 8)) ! 1704: return; ! 1705: ! 1706: func_clock_reset (self); ! 1707: ! 1708: if (self->spawnflags & 4) ! 1709: return; ! 1710: } ! 1711: ! 1712: self->nextthink = level.time + 1; ! 1713: } ! 1714: ! 1715: void func_clock_use (edict_t *self, edict_t *other, edict_t *activator) ! 1716: { ! 1717: if (!(self->spawnflags & 8)) ! 1718: self->use = NULL; ! 1719: if (self->activator) ! 1720: return; ! 1721: self->activator = activator; ! 1722: self->think (self); ! 1723: } ! 1724: ! 1725: void SP_func_clock (edict_t *self) ! 1726: { ! 1727: if (!self->target) ! 1728: { ! 1729: gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin)); ! 1730: G_FreeEdict (self); ! 1731: return; ! 1732: } ! 1733: ! 1734: if ((self->spawnflags & 2) && (!self->count)) ! 1735: { ! 1736: gi.dprintf("%s with no count at %s\n", self->classname, vtos(self->s.origin)); ! 1737: G_FreeEdict (self); ! 1738: return; ! 1739: } ! 1740: ! 1741: if ((self->spawnflags & 1) && (!self->count)) ! 1742: self->count = 60*60;; ! 1743: ! 1744: func_clock_reset (self); ! 1745: ! 1746: self->message = gi.TagMalloc (CLOCK_MESSAGE_SIZE, TAG_LEVEL); ! 1747: ! 1748: self->think = func_clock_think; ! 1749: ! 1750: if (self->spawnflags & 4) ! 1751: self->use = func_clock_use; ! 1752: else ! 1753: self->nextthink = level.time + 1; ! 1754: } ! 1755: ! 1756: //================================================================================= ! 1757: ! 1758: void teleporter_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) ! 1759: { ! 1760: edict_t *dest; ! 1761: int i; ! 1762: ! 1763: if (!other->client) ! 1764: return; ! 1765: dest = G_Find (NULL, FOFS(targetname), self->target); ! 1766: if (!dest) ! 1767: { ! 1768: gi.dprintf ("Couldn't find destination\n"); ! 1769: return; ! 1770: } ! 1771: ! 1772: // unlink to make sure it can't possibly interfere with KillBox ! 1773: gi.unlinkentity (other); ! 1774: ! 1775: VectorCopy (dest->s.origin, other->s.origin); ! 1776: VectorCopy (dest->s.origin, other->s.old_origin); ! 1777: other->s.origin[2] += 10; ! 1778: ! 1779: // clear the velocity and hold them in place briefly ! 1780: VectorClear (other->velocity); ! 1781: other->client->ps.pmove.pm_time = 160>>3; // hold time ! 1782: other->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT; ! 1783: ! 1784: // draw the teleport splash at source and on the player ! 1785: self->owner->s.event = EV_PLAYER_TELEPORT; ! 1786: other->s.event = EV_PLAYER_TELEPORT; ! 1787: ! 1788: // set angles ! 1789: for (i=0 ; i<3 ; i++) ! 1790: other->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest->s.angles[i] - other->client->resp.cmd_angles[i]); ! 1791: ! 1792: VectorClear (other->s.angles); ! 1793: VectorClear (other->client->ps.viewangles); ! 1794: VectorClear (other->client->v_angle); ! 1795: ! 1796: // kill anything at the destination ! 1797: KillBox (other); ! 1798: ! 1799: gi.linkentity (other); ! 1800: } ! 1801: ! 1802: /*QUAKED misc_teleporter (1 0 0) (-32 -32 -24) (32 32 -16) ! 1803: Stepping onto this disc will teleport players to the targeted misc_teleporter_dest object. ! 1804: */ ! 1805: void SP_misc_teleporter (edict_t *ent) ! 1806: { ! 1807: edict_t *trig; ! 1808: ! 1809: if (!ent->target) ! 1810: { ! 1811: gi.dprintf ("teleporter without a target.\n"); ! 1812: G_FreeEdict (ent); ! 1813: return; ! 1814: } ! 1815: ! 1816: gi.setmodel (ent, "models/objects/dmspot/tris.md2"); ! 1817: ent->s.skinnum = 1; ! 1818: ent->s.effects = EF_TELEPORTER; ! 1819: ent->s.sound = gi.soundindex ("world/amb10.wav"); ! 1820: ent->solid = SOLID_BBOX; ! 1821: ! 1822: VectorSet (ent->mins, -32, -32, -24); ! 1823: VectorSet (ent->maxs, 32, 32, -16); ! 1824: gi.linkentity (ent); ! 1825: ! 1826: trig = G_Spawn (); ! 1827: trig->touch = teleporter_touch; ! 1828: trig->solid = SOLID_TRIGGER; ! 1829: trig->target = ent->target; ! 1830: trig->owner = ent; ! 1831: VectorCopy (ent->s.origin, trig->s.origin); ! 1832: VectorSet (trig->mins, -8, -8, 8); ! 1833: VectorSet (trig->maxs, 8, 8, 24); ! 1834: gi.linkentity (trig); ! 1835: ! 1836: } ! 1837: ! 1838: /*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16) ! 1839: Point teleporters at these. ! 1840: */ ! 1841: void SP_misc_teleporter_dest (edict_t *ent) ! 1842: { ! 1843: gi.setmodel (ent, "models/objects/dmspot/tris.md2"); ! 1844: ent->s.skinnum = 0; ! 1845: ent->solid = SOLID_BBOX; ! 1846: // ent->s.effects |= EF_FLIES; ! 1847: VectorSet (ent->mins, -32, -32, -24); ! 1848: VectorSet (ent->maxs, 32, 32, -16); ! 1849: gi.linkentity (ent); ! 1850: } ! 1851:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.