File: game\g_misc.c

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