File: game\p_weapon.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_weapon.c
   21 
   22 #include "g_local.h"
   23 #include "m_player.h"
   24 
   25 
   26 static qboolean is_quad;
   27 static byte             is_silenced;
   28 
   29 
   30 void weapon_grenade_fire (edict_t *ent, qboolean held);
   31 
   32 
   33 static void P_ProjectSource (gclient_t *client, vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result)
   34 {
   35         vec3_t  _distance;
   36 
   37         VectorCopy (distance, _distance);
   38         if (client->pers.hand == LEFT_HANDED)
   39                 _distance[1] *= -1;
   40         else if (client->pers.hand == CENTER_HANDED)
   41                 _distance[1] = 0;
   42         G_ProjectSource (point, _distance, forward, right, result);
   43 }
   44 
   45 
   46 /*
   47 ===============
   48 PlayerNoise
   49 
   50 Each player can have two noise objects associated with it:
   51 a personal noise (jumping, pain, weapon firing), and a weapon
   52 target noise (bullet wall impacts)
   53 
   54 Monsters that don't directly see the player can move
   55 to a noise in hopes of seeing the player from there.
   56 ===============
   57 */
   58 void PlayerNoise(edict_t *who, vec3_t where, int type)
   59 {
   60         edict_t         *noise;
   61 
   62         if (type == PNOISE_WEAPON)
   63         {
   64                 if (who->client->silencer_shots)
   65                 {
   66                         who->client->silencer_shots--;
   67                         return;
   68                 }
   69         }
   70 
   71         if (deathmatch->value)
   72                 return;
   73 
   74         if (who->flags & FL_NOTARGET)
   75                 return;
   76 
   77 
   78         if (!who->mynoise)
   79         {
   80                 noise = G_Spawn();
   81                 noise->classname = "player_noise";
   82                 VectorSet (noise->mins, -8, -8, -8);
   83                 VectorSet (noise->maxs, 8, 8, 8);
   84                 noise->owner = who;
   85                 noise->svflags = SVF_NOCLIENT;
   86                 who->mynoise = noise;
   87 
   88                 noise = G_Spawn();
   89                 noise->classname = "player_noise";
   90                 VectorSet (noise->mins, -8, -8, -8);
   91                 VectorSet (noise->maxs, 8, 8, 8);
   92                 noise->owner = who;
   93                 noise->svflags = SVF_NOCLIENT;
   94                 who->mynoise2 = noise;
   95         }
   96 
   97         if (type == PNOISE_SELF || type == PNOISE_WEAPON)
   98         {
   99                 noise = who->mynoise;
  100                 level.sound_entity = noise;
  101                 level.sound_entity_framenum = level.framenum;
  102         }
  103         else // type == PNOISE_IMPACT
  104         {
  105                 noise = who->mynoise2;
  106                 level.sound2_entity = noise;
  107                 level.sound2_entity_framenum = level.framenum;
  108         }
  109 
  110         VectorCopy (where, noise->s.origin);
  111         VectorSubtract (where, noise->maxs, noise->absmin);
  112         VectorAdd (where, noise->maxs, noise->absmax);
  113         noise->teleport_time = level.time;
  114         gi.linkentity (noise);
  115 }
  116 
  117 
  118 qboolean Pickup_Weapon (edict_t *ent, edict_t *other)
  119 {
  120         int                     index;
  121         gitem_t         *ammo;
  122 
  123         index = ITEM_INDEX(ent->item);
  124 
  125         if ( ( ((int)(dmflags->value) & DF_WEAPONS_STAY) || coop->value) 
  126                 && other->client->pers.inventory[index])
  127         {
  128                 if (!(ent->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM) ) )
  129                         return false;   // leave the weapon for others to pickup
  130         }
  131 
  132         other->client->pers.inventory[index]++;
  133 
  134         if (!(ent->spawnflags & DROPPED_ITEM) )
  135         {
  136                 // give them some ammo with it
  137                 ammo = FindItem (ent->item->ammo);
  138                 if ( (int)dmflags->value & DF_INFINITE_AMMO )
  139                         Add_Ammo (other, ammo, 1000);
  140                 else
  141                         Add_Ammo (other, ammo, ammo->quantity);
  142 
  143                 if (! (ent->spawnflags & DROPPED_PLAYER_ITEM) )
  144                 {
  145                         if (deathmatch->value)
  146                         {
  147                                 if ((int)(dmflags->value) & DF_WEAPONS_STAY)
  148                                         ent->flags |= FL_RESPAWN;
  149                                 else
  150                                         SetRespawn (ent, 30);
  151                         }
  152                         if (coop->value)
  153                                 ent->flags |= FL_RESPAWN;
  154                 }
  155         }
  156 
  157         if (other->client->pers.weapon != ent->item && 
  158                 (other->client->pers.inventory[index] == 1) &&
  159                 ( !deathmatch->value || other->client->pers.weapon == FindItem("blaster") ) )
  160                 other->client->newweapon = ent->item;
  161 
  162         return true;
  163 }
  164 
  165 
  166 /*
  167 ===============
  168 ChangeWeapon
  169 
  170 The old weapon has been dropped all the way, so make the new one
  171 current
  172 ===============
  173 */
  174 void ChangeWeapon (edict_t *ent)
  175 {
  176         int i;
  177 
  178         if (ent->client->grenade_time)
  179         {
  180                 ent->client->grenade_time = level.time;
  181                 ent->client->weapon_sound = 0;
  182                 weapon_grenade_fire (ent, false);
  183                 ent->client->grenade_time = 0;
  184         }
  185 
  186         ent->client->pers.lastweapon = ent->client->pers.weapon;
  187         ent->client->pers.weapon = ent->client->newweapon;
  188         ent->client->newweapon = NULL;
  189         ent->client->machinegun_shots = 0;
  190 
  191         // set visible model
  192         if (ent->s.modelindex == 255) {
  193                 if (ent->client->pers.weapon)
  194                         i = ((ent->client->pers.weapon->weapmodel & 0xff) << 8);
  195                 else
  196                         i = 0;
  197                 ent->s.skinnum = (ent - g_edicts - 1) | i;
  198         }
  199 
  200         if (ent->client->pers.weapon && ent->client->pers.weapon->ammo)
  201                 ent->client->ammo_index = ITEM_INDEX(FindItem(ent->client->pers.weapon->ammo));
  202         else
  203                 ent->client->ammo_index = 0;
  204 
  205         if (!ent->client->pers.weapon)
  206         {       // dead
  207                 ent->client->ps.gunindex = 0;
  208                 return;
  209         }
  210 
  211         ent->client->weaponstate = WEAPON_ACTIVATING;
  212         ent->client->ps.gunframe = 0;
  213         ent->client->ps.gunindex = gi.modelindex(ent->client->pers.weapon->view_model);
  214 
  215         ent->client->anim_priority = ANIM_PAIN;
  216         if(ent->client->ps.pmove.pm_flags & PMF_DUCKED)
  217         {
  218                         ent->s.frame = FRAME_crpain1;
  219                         ent->client->anim_end = FRAME_crpain4;
  220         }
  221         else
  222         {
  223                         ent->s.frame = FRAME_pain301;
  224                         ent->client->anim_end = FRAME_pain304;
  225                         
  226         }
  227 }
  228 
  229 /*
  230 =================
  231 NoAmmoWeaponChange
  232 =================
  233 */
  234 void NoAmmoWeaponChange (edict_t *ent)
  235 {
  236         if ( ent->client->pers.inventory[ITEM_INDEX(FindItem("slugs"))]
  237                 &&  ent->client->pers.inventory[ITEM_INDEX(FindItem("railgun"))] )
  238         {
  239                 ent->client->newweapon = FindItem ("railgun");
  240                 return;
  241         }
  242         if ( ent->client->pers.inventory[ITEM_INDEX(FindItem("cells"))]
  243                 &&  ent->client->pers.inventory[ITEM_INDEX(FindItem("hyperblaster"))] )
  244         {
  245                 ent->client->newweapon = FindItem ("hyperblaster");
  246                 return;
  247         }
  248         if ( ent->client->pers.inventory[ITEM_INDEX(FindItem("bullets"))]
  249                 &&  ent->client->pers.inventory[ITEM_INDEX(FindItem("chaingun"))] )
  250         {
  251                 ent->client->newweapon = FindItem ("chaingun");
  252                 return;
  253         }
  254         if ( ent->client->pers.inventory[ITEM_INDEX(FindItem("bullets"))]
  255                 &&  ent->client->pers.inventory[ITEM_INDEX(FindItem("machinegun"))] )
  256         {
  257                 ent->client->newweapon = FindItem ("machinegun");
  258                 return;
  259         }
  260         if ( ent->client->pers.inventory[ITEM_INDEX(FindItem("shells"))] > 1
  261                 &&  ent->client->pers.inventory[ITEM_INDEX(FindItem("super shotgun"))] )
  262         {
  263                 ent->client->newweapon = FindItem ("super shotgun");
  264                 return;
  265         }
  266         if ( ent->client->pers.inventory[ITEM_INDEX(FindItem("shells"))]
  267                 &&  ent->client->pers.inventory[ITEM_INDEX(FindItem("shotgun"))] )
  268         {
  269                 ent->client->newweapon = FindItem ("shotgun");
  270                 return;
  271         }
  272         ent->client->newweapon = FindItem ("blaster");
  273 }
  274 
  275 /*
  276 =================
  277 Think_Weapon
  278 
  279 Called by ClientBeginServerFrame and ClientThink
  280 =================
  281 */
  282 void Think_Weapon (edict_t *ent)
  283 {
  284         // if just died, put the weapon away
  285         if (ent->health < 1)
  286         {
  287                 ent->client->newweapon = NULL;
  288                 ChangeWeapon (ent);
  289         }
  290 
  291         // call active weapon think routine
  292         if (ent->client->pers.weapon && ent->client->pers.weapon->weaponthink)
  293         {
  294                 is_quad = (ent->client->quad_framenum > level.framenum);
  295                 if (ent->client->silencer_shots)
  296                         is_silenced = MZ_SILENCED;
  297                 else
  298                         is_silenced = 0;
  299                 ent->client->pers.weapon->weaponthink (ent);
  300         }
  301 }
  302 
  303 
  304 /*
  305 ================
  306 Use_Weapon
  307 
  308 Make the weapon ready if there is ammo
  309 ================
  310 */
  311 void Use_Weapon (edict_t *ent, gitem_t *item)
  312 {
  313         int                     ammo_index;
  314         gitem_t         *ammo_item;
  315 
  316         // see if we're already using it
  317         if (item == ent->client->pers.weapon)
  318                 return;
  319 
  320         if (item->ammo && !g_select_empty->value && !(item->flags & IT_AMMO))
  321         {
  322                 ammo_item = FindItem(item->ammo);
  323                 ammo_index = ITEM_INDEX(ammo_item);
  324 
  325                 if (!ent->client->pers.inventory[ammo_index])
  326                 {
  327                         gi.cprintf (ent, PRINT_HIGH, "No %s for %s.\n", ammo_item->pickup_name, item->pickup_name);
  328                         return;
  329                 }
  330 
  331                 if (ent->client->pers.inventory[ammo_index] < item->quantity)
  332                 {
  333                         gi.cprintf (ent, PRINT_HIGH, "Not enough %s for %s.\n", ammo_item->pickup_name, item->pickup_name);
  334                         return;
  335                 }
  336         }
  337 
  338         // change to this weapon when down
  339         ent->client->newweapon = item;
  340 }
  341 
  342 
  343 
  344 /*
  345 ================
  346 Drop_Weapon
  347 ================
  348 */
  349 void Drop_Weapon (edict_t *ent, gitem_t *item)
  350 {
  351         int             index;
  352 
  353         if ((int)(dmflags->value) & DF_WEAPONS_STAY)
  354                 return;
  355 
  356         index = ITEM_INDEX(item);
  357         // see if we're already using it
  358         if ( ((item == ent->client->pers.weapon) || (item == ent->client->newweapon))&& (ent->client->pers.inventory[index] == 1) )
  359         {
  360                 gi.cprintf (ent, PRINT_HIGH, "Can't drop current weapon\n");
  361                 return;
  362         }
  363 
  364         Drop_Item (ent, item);
  365         ent->client->pers.inventory[index]--;
  366 }
  367 
  368 
  369 /*
  370 ================
  371 Weapon_Generic
  372 
  373 A generic function to handle the basics of weapon thinking
  374 ================
  375 */
  376 #define FRAME_FIRE_FIRST                (FRAME_ACTIVATE_LAST + 1)
  377 #define FRAME_IDLE_FIRST                (FRAME_FIRE_LAST + 1)
  378 #define FRAME_DEACTIVATE_FIRST  (FRAME_IDLE_LAST + 1)
  379 
  380 void Weapon_Generic (edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, int FRAME_IDLE_LAST, int FRAME_DEACTIVATE_LAST, int *pause_frames, int *fire_frames, void (*fire)(edict_t *ent))
  381 {
  382         int             n;
  383 
  384         if(ent->deadflag || ent->s.modelindex != 255) // VWep animations screw up corpses
  385         {
  386                 return;
  387         }
  388 
  389         if (ent->client->weaponstate == WEAPON_DROPPING)
  390         {
  391                 if (ent->client->ps.gunframe == FRAME_DEACTIVATE_LAST)
  392                 {
  393                         ChangeWeapon (ent);
  394                         return;
  395                 }
  396                 else if ((FRAME_DEACTIVATE_LAST - ent->client->ps.gunframe) == 4)
  397                 {
  398                         ent->client->anim_priority = ANIM_REVERSE;
  399                         if(ent->client->ps.pmove.pm_flags & PMF_DUCKED)
  400                         {
  401                                 ent->s.frame = FRAME_crpain4+1;
  402                                 ent->client->anim_end = FRAME_crpain1;
  403                         }
  404                         else
  405                         {
  406                                 ent->s.frame = FRAME_pain304+1;
  407                                 ent->client->anim_end = FRAME_pain301;
  408                                 
  409                         }
  410                 }
  411 
  412                 ent->client->ps.gunframe++;
  413                 return;
  414         }
  415 
  416         if (ent->client->weaponstate == WEAPON_ACTIVATING)
  417         {
  418                 if (ent->client->ps.gunframe == FRAME_ACTIVATE_LAST)
  419                 {
  420                         ent->client->weaponstate = WEAPON_READY;
  421                         ent->client->ps.gunframe = FRAME_IDLE_FIRST;
  422                         return;
  423                 }
  424 
  425                 ent->client->ps.gunframe++;
  426                 return;
  427         }
  428 
  429         if ((ent->client->newweapon) && (ent->client->weaponstate != WEAPON_FIRING))
  430         {
  431                 ent->client->weaponstate = WEAPON_DROPPING;
  432                 ent->client->ps.gunframe = FRAME_DEACTIVATE_FIRST;
  433 
  434                 if ((FRAME_DEACTIVATE_LAST - FRAME_DEACTIVATE_FIRST) < 4)
  435                 {
  436                         ent->client->anim_priority = ANIM_REVERSE;
  437                         if(ent->client->ps.pmove.pm_flags & PMF_DUCKED)
  438                         {
  439                                 ent->s.frame = FRAME_crpain4+1;
  440                                 ent->client->anim_end = FRAME_crpain1;
  441                         }
  442                         else
  443                         {
  444                                 ent->s.frame = FRAME_pain304+1;
  445                                 ent->client->anim_end = FRAME_pain301;
  446                                 
  447                         }
  448                 }
  449                 return;
  450         }
  451 
  452         if (ent->client->weaponstate == WEAPON_READY)
  453         {
  454                 if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTON_ATTACK) )
  455                 {
  456                         ent->client->latched_buttons &= ~BUTTON_ATTACK;
  457                         if ((!ent->client->ammo_index) || 
  458                                 ( ent->client->pers.inventory[ent->client->ammo_index] >= ent->client->pers.weapon->quantity))
  459                         {
  460                                 ent->client->ps.gunframe = FRAME_FIRE_FIRST;
  461                                 ent->client->weaponstate = WEAPON_FIRING;
  462 
  463                                 // start the animation
  464                                 ent->client->anim_priority = ANIM_ATTACK;
  465                                 if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
  466                                 {
  467                                         ent->s.frame = FRAME_crattak1-1;
  468                                         ent->client->anim_end = FRAME_crattak9;
  469                                 }
  470                                 else
  471                                 {
  472                                         ent->s.frame = FRAME_attack1-1;
  473                                         ent->client->anim_end = FRAME_attack8;
  474                                 }
  475                         }
  476                         else
  477                         {
  478                                 if (level.time >= ent->pain_debounce_time)
  479                                 {
  480                                         gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
  481                                         ent->pain_debounce_time = level.time + 1;
  482                                 }
  483                                 NoAmmoWeaponChange (ent);
  484                         }
  485                 }
  486                 else
  487                 {
  488                         if (ent->client->ps.gunframe == FRAME_IDLE_LAST)
  489                         {
  490                                 ent->client->ps.gunframe = FRAME_IDLE_FIRST;
  491                                 return;
  492                         }
  493 
  494                         if (pause_frames)
  495                         {
  496                                 for (n = 0; pause_frames[n]; n++)
  497                                 {
  498                                         if (ent->client->ps.gunframe == pause_frames[n])
  499                                         {
  500                                                 if (rand()&15)
  501                                                         return;
  502                                         }
  503                                 }
  504                         }
  505 
  506                         ent->client->ps.gunframe++;
  507                         return;
  508                 }
  509         }
  510 
  511         if (ent->client->weaponstate == WEAPON_FIRING)
  512         {
  513                 for (n = 0; fire_frames[n]; n++)
  514                 {
  515                         if (ent->client->ps.gunframe == fire_frames[n])
  516                         {
  517                                 if (ent->client->quad_framenum > level.framenum)
  518                                         gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
  519 
  520                                 fire (ent);
  521                                 break;
  522                         }
  523                 }
  524 
  525                 if (!fire_frames[n])
  526                         ent->client->ps.gunframe++;
  527 
  528                 if (ent->client->ps.gunframe == FRAME_IDLE_FIRST+1)
  529                         ent->client->weaponstate = WEAPON_READY;
  530         }
  531 }
  532 
  533 
  534 /*
  535 ======================================================================
  536 
  537 GRENADE
  538 
  539 ======================================================================
  540 */
  541 
  542 #define GRENADE_TIMER           3.0
  543 #define GRENADE_MINSPEED        400
  544 #define GRENADE_MAXSPEED        800
  545 
  546 void weapon_grenade_fire (edict_t *ent, qboolean held)
  547 {
  548         vec3_t  offset;
  549         vec3_t  forward, right;
  550         vec3_t  start;
  551         int             damage = 125;
  552         float   timer;
  553         int             speed;
  554         float   radius;
  555 
  556         radius = damage+40;
  557         if (is_quad)
  558                 damage *= 4;
  559 
  560         VectorSet(offset, 8, 8, ent->viewheight-8);
  561         AngleVectors (ent->client->v_angle, forward, right, NULL);
  562         P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
  563 
  564         timer = ent->client->grenade_time - level.time;
  565         speed = GRENADE_MINSPEED + (GRENADE_TIMER - timer) * ((GRENADE_MAXSPEED - GRENADE_MINSPEED) / GRENADE_TIMER);
  566         fire_grenade2 (ent, start, forward, damage, speed, timer, radius, held);
  567 
  568         if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
  569                 ent->client->pers.inventory[ent->client->ammo_index]--;
  570 
  571         ent->client->grenade_time = level.time + 1.0;
  572 
  573         if(ent->deadflag || ent->s.modelindex != 255) // VWep animations screw up corpses
  574         {
  575                 return;
  576         }
  577 
  578         if (ent->health <= 0)
  579                 return;
  580 
  581         if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
  582         {
  583                 ent->client->anim_priority = ANIM_ATTACK;
  584                 ent->s.frame = FRAME_crattak1-1;
  585                 ent->client->anim_end = FRAME_crattak3;
  586         }
  587         else
  588         {
  589                 ent->client->anim_priority = ANIM_REVERSE;
  590                 ent->s.frame = FRAME_wave08;
  591                 ent->client->anim_end = FRAME_wave01;
  592         }
  593 }
  594 
  595 void Weapon_Grenade (edict_t *ent)
  596 {
  597         if ((ent->client->newweapon) && (ent->client->weaponstate == WEAPON_READY))
  598         {
  599                 ChangeWeapon (ent);
  600                 return;
  601         }
  602 
  603         if (ent->client->weaponstate == WEAPON_ACTIVATING)
  604         {
  605                 ent->client->weaponstate = WEAPON_READY;
  606                 ent->client->ps.gunframe = 16;
  607                 return;
  608         }
  609 
  610         if (ent->client->weaponstate == WEAPON_READY)
  611         {
  612                 if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTON_ATTACK) )
  613                 {
  614                         ent->client->latched_buttons &= ~BUTTON_ATTACK;
  615                         if (ent->client->pers.inventory[ent->client->ammo_index])
  616                         {
  617                                 ent->client->ps.gunframe = 1;
  618                                 ent->client->weaponstate = WEAPON_FIRING;
  619                                 ent->client->grenade_time = 0;
  620                         }
  621                         else
  622                         {
  623                                 if (level.time >= ent->pain_debounce_time)
  624                                 {
  625                                         gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
  626                                         ent->pain_debounce_time = level.time + 1;
  627                                 }
  628                                 NoAmmoWeaponChange (ent);
  629                         }
  630                         return;
  631                 }
  632 
  633                 if ((ent->client->ps.gunframe == 29) || (ent->client->ps.gunframe == 34) || (ent->client->ps.gunframe == 39) || (ent->client->ps.gunframe == 48))
  634                 {
  635                         if (rand()&15)
  636                                 return;
  637                 }
  638 
  639                 if (++ent->client->ps.gunframe > 48)
  640                         ent->client->ps.gunframe = 16;
  641                 return;
  642         }
  643 
  644         if (ent->client->weaponstate == WEAPON_FIRING)
  645         {
  646                 if (ent->client->ps.gunframe == 5)
  647                         gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/hgrena1b.wav"), 1, ATTN_NORM, 0);
  648 
  649                 if (ent->client->ps.gunframe == 11)
  650                 {
  651                         if (!ent->client->grenade_time)
  652                         {
  653                                 ent->client->grenade_time = level.time + GRENADE_TIMER + 0.2;
  654                                 ent->client->weapon_sound = gi.soundindex("weapons/hgrenc1b.wav");
  655                         }
  656 
  657                         // they waited too long, detonate it in their hand
  658                         if (!ent->client->grenade_blew_up && level.time >= ent->client->grenade_time)
  659                         {
  660                                 ent->client->weapon_sound = 0;
  661                                 weapon_grenade_fire (ent, true);
  662                                 ent->client->grenade_blew_up = true;
  663                         }
  664 
  665                         if (ent->client->buttons & BUTTON_ATTACK)
  666                                 return;
  667 
  668                         if (ent->client->grenade_blew_up)
  669                         {
  670                                 if (level.time >= ent->client->grenade_time)
  671                                 {
  672                                         ent->client->ps.gunframe = 15;
  673                                         ent->client->grenade_blew_up = false;
  674                                 }
  675                                 else
  676                                 {
  677                                         return;
  678                                 }
  679                         }
  680                 }
  681 
  682                 if (ent->client->ps.gunframe == 12)
  683                 {
  684                         ent->client->weapon_sound = 0;
  685                         weapon_grenade_fire (ent, false);
  686                 }
  687 
  688                 if ((ent->client->ps.gunframe == 15) && (level.time < ent->client->grenade_time))
  689                         return;
  690 
  691                 ent->client->ps.gunframe++;
  692 
  693                 if (ent->client->ps.gunframe == 16)
  694                 {
  695                         ent->client->grenade_time = 0;
  696                         ent->client->weaponstate = WEAPON_READY;
  697                 }
  698         }
  699 }
  700 
  701 /*
  702 ======================================================================
  703 
  704 GRENADE LAUNCHER
  705 
  706 ======================================================================
  707 */
  708 
  709 void weapon_grenadelauncher_fire (edict_t *ent)
  710 {
  711         vec3_t  offset;
  712         vec3_t  forward, right;
  713         vec3_t  start;
  714         int             damage = 120;
  715         float   radius;
  716 
  717         radius = damage+40;
  718         if (is_quad)
  719                 damage *= 4;
  720 
  721         VectorSet(offset, 8, 8, ent->viewheight-8);
  722         AngleVectors (ent->client->v_angle, forward, right, NULL);
  723         P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
  724 
  725         VectorScale (forward, -2, ent->client->kick_origin);
  726         ent->client->kick_angles[0] = -1;
  727 
  728         fire_grenade (ent, start, forward, damage, 600, 2.5, radius);
  729 
  730         gi.WriteByte (svc_muzzleflash);
  731         gi.WriteShort (ent-g_edicts);
  732         gi.WriteByte (MZ_GRENADE | is_silenced);
  733         gi.multicast (ent->s.origin, MULTICAST_PVS);
  734 
  735         ent->client->ps.gunframe++;
  736 
  737         PlayerNoise(ent, start, PNOISE_WEAPON);
  738 
  739         if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
  740                 ent->client->pers.inventory[ent->client->ammo_index]--;
  741 }
  742 
  743 void Weapon_GrenadeLauncher (edict_t *ent)
  744 {
  745         static int      pause_frames[]  = {34, 51, 59, 0};
  746         static int      fire_frames[]   = {6, 0};
  747 
  748         Weapon_Generic (ent, 5, 16, 59, 64, pause_frames, fire_frames, weapon_grenadelauncher_fire);
  749 }
  750 
  751 /*
  752 ======================================================================
  753 
  754 ROCKET
  755 
  756 ======================================================================
  757 */
  758 
  759 void Weapon_RocketLauncher_Fire (edict_t *ent)
  760 {
  761         vec3_t  offset, start;
  762         vec3_t  forward, right;
  763         int             damage;
  764         float   damage_radius;
  765         int             radius_damage;
  766 
  767         damage = 100 + (int)(random() * 20.0);
  768         radius_damage = 120;
  769         damage_radius = 120;
  770         if (is_quad)
  771         {
  772                 damage *= 4;
  773                 radius_damage *= 4;
  774         }
  775 
  776         AngleVectors (ent->client->v_angle, forward, right, NULL);
  777 
  778         VectorScale (forward, -2, ent->client->kick_origin);
  779         ent->client->kick_angles[0] = -1;
  780 
  781         VectorSet(offset, 8, 8, ent->viewheight-8);
  782         P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
  783         fire_rocket (ent, start, forward, damage, 650, damage_radius, radius_damage);
  784 
  785         // send muzzle flash
  786         gi.WriteByte (svc_muzzleflash);
  787         gi.WriteShort (ent-g_edicts);
  788         gi.WriteByte (MZ_ROCKET | is_silenced);
  789         gi.multicast (ent->s.origin, MULTICAST_PVS);
  790 
  791         ent->client->ps.gunframe++;
  792 
  793         PlayerNoise(ent, start, PNOISE_WEAPON);
  794 
  795         if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
  796                 ent->client->pers.inventory[ent->client->ammo_index]--;
  797 }
  798 
  799 void Weapon_RocketLauncher (edict_t *ent)
  800 {
  801         static int      pause_frames[]  = {25, 33, 42, 50, 0};
  802         static int      fire_frames[]   = {5, 0};
  803 
  804         Weapon_Generic (ent, 4, 12, 50, 54, pause_frames, fire_frames, Weapon_RocketLauncher_Fire);
  805 }
  806 
  807 
  808 /*
  809 ======================================================================
  810 
  811 BLASTER / HYPERBLASTER
  812 
  813 ======================================================================
  814 */
  815 
  816 void Blaster_Fire (edict_t *ent, vec3_t g_offset, int damage, qboolean hyper, int effect)
  817 {
  818         vec3_t  forward, right;
  819         vec3_t  start;
  820         vec3_t  offset;
  821 
  822         if (is_quad)
  823                 damage *= 4;
  824         AngleVectors (ent->client->v_angle, forward, right, NULL);
  825         VectorSet(offset, 24, 8, ent->viewheight-8);
  826         VectorAdd (offset, g_offset, offset);
  827         P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
  828 
  829         VectorScale (forward, -2, ent->client->kick_origin);
  830         ent->client->kick_angles[0] = -1;
  831 
  832         fire_blaster (ent, start, forward, damage, 1000, effect, hyper);
  833 
  834         // send muzzle flash
  835         gi.WriteByte (svc_muzzleflash);
  836         gi.WriteShort (ent-g_edicts);
  837         if (hyper)
  838                 gi.WriteByte (MZ_HYPERBLASTER | is_silenced);
  839         else
  840                 gi.WriteByte (MZ_BLASTER | is_silenced);
  841         gi.multicast (ent->s.origin, MULTICAST_PVS);
  842 
  843         PlayerNoise(ent, start, PNOISE_WEAPON);
  844 }
  845 
  846 
  847 void Weapon_Blaster_Fire (edict_t *ent)
  848 {
  849         int             damage;
  850 
  851         if (deathmatch->value)
  852                 damage = 15;
  853         else
  854                 damage = 10;
  855         Blaster_Fire (ent, vec3_origin, damage, false, EF_BLASTER);
  856         ent->client->ps.gunframe++;
  857 }
  858 
  859 void Weapon_Blaster (edict_t *ent)
  860 {
  861         static int      pause_frames[]  = {19, 32, 0};
  862         static int      fire_frames[]   = {5, 0};
  863 
  864         Weapon_Generic (ent, 4, 8, 52, 55, pause_frames, fire_frames, Weapon_Blaster_Fire);
  865 }
  866 
  867 
  868 void Weapon_HyperBlaster_Fire (edict_t *ent)
  869 {
  870         float   rotation;
  871         vec3_t  offset;
  872         int             effect;
  873         int             damage;
  874 
  875         ent->client->weapon_sound = gi.soundindex("weapons/hyprbl1a.wav");
  876 
  877         if (!(ent->client->buttons & BUTTON_ATTACK))
  878         {
  879                 ent->client->ps.gunframe++;
  880         }
  881         else
  882         {
  883                 if (! ent->client->pers.inventory[ent->client->ammo_index] )
  884                 {
  885                         if (level.time >= ent->pain_debounce_time)
  886                         {
  887                                 gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
  888                                 ent->pain_debounce_time = level.time + 1;
  889                         }
  890                         NoAmmoWeaponChange (ent);
  891                 }
  892                 else
  893                 {
  894                         rotation = (ent->client->ps.gunframe - 5) * 2*M_PI/6;
  895                         offset[0] = -4 * sin(rotation);
  896                         offset[1] = 0;
  897                         offset[2] = 4 * cos(rotation);
  898 
  899                         if ((ent->client->ps.gunframe == 6) || (ent->client->ps.gunframe == 9))
  900                                 effect = EF_HYPERBLASTER;
  901                         else
  902                                 effect = 0;
  903                         if (deathmatch->value)
  904                                 damage = 15;
  905                         else
  906                                 damage = 20;
  907                         Blaster_Fire (ent, offset, damage, true, effect);
  908                         if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
  909                                 ent->client->pers.inventory[ent->client->ammo_index]--;
  910 
  911                         ent->client->anim_priority = ANIM_ATTACK;
  912                         if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
  913                         {
  914                                 ent->s.frame = FRAME_crattak1 - 1;
  915                                 ent->client->anim_end = FRAME_crattak9;
  916                         }
  917                         else
  918                         {
  919                                 ent->s.frame = FRAME_attack1 - 1;
  920                                 ent->client->anim_end = FRAME_attack8;
  921                         }
  922                 }
  923 
  924                 ent->client->ps.gunframe++;
  925                 if (ent->client->ps.gunframe == 12 && ent->client->pers.inventory[ent->client->ammo_index])
  926                         ent->client->ps.gunframe = 6;
  927         }
  928 
  929         if (ent->client->ps.gunframe == 12)
  930         {
  931                 gi.sound(ent, CHAN_AUTO, gi.soundindex("weapons/hyprbd1a.wav"), 1, ATTN_NORM, 0);
  932                 ent->client->weapon_sound = 0;
  933         }
  934 
  935 }
  936 
  937 void Weapon_HyperBlaster (edict_t *ent)
  938 {
  939         static int      pause_frames[]  = {0};
  940         static int      fire_frames[]   = {6, 7, 8, 9, 10, 11, 0};
  941 
  942         Weapon_Generic (ent, 5, 20, 49, 53, pause_frames, fire_frames, Weapon_HyperBlaster_Fire);
  943 }
  944 
  945 /*
  946 ======================================================================
  947 
  948 MACHINEGUN / CHAINGUN
  949 
  950 ======================================================================
  951 */
  952 
  953 void Machinegun_Fire (edict_t *ent)
  954 {
  955         int     i;
  956         vec3_t          start;
  957         vec3_t          forward, right;
  958         vec3_t          angles;
  959         int                     damage = 8;
  960         int                     kick = 2;
  961         vec3_t          offset;
  962 
  963         if (!(ent->client->buttons & BUTTON_ATTACK))
  964         {
  965                 ent->client->machinegun_shots = 0;
  966                 ent->client->ps.gunframe++;
  967                 return;
  968         }
  969 
  970         if (ent->client->ps.gunframe == 5)
  971                 ent->client->ps.gunframe = 4;
  972         else
  973                 ent->client->ps.gunframe = 5;
  974 
  975         if (ent->client->pers.inventory[ent->client->ammo_index] < 1)
  976         {
  977                 ent->client->ps.gunframe = 6;
  978                 if (level.time >= ent->pain_debounce_time)
  979                 {
  980                         gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
  981                         ent->pain_debounce_time = level.time + 1;
  982                 }
  983                 NoAmmoWeaponChange (ent);
  984                 return;
  985         }
  986 
  987         if (is_quad)
  988         {
  989                 damage *= 4;
  990                 kick *= 4;
  991         }
  992 
  993         for (i=1 ; i<3 ; i++)
  994         {
  995                 ent->client->kick_origin[i] = crandom() * 0.35;
  996                 ent->client->kick_angles[i] = crandom() * 0.7;
  997         }
  998         ent->client->kick_origin[0] = crandom() * 0.35;
  999         ent->client->kick_angles[0] = ent->client->machinegun_shots * -1.5;
 1000 
 1001         // raise the gun as it is firing
 1002         if (!deathmatch->value)
 1003         {
 1004                 ent->client->machinegun_shots++;
 1005                 if (ent->client->machinegun_shots > 9)
 1006                         ent->client->machinegun_shots = 9;
 1007         }
 1008 
 1009         // get start / end positions
 1010         VectorAdd (ent->client->v_angle, ent->client->kick_angles, angles);
 1011         AngleVectors (angles, forward, right, NULL);
 1012         VectorSet(offset, 0, 8, ent->viewheight-8);
 1013         P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
 1014         fire_bullet (ent, start, forward, damage, kick, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_MACHINEGUN);
 1015 
 1016         gi.WriteByte (svc_muzzleflash);
 1017         gi.WriteShort (ent-g_edicts);
 1018         gi.WriteByte (MZ_MACHINEGUN | is_silenced);
 1019         gi.multicast (ent->s.origin, MULTICAST_PVS);
 1020 
 1021         PlayerNoise(ent, start, PNOISE_WEAPON);
 1022 
 1023         if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
 1024                 ent->client->pers.inventory[ent->client->ammo_index]--;
 1025 
 1026         ent->client->anim_priority = ANIM_ATTACK;
 1027         if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
 1028         {
 1029                 ent->s.frame = FRAME_crattak1 - (int) (random()+0.25);
 1030                 ent->client->anim_end = FRAME_crattak9;
 1031         }
 1032         else
 1033         {
 1034                 ent->s.frame = FRAME_attack1 - (int) (random()+0.25);
 1035                 ent->client->anim_end = FRAME_attack8;
 1036         }
 1037 }
 1038 
 1039 void Weapon_Machinegun (edict_t *ent)
 1040 {
 1041         static int      pause_frames[]  = {23, 45, 0};
 1042         static int      fire_frames[]   = {4, 5, 0};
 1043 
 1044         Weapon_Generic (ent, 3, 5, 45, 49, pause_frames, fire_frames, Machinegun_Fire);
 1045 }
 1046 
 1047 void Chaingun_Fire (edict_t *ent)
 1048 {
 1049         int                     i;
 1050         int                     shots;
 1051         vec3_t          start;
 1052         vec3_t          forward, right, up;
 1053         float           r, u;
 1054         vec3_t          offset;
 1055         int                     damage;
 1056         int                     kick = 2;
 1057 
 1058         if (deathmatch->value)
 1059                 damage = 6;
 1060         else
 1061                 damage = 8;
 1062 
 1063         if (ent->client->ps.gunframe == 5)
 1064                 gi.sound(ent, CHAN_AUTO, gi.soundindex("weapons/chngnu1a.wav"), 1, ATTN_IDLE, 0);
 1065 
 1066         if ((ent->client->ps.gunframe == 14) && !(ent->client->buttons & BUTTON_ATTACK))
 1067         {
 1068                 ent->client->ps.gunframe = 32;
 1069                 ent->client->weapon_sound = 0;
 1070                 return;
 1071         }
 1072         else if ((ent->client->ps.gunframe == 21) && (ent->client->buttons & BUTTON_ATTACK)
 1073                 && ent->client->pers.inventory[ent->client->ammo_index])
 1074         {
 1075                 ent->client->ps.gunframe = 15;
 1076         }
 1077         else
 1078         {
 1079                 ent->client->ps.gunframe++;
 1080         }
 1081 
 1082         if (ent->client->ps.gunframe == 22)
 1083         {
 1084                 ent->client->weapon_sound = 0;
 1085                 gi.sound(ent, CHAN_AUTO, gi.soundindex("weapons/chngnd1a.wav"), 1, ATTN_IDLE, 0);
 1086         }
 1087         else
 1088         {
 1089                 ent->client->weapon_sound = gi.soundindex("weapons/chngnl1a.wav");
 1090         }
 1091 
 1092         ent->client->anim_priority = ANIM_ATTACK;
 1093         if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
 1094         {
 1095                 ent->s.frame = FRAME_crattak1 - (ent->client->ps.gunframe & 1);
 1096                 ent->client->anim_end = FRAME_crattak9;
 1097         }
 1098         else
 1099         {
 1100                 ent->s.frame = FRAME_attack1 - (ent->client->ps.gunframe & 1);
 1101                 ent->client->anim_end = FRAME_attack8;
 1102         }
 1103 
 1104         if (ent->client->ps.gunframe <= 9)
 1105                 shots = 1;
 1106         else if (ent->client->ps.gunframe <= 14)
 1107         {
 1108                 if (ent->client->buttons & BUTTON_ATTACK)
 1109                         shots = 2;
 1110                 else
 1111                         shots = 1;
 1112         }
 1113         else
 1114                 shots = 3;
 1115 
 1116         if (ent->client->pers.inventory[ent->client->ammo_index] < shots)
 1117                 shots = ent->client->pers.inventory[ent->client->ammo_index];
 1118 
 1119         if (!shots)
 1120         {
 1121                 if (level.time >= ent->pain_debounce_time)
 1122                 {
 1123                         gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
 1124                         ent->pain_debounce_time = level.time + 1;
 1125                 }
 1126                 NoAmmoWeaponChange (ent);
 1127                 return;
 1128         }
 1129 
 1130         if (is_quad)
 1131         {
 1132                 damage *= 4;
 1133                 kick *= 4;
 1134         }
 1135 
 1136         for (i=0 ; i<3 ; i++)
 1137         {
 1138                 ent->client->kick_origin[i] = crandom() * 0.35;
 1139                 ent->client->kick_angles[i] = crandom() * 0.7;
 1140         }
 1141 
 1142         for (i=0 ; i<shots ; i++)
 1143         {
 1144                 // get start / end positions
 1145                 AngleVectors (ent->client->v_angle, forward, right, up);
 1146                 r = 7 + crandom()*4;
 1147                 u = crandom()*4;
 1148                 VectorSet(offset, 0, r, u + ent->viewheight-8);
 1149                 P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
 1150 
 1151                 fire_bullet (ent, start, forward, damage, kick, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_CHAINGUN);
 1152         }
 1153 
 1154         // send muzzle flash
 1155         gi.WriteByte (svc_muzzleflash);
 1156         gi.WriteShort (ent-g_edicts);
 1157         gi.WriteByte ((MZ_CHAINGUN1 + shots - 1) | is_silenced);
 1158         gi.multicast (ent->s.origin, MULTICAST_PVS);
 1159 
 1160         PlayerNoise(ent, start, PNOISE_WEAPON);
 1161 
 1162         if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
 1163                 ent->client->pers.inventory[ent->client->ammo_index] -= shots;
 1164 }
 1165 
 1166 
 1167 void Weapon_Chaingun (edict_t *ent)
 1168 {
 1169         static int      pause_frames[]  = {38, 43, 51, 61, 0};
 1170         static int      fire_frames[]   = {5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 0};
 1171 
 1172         Weapon_Generic (ent, 4, 31, 61, 64, pause_frames, fire_frames, Chaingun_Fire);
 1173 }
 1174 
 1175 
 1176 /*
 1177 ======================================================================
 1178 
 1179 SHOTGUN / SUPERSHOTGUN
 1180 
 1181 ======================================================================
 1182 */
 1183 
 1184 void weapon_shotgun_fire (edict_t *ent)
 1185 {
 1186         vec3_t          start;
 1187         vec3_t          forward, right;
 1188         vec3_t          offset;
 1189         int                     damage = 4;
 1190         int                     kick = 8;
 1191 
 1192         if (ent->client->ps.gunframe == 9)
 1193         {
 1194                 ent->client->ps.gunframe++;
 1195                 return;
 1196         }
 1197 
 1198         AngleVectors (ent->client->v_angle, forward, right, NULL);
 1199 
 1200         VectorScale (forward, -2, ent->client->kick_origin);
 1201         ent->client->kick_angles[0] = -2;
 1202 
 1203         VectorSet(offset, 0, 8,  ent->viewheight-8);
 1204         P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
 1205 
 1206         if (is_quad)
 1207         {
 1208                 damage *= 4;
 1209                 kick *= 4;
 1210         }
 1211 
 1212         if (deathmatch->value)
 1213                 fire_shotgun (ent, start, forward, damage, kick, 500, 500, DEFAULT_DEATHMATCH_SHOTGUN_COUNT, MOD_SHOTGUN);
 1214         else
 1215                 fire_shotgun (ent, start, forward, damage, kick, 500, 500, DEFAULT_SHOTGUN_COUNT, MOD_SHOTGUN);
 1216 
 1217         // send muzzle flash
 1218         gi.WriteByte (svc_muzzleflash);
 1219         gi.WriteShort (ent-g_edicts);
 1220         gi.WriteByte (MZ_SHOTGUN | is_silenced);
 1221         gi.multicast (ent->s.origin, MULTICAST_PVS);
 1222 
 1223         ent->client->ps.gunframe++;
 1224         PlayerNoise(ent, start, PNOISE_WEAPON);
 1225 
 1226         if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
 1227                 ent->client->pers.inventory[ent->client->ammo_index]--;
 1228 }
 1229 
 1230 void Weapon_Shotgun (edict_t *ent)
 1231 {
 1232         static int      pause_frames[]  = {22, 28, 34, 0};
 1233         static int      fire_frames[]   = {8, 9, 0};
 1234 
 1235         Weapon_Generic (ent, 7, 18, 36, 39, pause_frames, fire_frames, weapon_shotgun_fire);
 1236 }
 1237 
 1238 
 1239 void weapon_supershotgun_fire (edict_t *ent)
 1240 {
 1241         vec3_t          start;
 1242         vec3_t          forward, right;
 1243         vec3_t          offset;
 1244         vec3_t          v;
 1245         int                     damage = 6;
 1246         int                     kick = 12;
 1247 
 1248         AngleVectors (ent->client->v_angle, forward, right, NULL);
 1249 
 1250         VectorScale (forward, -2, ent->client->kick_origin);
 1251         ent->client->kick_angles[0] = -2;
 1252 
 1253         VectorSet(offset, 0, 8,  ent->viewheight-8);
 1254         P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
 1255 
 1256         if (is_quad)
 1257         {
 1258                 damage *= 4;
 1259                 kick *= 4;
 1260         }
 1261 
 1262         v[PITCH] = ent->client->v_angle[PITCH];
 1263         v[YAW]   = ent->client->v_angle[YAW] - 5;
 1264         v[ROLL]  = ent->client->v_angle[ROLL];
 1265         AngleVectors (v, forward, NULL, NULL);
 1266         fire_shotgun (ent, start, forward, damage, kick, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SSHOTGUN_COUNT/2, MOD_SSHOTGUN);
 1267         v[YAW]   = ent->client->v_angle[YAW] + 5;
 1268         AngleVectors (v, forward, NULL, NULL);
 1269         fire_shotgun (ent, start, forward, damage, kick, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SSHOTGUN_COUNT/2, MOD_SSHOTGUN);
 1270 
 1271         // send muzzle flash
 1272         gi.WriteByte (svc_muzzleflash);
 1273         gi.WriteShort (ent-g_edicts);
 1274         gi.WriteByte (MZ_SSHOTGUN | is_silenced);
 1275         gi.multicast (ent->s.origin, MULTICAST_PVS);
 1276 
 1277         ent->client->ps.gunframe++;
 1278         PlayerNoise(ent, start, PNOISE_WEAPON);
 1279 
 1280         if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
 1281                 ent->client->pers.inventory[ent->client->ammo_index] -= 2;
 1282 }
 1283 
 1284 void Weapon_SuperShotgun (edict_t *ent)
 1285 {
 1286         static int      pause_frames[]  = {29, 42, 57, 0};
 1287         static int      fire_frames[]   = {7, 0};
 1288 
 1289         Weapon_Generic (ent, 6, 17, 57, 61, pause_frames, fire_frames, weapon_supershotgun_fire);
 1290 }
 1291 
 1292 
 1293 
 1294 /*
 1295 ======================================================================
 1296 
 1297 RAILGUN
 1298 
 1299 ======================================================================
 1300 */
 1301 
 1302 void weapon_railgun_fire (edict_t *ent)
 1303 {
 1304         vec3_t          start;
 1305         vec3_t          forward, right;
 1306         vec3_t          offset;
 1307         int                     damage;
 1308         int                     kick;
 1309 
 1310         if (deathmatch->value)
 1311         {       // normal damage is too extreme in dm
 1312                 damage = 100;
 1313                 kick = 200;
 1314         }
 1315         else
 1316         {
 1317                 damage = 150;
 1318                 kick = 250;
 1319         }
 1320 
 1321         if (is_quad)
 1322         {
 1323                 damage *= 4;
 1324                 kick *= 4;
 1325         }
 1326 
 1327         AngleVectors (ent->client->v_angle, forward, right, NULL);
 1328 
 1329         VectorScale (forward, -3, ent->client->kick_origin);
 1330         ent->client->kick_angles[0] = -3;
 1331 
 1332         VectorSet(offset, 0, 7,  ent->viewheight-8);
 1333         P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
 1334         fire_rail (ent, start, forward, damage, kick);
 1335 
 1336         // send muzzle flash
 1337         gi.WriteByte (svc_muzzleflash);
 1338         gi.WriteShort (ent-g_edicts);
 1339         gi.WriteByte (MZ_RAILGUN | is_silenced);
 1340         gi.multicast (ent->s.origin, MULTICAST_PVS);
 1341 
 1342         ent->client->ps.gunframe++;
 1343         PlayerNoise(ent, start, PNOISE_WEAPON);
 1344 
 1345         if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
 1346                 ent->client->pers.inventory[ent->client->ammo_index]--;
 1347 }
 1348 
 1349 
 1350 void Weapon_Railgun (edict_t *ent)
 1351 {
 1352         static int      pause_frames[]  = {56, 0};
 1353         static int      fire_frames[]   = {4, 0};
 1354 
 1355         Weapon_Generic (ent, 3, 18, 56, 61, pause_frames, fire_frames, weapon_railgun_fire);
 1356 }
 1357 
 1358 
 1359 /*
 1360 ======================================================================
 1361 
 1362 BFG10K
 1363 
 1364 ======================================================================
 1365 */
 1366 
 1367 void weapon_bfg_fire (edict_t *ent)
 1368 {
 1369         vec3_t  offset, start;
 1370         vec3_t  forward, right;
 1371         int             damage;
 1372         float   damage_radius = 1000;
 1373 
 1374         if (deathmatch->value)
 1375                 damage = 200;
 1376         else
 1377                 damage = 500;
 1378 
 1379         if (ent->client->ps.gunframe == 9)
 1380         {
 1381                 // send muzzle flash
 1382                 gi.WriteByte (svc_muzzleflash);
 1383                 gi.WriteShort (ent-g_edicts);
 1384                 gi.WriteByte (MZ_BFG | is_silenced);
 1385                 gi.multicast (ent->s.origin, MULTICAST_PVS);
 1386 
 1387                 ent->client->ps.gunframe++;
 1388 
 1389                 PlayerNoise(ent, start, PNOISE_WEAPON);
 1390                 return;
 1391         }
 1392 
 1393         // cells can go down during windup (from power armor hits), so
 1394         // check again and abort firing if we don't have enough now
 1395         if (ent->client->pers.inventory[ent->client->ammo_index] < 50)
 1396         {
 1397                 ent->client->ps.gunframe++;
 1398                 return;
 1399         }
 1400 
 1401         if (is_quad)
 1402                 damage *= 4;
 1403 
 1404         AngleVectors (ent->client->v_angle, forward, right, NULL);
 1405 
 1406         VectorScale (forward, -2, ent->client->kick_origin);
 1407 
 1408         // make a big pitch kick with an inverse fall
 1409         ent->client->v_dmg_pitch = -40;
 1410         ent->client->v_dmg_roll = crandom()*8;
 1411         ent->client->v_dmg_time = level.time + DAMAGE_TIME;
 1412 
 1413         VectorSet(offset, 8, 8, ent->viewheight-8);
 1414         P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
 1415         fire_bfg (ent, start, forward, damage, 400, damage_radius);
 1416 
 1417         ent->client->ps.gunframe++;
 1418 
 1419         PlayerNoise(ent, start, PNOISE_WEAPON);
 1420 
 1421         if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
 1422                 ent->client->pers.inventory[ent->client->ammo_index] -= 50;
 1423 }
 1424 
 1425 void Weapon_BFG (edict_t *ent)
 1426 {
 1427         static int      pause_frames[]  = {39, 45, 50, 55, 0};
 1428         static int      fire_frames[]   = {9, 17, 0};
 1429 
 1430         Weapon_Generic (ent, 8, 32, 55, 58, pause_frames, fire_frames, weapon_bfg_fire);
 1431 }
 1432 
 1433 
 1434 //======================================================================
 1435