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