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