Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

p_weapon.c (35823B)


      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 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 static void Weapon_Generic2 (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 || instantweap->value)
    419 		{
    420 			ent->client->weaponstate = WEAPON_READY;
    421 			ent->client->ps.gunframe = FRAME_IDLE_FIRST;
    422 			// we go recursive here to instant ready the weapon
    423 			Weapon_Generic2 (ent, FRAME_ACTIVATE_LAST, FRAME_FIRE_LAST, 
    424 				FRAME_IDLE_LAST, FRAME_DEACTIVATE_LAST, pause_frames, 
    425 				fire_frames, fire);
    426 			return;
    427 		}
    428 
    429 		ent->client->ps.gunframe++;
    430 		return;
    431 	}
    432 
    433 	if ((ent->client->newweapon) && (ent->client->weaponstate != WEAPON_FIRING))
    434 	{
    435 		ent->client->weaponstate = WEAPON_DROPPING;
    436 		if (instantweap->value) {
    437 			ChangeWeapon(ent);
    438 			return;
    439 		} else
    440 			ent->client->ps.gunframe = FRAME_DEACTIVATE_FIRST;
    441 
    442 		if ((FRAME_DEACTIVATE_LAST - FRAME_DEACTIVATE_FIRST) < 4)
    443 		{
    444 			ent->client->anim_priority = ANIM_REVERSE;
    445 			if(ent->client->ps.pmove.pm_flags & PMF_DUCKED)
    446 			{
    447 				ent->s.frame = FRAME_crpain4+1;
    448 				ent->client->anim_end = FRAME_crpain1;
    449 			}
    450 			else
    451 			{
    452 				ent->s.frame = FRAME_pain304+1;
    453 				ent->client->anim_end = FRAME_pain301;
    454 				
    455 			}
    456 		}
    457 		return;
    458 	}
    459 
    460 	if (ent->client->weaponstate == WEAPON_READY)
    461 	{
    462 		if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTON_ATTACK) )
    463 		{
    464 			ent->client->latched_buttons &= ~BUTTON_ATTACK;
    465 			if ((!ent->client->ammo_index) || 
    466 				( ent->client->pers.inventory[ent->client->ammo_index] >= ent->client->pers.weapon->quantity))
    467 			{
    468 				ent->client->ps.gunframe = FRAME_FIRE_FIRST;
    469 				ent->client->weaponstate = WEAPON_FIRING;
    470 
    471 				// start the animation
    472 				ent->client->anim_priority = ANIM_ATTACK;
    473 				if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
    474 				{
    475 					ent->s.frame = FRAME_crattak1-1;
    476 					ent->client->anim_end = FRAME_crattak9;
    477 				}
    478 				else
    479 				{
    480 					ent->s.frame = FRAME_attack1-1;
    481 					ent->client->anim_end = FRAME_attack8;
    482 				}
    483 			}
    484 			else
    485 			{
    486 				if (level.time >= ent->pain_debounce_time)
    487 				{
    488 					gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
    489 					ent->pain_debounce_time = level.time + 1;
    490 				}
    491 				NoAmmoWeaponChange (ent);
    492 			}
    493 		}
    494 		else
    495 		{
    496 			if (ent->client->ps.gunframe == FRAME_IDLE_LAST)
    497 			{
    498 				ent->client->ps.gunframe = FRAME_IDLE_FIRST;
    499 				return;
    500 			}
    501 
    502 			if (pause_frames)
    503 			{
    504 				for (n = 0; pause_frames[n]; n++)
    505 				{
    506 					if (ent->client->ps.gunframe == pause_frames[n])
    507 					{
    508 						if (rand()&15)
    509 							return;
    510 					}
    511 				}
    512 			}
    513 
    514 			ent->client->ps.gunframe++;
    515 			return;
    516 		}
    517 	}
    518 
    519 	if (ent->client->weaponstate == WEAPON_FIRING)
    520 	{
    521 		for (n = 0; fire_frames[n]; n++)
    522 		{
    523 			if (ent->client->ps.gunframe == fire_frames[n])
    524 			{
    525 //ZOID
    526 				if (!CTFApplyStrengthSound(ent))
    527 //ZOID
    528 				if (ent->client->quad_framenum > level.framenum)
    529 					gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
    530 //ZOID
    531 				CTFApplyHasteSound(ent);
    532 //ZOID
    533 
    534 				fire (ent);
    535 				break;
    536 			}
    537 		}
    538 
    539 		if (!fire_frames[n])
    540 			ent->client->ps.gunframe++;
    541 
    542 		if (ent->client->ps.gunframe == FRAME_IDLE_FIRST+1)
    543 			ent->client->weaponstate = WEAPON_READY;
    544 	}
    545 }
    546 
    547 //ZOID
    548 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))
    549 {
    550 	int oldstate = ent->client->weaponstate;
    551 
    552 	Weapon_Generic2 (ent, FRAME_ACTIVATE_LAST, FRAME_FIRE_LAST, 
    553 		FRAME_IDLE_LAST, FRAME_DEACTIVATE_LAST, pause_frames, 
    554 		fire_frames, fire);
    555 
    556 	// run the weapon frame again if hasted
    557 	if (stricmp(ent->client->pers.weapon->pickup_name, "Grapple") == 0 &&
    558 		ent->client->weaponstate == WEAPON_FIRING)
    559 		return;
    560 
    561 	if ((CTFApplyHaste(ent) ||
    562 		(Q_stricmp(ent->client->pers.weapon->pickup_name, "Grapple") == 0 &&
    563 		ent->client->weaponstate != WEAPON_FIRING))
    564 		&& oldstate == ent->client->weaponstate) {
    565 		Weapon_Generic2 (ent, FRAME_ACTIVATE_LAST, FRAME_FIRE_LAST, 
    566 			FRAME_IDLE_LAST, FRAME_DEACTIVATE_LAST, pause_frames, 
    567 			fire_frames, fire);
    568 	}
    569 }
    570 //ZOID
    571 
    572 /*
    573 ======================================================================
    574 
    575 GRENADE
    576 
    577 ======================================================================
    578 */
    579 
    580 #define GRENADE_TIMER		3.0
    581 #define GRENADE_MINSPEED	400
    582 #define GRENADE_MAXSPEED	800
    583 
    584 void weapon_grenade_fire (edict_t *ent, qboolean held)
    585 {
    586 	vec3_t	offset;
    587 	vec3_t	forward, right;
    588 	vec3_t	start;
    589 	int		damage = 125;
    590 	float	timer;
    591 	int		speed;
    592 	float	radius;
    593 
    594 	radius = damage+40;
    595 	if (is_quad)
    596 		damage *= 4;
    597 
    598 	VectorSet(offset, 8, 8, ent->viewheight-8);
    599 	AngleVectors (ent->client->v_angle, forward, right, NULL);
    600 	P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
    601 
    602 	timer = ent->client->grenade_time - level.time;
    603 	speed = GRENADE_MINSPEED + (GRENADE_TIMER - timer) * ((GRENADE_MAXSPEED - GRENADE_MINSPEED) / GRENADE_TIMER);
    604 	fire_grenade2 (ent, start, forward, damage, speed, timer, radius, held);
    605 
    606 	if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
    607 		ent->client->pers.inventory[ent->client->ammo_index]--;
    608 
    609 	ent->client->grenade_time = level.time + 1.0;
    610 
    611 	if(ent->deadflag || ent->s.modelindex != 255) // VWep animations screw up corpses
    612 	{
    613 		return;
    614 	}
    615 
    616 	if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
    617 	{
    618 		ent->client->anim_priority = ANIM_ATTACK;
    619 		ent->s.frame = FRAME_crattak1-1;
    620 		ent->client->anim_end = FRAME_crattak3;
    621 	}
    622 	else
    623 	{
    624 		ent->client->anim_priority = ANIM_REVERSE;
    625 		ent->s.frame = FRAME_wave08;
    626 		ent->client->anim_end = FRAME_wave01;
    627 	}
    628 }
    629 
    630 void Weapon_Grenade (edict_t *ent)
    631 {
    632 	if ((ent->client->newweapon) && (ent->client->weaponstate == WEAPON_READY))
    633 	{
    634 		ChangeWeapon (ent);
    635 		return;
    636 	}
    637 
    638 	if (ent->client->weaponstate == WEAPON_ACTIVATING)
    639 	{
    640 		ent->client->weaponstate = WEAPON_READY;
    641 		ent->client->ps.gunframe = 16;
    642 		return;
    643 	}
    644 
    645 	if (ent->client->weaponstate == WEAPON_READY)
    646 	{
    647 		if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTON_ATTACK) )
    648 		{
    649 			ent->client->latched_buttons &= ~BUTTON_ATTACK;
    650 			if (ent->client->pers.inventory[ent->client->ammo_index])
    651 			{
    652 				ent->client->ps.gunframe = 1;
    653 				ent->client->weaponstate = WEAPON_FIRING;
    654 				ent->client->grenade_time = 0;
    655 			}
    656 			else
    657 			{
    658 				if (level.time >= ent->pain_debounce_time)
    659 				{
    660 					gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
    661 					ent->pain_debounce_time = level.time + 1;
    662 				}
    663 				NoAmmoWeaponChange (ent);
    664 			}
    665 			return;
    666 		}
    667 
    668 		if ((ent->client->ps.gunframe == 29) || (ent->client->ps.gunframe == 34) || (ent->client->ps.gunframe == 39) || (ent->client->ps.gunframe == 48))
    669 		{
    670 			if (rand()&15)
    671 				return;
    672 		}
    673 
    674 		if (++ent->client->ps.gunframe > 48)
    675 			ent->client->ps.gunframe = 16;
    676 		return;
    677 	}
    678 
    679 	if (ent->client->weaponstate == WEAPON_FIRING)
    680 	{
    681 		if (ent->client->ps.gunframe == 5)
    682 			gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/hgrena1b.wav"), 1, ATTN_NORM, 0);
    683 
    684 		if (ent->client->ps.gunframe == 11)
    685 		{
    686 			if (!ent->client->grenade_time)
    687 			{
    688 				ent->client->grenade_time = level.time + GRENADE_TIMER + 0.2;
    689 				ent->client->weapon_sound = gi.soundindex("weapons/hgrenc1b.wav");
    690 			}
    691 
    692 			// they waited too long, detonate it in their hand
    693 			if (!ent->client->grenade_blew_up && level.time >= ent->client->grenade_time)
    694 			{
    695 				ent->client->weapon_sound = 0;
    696 				weapon_grenade_fire (ent, true);
    697 				ent->client->grenade_blew_up = true;
    698 			}
    699 
    700 			if (ent->client->buttons & BUTTON_ATTACK)
    701 				return;
    702 
    703 			if (ent->client->grenade_blew_up)
    704 			{
    705 				if (level.time >= ent->client->grenade_time)
    706 				{
    707 					ent->client->ps.gunframe = 15;
    708 					ent->client->grenade_blew_up = false;
    709 				}
    710 				else
    711 				{
    712 					return;
    713 				}
    714 			}
    715 		}
    716 
    717 		if (ent->client->ps.gunframe == 12)
    718 		{
    719 			ent->client->weapon_sound = 0;
    720 			weapon_grenade_fire (ent, false);
    721 		}
    722 
    723 		if ((ent->client->ps.gunframe == 15) && (level.time < ent->client->grenade_time))
    724 			return;
    725 
    726 		ent->client->ps.gunframe++;
    727 
    728 		if (ent->client->ps.gunframe == 16)
    729 		{
    730 			ent->client->grenade_time = 0;
    731 			ent->client->weaponstate = WEAPON_READY;
    732 		}
    733 	}
    734 }
    735 
    736 /*
    737 ======================================================================
    738 
    739 GRENADE LAUNCHER
    740 
    741 ======================================================================
    742 */
    743 
    744 void weapon_grenadelauncher_fire (edict_t *ent)
    745 {
    746 	vec3_t	offset;
    747 	vec3_t	forward, right;
    748 	vec3_t	start;
    749 	int		damage = 120;
    750 	float	radius;
    751 
    752 	radius = damage+40;
    753 	if (is_quad)
    754 		damage *= 4;
    755 
    756 	VectorSet(offset, 8, 8, ent->viewheight-8);
    757 	AngleVectors (ent->client->v_angle, forward, right, NULL);
    758 	P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
    759 
    760 	VectorScale (forward, -2, ent->client->kick_origin);
    761 	ent->client->kick_angles[0] = -1;
    762 
    763 	fire_grenade (ent, start, forward, damage, 600, 2.5, radius);
    764 
    765 	gi.WriteByte (svc_muzzleflash);
    766 	gi.WriteShort (ent-g_edicts);
    767 	gi.WriteByte (MZ_GRENADE | is_silenced);
    768 	gi.multicast (ent->s.origin, MULTICAST_PVS);
    769 
    770 	ent->client->ps.gunframe++;
    771 
    772 	PlayerNoise(ent, start, PNOISE_WEAPON);
    773 
    774 	if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
    775 		ent->client->pers.inventory[ent->client->ammo_index]--;
    776 }
    777 
    778 void Weapon_GrenadeLauncher (edict_t *ent)
    779 {
    780 	static int	pause_frames[]	= {34, 51, 59, 0};
    781 	static int	fire_frames[]	= {6, 0};
    782 
    783 	Weapon_Generic (ent, 5, 16, 59, 64, pause_frames, fire_frames, weapon_grenadelauncher_fire);
    784 }
    785 
    786 /*
    787 ======================================================================
    788 
    789 ROCKET
    790 
    791 ======================================================================
    792 */
    793 
    794 void Weapon_RocketLauncher_Fire (edict_t *ent)
    795 {
    796 	vec3_t	offset, start;
    797 	vec3_t	forward, right;
    798 	int		damage;
    799 	float	damage_radius;
    800 	int		radius_damage;
    801 
    802 	damage = 100 + (int)(random() * 20.0);
    803 	radius_damage = 120;
    804 	damage_radius = 120;
    805 	if (is_quad)
    806 	{
    807 		damage *= 4;
    808 		radius_damage *= 4;
    809 	}
    810 
    811 	AngleVectors (ent->client->v_angle, forward, right, NULL);
    812 
    813 	VectorScale (forward, -2, ent->client->kick_origin);
    814 	ent->client->kick_angles[0] = -1;
    815 
    816 	VectorSet(offset, 8, 8, ent->viewheight-8);
    817 	P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
    818 	fire_rocket (ent, start, forward, damage, 650, damage_radius, radius_damage);
    819 
    820 	// send muzzle flash
    821 	gi.WriteByte (svc_muzzleflash);
    822 	gi.WriteShort (ent-g_edicts);
    823 	gi.WriteByte (MZ_ROCKET | is_silenced);
    824 	gi.multicast (ent->s.origin, MULTICAST_PVS);
    825 
    826 	ent->client->ps.gunframe++;
    827 
    828 	PlayerNoise(ent, start, PNOISE_WEAPON);
    829 
    830 	if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
    831 		ent->client->pers.inventory[ent->client->ammo_index]--;
    832 }
    833 
    834 void Weapon_RocketLauncher (edict_t *ent)
    835 {
    836 	static int	pause_frames[]	= {25, 33, 42, 50, 0};
    837 	static int	fire_frames[]	= {5, 0};
    838 
    839 	Weapon_Generic (ent, 4, 12, 50, 54, pause_frames, fire_frames, Weapon_RocketLauncher_Fire);
    840 }
    841 
    842 
    843 /*
    844 ======================================================================
    845 
    846 BLASTER / HYPERBLASTER
    847 
    848 ======================================================================
    849 */
    850 
    851 void Blaster_Fire (edict_t *ent, vec3_t g_offset, int damage, qboolean hyper, int effect)
    852 {
    853 	vec3_t	forward, right;
    854 	vec3_t	start;
    855 	vec3_t	offset;
    856 
    857 	if (is_quad)
    858 		damage *= 4;
    859 	AngleVectors (ent->client->v_angle, forward, right, NULL);
    860 	VectorSet(offset, 24, 8, ent->viewheight-8);
    861 	VectorAdd (offset, g_offset, offset);
    862 	P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
    863 
    864 	VectorScale (forward, -2, ent->client->kick_origin);
    865 	ent->client->kick_angles[0] = -1;
    866 
    867 	fire_blaster (ent, start, forward, damage, 1000, effect, hyper);
    868 
    869 	// send muzzle flash
    870 	gi.WriteByte (svc_muzzleflash);
    871 	gi.WriteShort (ent-g_edicts);
    872 	if (hyper)
    873 		gi.WriteByte (MZ_HYPERBLASTER | is_silenced);
    874 	else
    875 		gi.WriteByte (MZ_BLASTER | is_silenced);
    876 	gi.multicast (ent->s.origin, MULTICAST_PVS);
    877 
    878 	PlayerNoise(ent, start, PNOISE_WEAPON);
    879 }
    880 
    881 
    882 void Weapon_Blaster_Fire (edict_t *ent)
    883 {
    884 	int		damage;
    885 
    886 	if (deathmatch->value)
    887 		damage = 15;
    888 	else
    889 		damage = 10;
    890 	Blaster_Fire (ent, vec3_origin, damage, false, EF_BLASTER);
    891 	ent->client->ps.gunframe++;
    892 }
    893 
    894 void Weapon_Blaster (edict_t *ent)
    895 {
    896 	static int	pause_frames[]	= {19, 32, 0};
    897 	static int	fire_frames[]	= {5, 0};
    898 
    899 	Weapon_Generic (ent, 4, 8, 52, 55, pause_frames, fire_frames, Weapon_Blaster_Fire);
    900 }
    901 
    902 
    903 void Weapon_HyperBlaster_Fire (edict_t *ent)
    904 {
    905 	float	rotation;
    906 	vec3_t	offset;
    907 	int		effect;
    908 	int		damage;
    909 
    910 	ent->client->weapon_sound = gi.soundindex("weapons/hyprbl1a.wav");
    911 
    912 	if (!(ent->client->buttons & BUTTON_ATTACK))
    913 	{
    914 		ent->client->ps.gunframe++;
    915 	}
    916 	else
    917 	{
    918 		if (! ent->client->pers.inventory[ent->client->ammo_index] )
    919 		{
    920 			if (level.time >= ent->pain_debounce_time)
    921 			{
    922 				gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
    923 				ent->pain_debounce_time = level.time + 1;
    924 			}
    925 			NoAmmoWeaponChange (ent);
    926 		}
    927 		else
    928 		{
    929 			rotation = (ent->client->ps.gunframe - 5) * 2*M_PI/6;
    930 			offset[0] = -4 * sin(rotation);
    931 			offset[1] = 0;
    932 			offset[2] = 4 * cos(rotation);
    933 
    934 			if ((ent->client->ps.gunframe == 6) || (ent->client->ps.gunframe == 9))
    935 				effect = EF_HYPERBLASTER;
    936 			else
    937 				effect = 0;
    938 			if (deathmatch->value)
    939 				damage = 15;
    940 			else
    941 				damage = 20;
    942 			Blaster_Fire (ent, offset, damage, true, effect);
    943 			if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
    944 				ent->client->pers.inventory[ent->client->ammo_index]--;
    945 
    946 			ent->client->anim_priority = ANIM_ATTACK;
    947 			if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
    948 			{
    949 				ent->s.frame = FRAME_crattak1 - 1;
    950 				ent->client->anim_end = FRAME_crattak9;
    951 			}
    952 			else
    953 			{
    954 				ent->s.frame = FRAME_attack1 - 1;
    955 				ent->client->anim_end = FRAME_attack8;
    956 			}
    957 		}
    958 
    959 		ent->client->ps.gunframe++;
    960 		if (ent->client->ps.gunframe == 12 && ent->client->pers.inventory[ent->client->ammo_index])
    961 			ent->client->ps.gunframe = 6;
    962 	}
    963 
    964 	if (ent->client->ps.gunframe == 12)
    965 	{
    966 		gi.sound(ent, CHAN_AUTO, gi.soundindex("weapons/hyprbd1a.wav"), 1, ATTN_NORM, 0);
    967 		ent->client->weapon_sound = 0;
    968 	}
    969 
    970 }
    971 
    972 void Weapon_HyperBlaster (edict_t *ent)
    973 {
    974 	static int	pause_frames[]	= {0};
    975 	static int	fire_frames[]	= {6, 7, 8, 9, 10, 11, 0};
    976 
    977 	Weapon_Generic (ent, 5, 20, 49, 53, pause_frames, fire_frames, Weapon_HyperBlaster_Fire);
    978 }
    979 
    980 /*
    981 ======================================================================
    982 
    983 MACHINEGUN / CHAINGUN
    984 
    985 ======================================================================
    986 */
    987 
    988 void Machinegun_Fire (edict_t *ent)
    989 {
    990 	int	i;
    991 	vec3_t		start;
    992 	vec3_t		forward, right;
    993 	vec3_t		angles;
    994 	int			damage = 8;
    995 	int			kick = 2;
    996 	vec3_t		offset;
    997 
    998 	if (!(ent->client->buttons & BUTTON_ATTACK))
    999 	{
   1000 		ent->client->machinegun_shots = 0;
   1001 		ent->client->ps.gunframe++;
   1002 		return;
   1003 	}
   1004 
   1005 	if (ent->client->ps.gunframe == 5)
   1006 		ent->client->ps.gunframe = 4;
   1007 	else
   1008 		ent->client->ps.gunframe = 5;
   1009 
   1010 	if (ent->client->pers.inventory[ent->client->ammo_index] < 1)
   1011 	{
   1012 		ent->client->ps.gunframe = 6;
   1013 		if (level.time >= ent->pain_debounce_time)
   1014 		{
   1015 			gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
   1016 			ent->pain_debounce_time = level.time + 1;
   1017 		}
   1018 		NoAmmoWeaponChange (ent);
   1019 		return;
   1020 	}
   1021 
   1022 	if (is_quad)
   1023 	{
   1024 		damage *= 4;
   1025 		kick *= 4;
   1026 	}
   1027 
   1028 	for (i=1 ; i<3 ; i++)
   1029 	{
   1030 		ent->client->kick_origin[i] = crandom() * 0.35;
   1031 		ent->client->kick_angles[i] = crandom() * 0.7;
   1032 	}
   1033 	ent->client->kick_origin[0] = crandom() * 0.35;
   1034 	ent->client->kick_angles[0] = ent->client->machinegun_shots * -1.5;
   1035 
   1036 	// raise the gun as it is firing
   1037 	if (!deathmatch->value)
   1038 	{
   1039 		ent->client->machinegun_shots++;
   1040 		if (ent->client->machinegun_shots > 9)
   1041 			ent->client->machinegun_shots = 9;
   1042 	}
   1043 
   1044 	// get start / end positions
   1045 	VectorAdd (ent->client->v_angle, ent->client->kick_angles, angles);
   1046 	AngleVectors (angles, forward, right, NULL);
   1047 	VectorSet(offset, 0, 8, ent->viewheight-8);
   1048 	P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
   1049 	fire_bullet (ent, start, forward, damage, kick, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_MACHINEGUN);
   1050 
   1051 	gi.WriteByte (svc_muzzleflash);
   1052 	gi.WriteShort (ent-g_edicts);
   1053 	gi.WriteByte (MZ_MACHINEGUN | is_silenced);
   1054 	gi.multicast (ent->s.origin, MULTICAST_PVS);
   1055 
   1056 	PlayerNoise(ent, start, PNOISE_WEAPON);
   1057 
   1058 	if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
   1059 		ent->client->pers.inventory[ent->client->ammo_index]--;
   1060 
   1061 	ent->client->anim_priority = ANIM_ATTACK;
   1062 	if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
   1063 	{
   1064 		ent->s.frame = FRAME_crattak1 - (int) (random()+0.25);
   1065 		ent->client->anim_end = FRAME_crattak9;
   1066 	}
   1067 	else
   1068 	{
   1069 		ent->s.frame = FRAME_attack1 - (int) (random()+0.25);
   1070 		ent->client->anim_end = FRAME_attack8;
   1071 	}
   1072 }
   1073 
   1074 void Weapon_Machinegun (edict_t *ent)
   1075 {
   1076 	static int	pause_frames[]	= {23, 45, 0};
   1077 	static int	fire_frames[]	= {4, 5, 0};
   1078 
   1079 	Weapon_Generic (ent, 3, 5, 45, 49, pause_frames, fire_frames, Machinegun_Fire);
   1080 }
   1081 
   1082 void Chaingun_Fire (edict_t *ent)
   1083 {
   1084 	int			i;
   1085 	int			shots;
   1086 	vec3_t		start;
   1087 	vec3_t		forward, right, up;
   1088 	float		r, u;
   1089 	vec3_t		offset;
   1090 	int			damage;
   1091 	int			kick = 2;
   1092 
   1093 	if (deathmatch->value)
   1094 		damage = 6;
   1095 	else
   1096 		damage = 8;
   1097 
   1098 	if (ent->client->ps.gunframe == 5)
   1099 		gi.sound(ent, CHAN_AUTO, gi.soundindex("weapons/chngnu1a.wav"), 1, ATTN_IDLE, 0);
   1100 
   1101 	if ((ent->client->ps.gunframe == 14) && !(ent->client->buttons & BUTTON_ATTACK))
   1102 	{
   1103 		ent->client->ps.gunframe = 32;
   1104 		ent->client->weapon_sound = 0;
   1105 		return;
   1106 	}
   1107 	else if ((ent->client->ps.gunframe == 21) && (ent->client->buttons & BUTTON_ATTACK)
   1108 		&& ent->client->pers.inventory[ent->client->ammo_index])
   1109 	{
   1110 		ent->client->ps.gunframe = 15;
   1111 	}
   1112 	else
   1113 	{
   1114 		ent->client->ps.gunframe++;
   1115 	}
   1116 
   1117 	if (ent->client->ps.gunframe == 22)
   1118 	{
   1119 		ent->client->weapon_sound = 0;
   1120 		gi.sound(ent, CHAN_AUTO, gi.soundindex("weapons/chngnd1a.wav"), 1, ATTN_IDLE, 0);
   1121 	}
   1122 	else
   1123 	{
   1124 		ent->client->weapon_sound = gi.soundindex("weapons/chngnl1a.wav");
   1125 	}
   1126 
   1127 	ent->client->anim_priority = ANIM_ATTACK;
   1128 	if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
   1129 	{
   1130 		ent->s.frame = FRAME_crattak1 - (ent->client->ps.gunframe & 1);
   1131 		ent->client->anim_end = FRAME_crattak9;
   1132 	}
   1133 	else
   1134 	{
   1135 		ent->s.frame = FRAME_attack1 - (ent->client->ps.gunframe & 1);
   1136 		ent->client->anim_end = FRAME_attack8;
   1137 	}
   1138 
   1139 	if (ent->client->ps.gunframe <= 9)
   1140 		shots = 1;
   1141 	else if (ent->client->ps.gunframe <= 14)
   1142 	{
   1143 		if (ent->client->buttons & BUTTON_ATTACK)
   1144 			shots = 2;
   1145 		else
   1146 			shots = 1;
   1147 	}
   1148 	else
   1149 		shots = 3;
   1150 
   1151 	if (ent->client->pers.inventory[ent->client->ammo_index] < shots)
   1152 		shots = ent->client->pers.inventory[ent->client->ammo_index];
   1153 
   1154 	if (!shots)
   1155 	{
   1156 		if (level.time >= ent->pain_debounce_time)
   1157 		{
   1158 			gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
   1159 			ent->pain_debounce_time = level.time + 1;
   1160 		}
   1161 		NoAmmoWeaponChange (ent);
   1162 		return;
   1163 	}
   1164 
   1165 	if (is_quad)
   1166 	{
   1167 		damage *= 4;
   1168 		kick *= 4;
   1169 	}
   1170 
   1171 	for (i=0 ; i<3 ; i++)
   1172 	{
   1173 		ent->client->kick_origin[i] = crandom() * 0.35;
   1174 		ent->client->kick_angles[i] = crandom() * 0.7;
   1175 	}
   1176 
   1177 	for (i=0 ; i<shots ; i++)
   1178 	{
   1179 		// get start / end positions
   1180 		AngleVectors (ent->client->v_angle, forward, right, up);
   1181 		r = 7 + crandom()*4;
   1182 		u = crandom()*4;
   1183 		VectorSet(offset, 0, r, u + ent->viewheight-8);
   1184 		P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
   1185 
   1186 		fire_bullet (ent, start, forward, damage, kick, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_CHAINGUN);
   1187 	}
   1188 
   1189 	// send muzzle flash
   1190 	gi.WriteByte (svc_muzzleflash);
   1191 	gi.WriteShort (ent-g_edicts);
   1192 	gi.WriteByte ((MZ_CHAINGUN1 + shots - 1) | is_silenced);
   1193 	gi.multicast (ent->s.origin, MULTICAST_PVS);
   1194 
   1195 	PlayerNoise(ent, start, PNOISE_WEAPON);
   1196 
   1197 	if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
   1198 		ent->client->pers.inventory[ent->client->ammo_index] -= shots;
   1199 }
   1200 
   1201 
   1202 void Weapon_Chaingun (edict_t *ent)
   1203 {
   1204 	static int	pause_frames[]	= {38, 43, 51, 61, 0};
   1205 	static int	fire_frames[]	= {5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 0};
   1206 
   1207 	Weapon_Generic (ent, 4, 31, 61, 64, pause_frames, fire_frames, Chaingun_Fire);
   1208 }
   1209 
   1210 
   1211 /*
   1212 ======================================================================
   1213 
   1214 SHOTGUN / SUPERSHOTGUN
   1215 
   1216 ======================================================================
   1217 */
   1218 
   1219 void weapon_shotgun_fire (edict_t *ent)
   1220 {
   1221 	vec3_t		start;
   1222 	vec3_t		forward, right;
   1223 	vec3_t		offset;
   1224 	int			damage = 4;
   1225 	int			kick = 8;
   1226 
   1227 	if (ent->client->ps.gunframe == 9)
   1228 	{
   1229 		ent->client->ps.gunframe++;
   1230 		return;
   1231 	}
   1232 
   1233 	AngleVectors (ent->client->v_angle, forward, right, NULL);
   1234 
   1235 	VectorScale (forward, -2, ent->client->kick_origin);
   1236 	ent->client->kick_angles[0] = -2;
   1237 
   1238 	VectorSet(offset, 0, 8,  ent->viewheight-8);
   1239 	P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
   1240 
   1241 	if (is_quad)
   1242 	{
   1243 		damage *= 4;
   1244 		kick *= 4;
   1245 	}
   1246 
   1247 	if (deathmatch->value)
   1248 		fire_shotgun (ent, start, forward, damage, kick, 500, 500, DEFAULT_DEATHMATCH_SHOTGUN_COUNT, MOD_SHOTGUN);
   1249 	else
   1250 		fire_shotgun (ent, start, forward, damage, kick, 500, 500, DEFAULT_SHOTGUN_COUNT, MOD_SHOTGUN);
   1251 
   1252 	// send muzzle flash
   1253 	gi.WriteByte (svc_muzzleflash);
   1254 	gi.WriteShort (ent-g_edicts);
   1255 	gi.WriteByte (MZ_SHOTGUN | is_silenced);
   1256 	gi.multicast (ent->s.origin, MULTICAST_PVS);
   1257 
   1258 	ent->client->ps.gunframe++;
   1259 	PlayerNoise(ent, start, PNOISE_WEAPON);
   1260 
   1261 	if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
   1262 		ent->client->pers.inventory[ent->client->ammo_index]--;
   1263 }
   1264 
   1265 void Weapon_Shotgun (edict_t *ent)
   1266 {
   1267 	static int	pause_frames[]	= {22, 28, 34, 0};
   1268 	static int	fire_frames[]	= {8, 9, 0};
   1269 
   1270 	Weapon_Generic (ent, 7, 18, 36, 39, pause_frames, fire_frames, weapon_shotgun_fire);
   1271 }
   1272 
   1273 
   1274 void weapon_supershotgun_fire (edict_t *ent)
   1275 {
   1276 	vec3_t		start;
   1277 	vec3_t		forward, right;
   1278 	vec3_t		offset;
   1279 	vec3_t		v;
   1280 	int			damage = 6;
   1281 	int			kick = 12;
   1282 
   1283 	AngleVectors (ent->client->v_angle, forward, right, NULL);
   1284 
   1285 	VectorScale (forward, -2, ent->client->kick_origin);
   1286 	ent->client->kick_angles[0] = -2;
   1287 
   1288 	VectorSet(offset, 0, 8,  ent->viewheight-8);
   1289 	P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
   1290 
   1291 	if (is_quad)
   1292 	{
   1293 		damage *= 4;
   1294 		kick *= 4;
   1295 	}
   1296 
   1297 	v[PITCH] = ent->client->v_angle[PITCH];
   1298 	v[YAW]   = ent->client->v_angle[YAW] - 5;
   1299 	v[ROLL]  = ent->client->v_angle[ROLL];
   1300 	AngleVectors (v, forward, NULL, NULL);
   1301 	fire_shotgun (ent, start, forward, damage, kick, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SSHOTGUN_COUNT/2, MOD_SSHOTGUN);
   1302 	v[YAW]   = ent->client->v_angle[YAW] + 5;
   1303 	AngleVectors (v, forward, NULL, NULL);
   1304 	fire_shotgun (ent, start, forward, damage, kick, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SSHOTGUN_COUNT/2, MOD_SSHOTGUN);
   1305 
   1306 	// send muzzle flash
   1307 	gi.WriteByte (svc_muzzleflash);
   1308 	gi.WriteShort (ent-g_edicts);
   1309 	gi.WriteByte (MZ_SSHOTGUN | is_silenced);
   1310 	gi.multicast (ent->s.origin, MULTICAST_PVS);
   1311 
   1312 	ent->client->ps.gunframe++;
   1313 	PlayerNoise(ent, start, PNOISE_WEAPON);
   1314 
   1315 	if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
   1316 		ent->client->pers.inventory[ent->client->ammo_index] -= 2;
   1317 }
   1318 
   1319 void Weapon_SuperShotgun (edict_t *ent)
   1320 {
   1321 	static int	pause_frames[]	= {29, 42, 57, 0};
   1322 	static int	fire_frames[]	= {7, 0};
   1323 
   1324 	Weapon_Generic (ent, 6, 17, 57, 61, pause_frames, fire_frames, weapon_supershotgun_fire);
   1325 }
   1326 
   1327 
   1328 
   1329 /*
   1330 ======================================================================
   1331 
   1332 RAILGUN
   1333 
   1334 ======================================================================
   1335 */
   1336 
   1337 void weapon_railgun_fire (edict_t *ent)
   1338 {
   1339 	vec3_t		start;
   1340 	vec3_t		forward, right;
   1341 	vec3_t		offset;
   1342 	int			damage;
   1343 	int			kick;
   1344 
   1345 	if (deathmatch->value)
   1346 	{	// normal damage is too extreme in dm
   1347 		damage = 100;
   1348 		kick = 200;
   1349 	}
   1350 	else
   1351 	{
   1352 		damage = 150;
   1353 		kick = 250;
   1354 	}
   1355 
   1356 	if (is_quad)
   1357 	{
   1358 		damage *= 4;
   1359 		kick *= 4;
   1360 	}
   1361 
   1362 	AngleVectors (ent->client->v_angle, forward, right, NULL);
   1363 
   1364 	VectorScale (forward, -3, ent->client->kick_origin);
   1365 	ent->client->kick_angles[0] = -3;
   1366 
   1367 	VectorSet(offset, 0, 7,  ent->viewheight-8);
   1368 	P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
   1369 	fire_rail (ent, start, forward, damage, kick);
   1370 
   1371 	// send muzzle flash
   1372 	gi.WriteByte (svc_muzzleflash);
   1373 	gi.WriteShort (ent-g_edicts);
   1374 	gi.WriteByte (MZ_RAILGUN | is_silenced);
   1375 	gi.multicast (ent->s.origin, MULTICAST_PVS);
   1376 
   1377 	ent->client->ps.gunframe++;
   1378 	PlayerNoise(ent, start, PNOISE_WEAPON);
   1379 
   1380 	if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
   1381 		ent->client->pers.inventory[ent->client->ammo_index]--;
   1382 }
   1383 
   1384 
   1385 void Weapon_Railgun (edict_t *ent)
   1386 {
   1387 	static int	pause_frames[]	= {56, 0};
   1388 	static int	fire_frames[]	= {4, 0};
   1389 
   1390 	Weapon_Generic (ent, 3, 18, 56, 61, pause_frames, fire_frames, weapon_railgun_fire);
   1391 }
   1392 
   1393 
   1394 /*
   1395 ======================================================================
   1396 
   1397 BFG10K
   1398 
   1399 ======================================================================
   1400 */
   1401 
   1402 void weapon_bfg_fire (edict_t *ent)
   1403 {
   1404 	vec3_t	offset, start;
   1405 	vec3_t	forward, right;
   1406 	int		damage;
   1407 	float	damage_radius = 1000;
   1408 
   1409 	if (deathmatch->value)
   1410 		damage = 200;
   1411 	else
   1412 		damage = 500;
   1413 
   1414 	if (ent->client->ps.gunframe == 9)
   1415 	{
   1416 		// send muzzle flash
   1417 		gi.WriteByte (svc_muzzleflash);
   1418 		gi.WriteShort (ent-g_edicts);
   1419 		gi.WriteByte (MZ_BFG | is_silenced);
   1420 		gi.multicast (ent->s.origin, MULTICAST_PVS);
   1421 
   1422 		ent->client->ps.gunframe++;
   1423 
   1424 		PlayerNoise(ent, ent->s.origin, PNOISE_WEAPON);
   1425 		return;
   1426 	}
   1427 
   1428 	// cells can go down during windup (from power armor hits), so
   1429 	// check again and abort firing if we don't have enough now
   1430 	if (ent->client->pers.inventory[ent->client->ammo_index] < 50)
   1431 	{
   1432 		ent->client->ps.gunframe++;
   1433 		return;
   1434 	}
   1435 
   1436 	if (is_quad)
   1437 		damage *= 4;
   1438 
   1439 	AngleVectors (ent->client->v_angle, forward, right, NULL);
   1440 
   1441 	VectorScale (forward, -2, ent->client->kick_origin);
   1442 
   1443 	// make a big pitch kick with an inverse fall
   1444 	ent->client->v_dmg_pitch = -40;
   1445 	ent->client->v_dmg_roll = crandom()*8;
   1446 	ent->client->v_dmg_time = level.time + DAMAGE_TIME;
   1447 
   1448 	VectorSet(offset, 8, 8, ent->viewheight-8);
   1449 	P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
   1450 	fire_bfg (ent, start, forward, damage, 400, damage_radius);
   1451 
   1452 	ent->client->ps.gunframe++;
   1453 
   1454 	PlayerNoise(ent, start, PNOISE_WEAPON);
   1455 
   1456 	if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
   1457 		ent->client->pers.inventory[ent->client->ammo_index] -= 50;
   1458 }
   1459 
   1460 void Weapon_BFG (edict_t *ent)
   1461 {
   1462 	static int	pause_frames[]	= {39, 45, 50, 55, 0};
   1463 	static int	fire_frames[]	= {9, 17, 0};
   1464 
   1465 	Weapon_Generic (ent, 8, 32, 55, 58, pause_frames, fire_frames, weapon_bfg_fire);
   1466 }
   1467 
   1468 
   1469 //======================================================================