g_misc.c (45231B)
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 */ 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 } 266 else 267 { 268 self->think = NULL; 269 self->nextthink = 0; 270 } 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); 375 other->s.event = EV_OTHER_TELEPORT; 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++) 1815 other->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest->s.angles[i] - other->client->resp.cmd_angles[i]); 1816 1817 VectorClear (other->s.angles); 1818 VectorClear (other->client->ps.viewangles); 1819 VectorClear (other->client->v_angle); 1820 1821 // kill anything at the destination 1822 KillBox (other); 1823 1824 gi.linkentity (other); 1825 } 1826 1827 /*QUAKED misc_teleporter (1 0 0) (-32 -32 -24) (32 32 -16) 1828 Stepping onto this disc will teleport players to the targeted misc_teleporter_dest object. 1829 */ 1830 void SP_misc_teleporter (edict_t *ent) 1831 { 1832 edict_t *trig; 1833 1834 if (!ent->target) 1835 { 1836 gi.dprintf ("teleporter without a target.\n"); 1837 G_FreeEdict (ent); 1838 return; 1839 } 1840 1841 gi.setmodel (ent, "models/objects/dmspot/tris.md2"); 1842 ent->s.skinnum = 1; 1843 ent->s.effects = EF_TELEPORTER; 1844 ent->s.sound = gi.soundindex ("world/amb10.wav"); 1845 ent->solid = SOLID_BBOX; 1846 1847 VectorSet (ent->mins, -32, -32, -24); 1848 VectorSet (ent->maxs, 32, 32, -16); 1849 gi.linkentity (ent); 1850 1851 trig = G_Spawn (); 1852 trig->touch = teleporter_touch; 1853 trig->solid = SOLID_TRIGGER; 1854 trig->target = ent->target; 1855 trig->owner = ent; 1856 VectorCopy (ent->s.origin, trig->s.origin); 1857 VectorSet (trig->mins, -8, -8, 8); 1858 VectorSet (trig->maxs, 8, 8, 24); 1859 gi.linkentity (trig); 1860 1861 } 1862 1863 /*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16) 1864 Point teleporters at these. 1865 */ 1866 void SP_misc_teleporter_dest (edict_t *ent) 1867 { 1868 gi.setmodel (ent, "models/objects/dmspot/tris.md2"); 1869 ent->s.skinnum = 0; 1870 ent->solid = SOLID_BBOX; 1871 // ent->s.effects |= EF_FLIES; 1872 VectorSet (ent->mins, -32, -32, -24); 1873 VectorSet (ent->maxs, 32, 32, -16); 1874 gi.linkentity (ent); 1875 } 1876