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