Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

p_weapon.c (34565B)


      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, ent->s.origin, 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 //======================================================================