Semi or Burst fire

From Valve Developer Community
Jump to: navigation, search



This is a simple tutorial about adding semi-auto or burst fire mode to a weapon.

The weapon_pistol.cpp will be used as the basis of this tutorial. The extra lines to add are marked in bold.

First define a constant that will hold the maximum number of bullets to be fired with one trigger pull (mouse click). (Set it to 1 for semi-auto, to 3 for a 3 shot burst, etc.)

#define	PISTOL_ACCURACY_SHOT_PENALTY_TIME		0.2f	// Applied amount of time each shot adds to the time we must recover from
#define	PISTOL_ACCURACY_MAXIMUM_PENALTY_TIME	1.5f	// Maximum penalty to deal out
#define	MAXBURST	1


At the declaration of the pistol class, add the following lines. (You can copy-paste these sections over the original part if you would like to.)

private:
CNetworkVar( float,	m_flSoonestPrimaryAttack );
CNetworkVar( float,	m_flLastAttackTime );
CNetworkVar( float,	m_flAccuracyPenalty );
CNetworkVar( int,	m_nNumShotsFired );
CNetworkVar( int,	m_iSemi );


private:
CWeaponPistol( const CWeaponPistol & );
};
IMPLEMENT_NETWORKCLASS_ALIASED( WeaponPistol, DT_WeaponPistol )

BEGIN_NETWORK_TABLE( CWeaponPistol, DT_WeaponPistol )
#ifdef CLIENT_DLL
RecvPropTime( RECVINFO( m_flSoonestPrimaryAttack ) ),
RecvPropTime( RECVINFO( m_flLastAttackTime ) ),
RecvPropFloat( RECVINFO( m_flAccuracyPenalty ) ),
RecvPropInt( RECVINFO( m_nNumShotsFired ) ),
RecvPropInt( RECVINFO( m_iSemi ) ),
#else
SendPropTime( SENDINFO( m_flSoonestPrimaryAttack ) ),
SendPropTime( SENDINFO( m_flLastAttackTime ) ),
SendPropFloat( SENDINFO( m_flAccuracyPenalty ) ),
SendPropInt( SENDINFO( m_nNumShotsFired ) ),
SendPropInt( SENDINFO( m_iSemi ) ),
#endif
END_NETWORK_TABLE()
BEGIN_PREDICTION_DATA( CWeaponPistol )
DEFINE_PRED_FIELD( m_iSemi, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
END_PREDICTION_DATA()

This will create the m_iSemi variable that will hold the MAXBURST constant. The m_iSemi should be passed to the client as it is a client specific variable. Meaning each client using the weapon has a different value for this variable.

Set up the new variable in the constructor with this line:

CWeaponPistol::CWeaponPistol( void )
{
m_flSoonestPrimaryAttack = gpGlobals->curtime;
m_flAccuracyPenalty = 0.0f;
m_fMinRange1		= 24;
m_fMaxRange1		= 1500;
m_fMinRange2		= 24;
m_fMaxRange2		= 200;
m_bFiresUnderwater	= true;
m_iSemi=MAXBURST; 
}

Time to add the actual code. Look up the ItemPostFrame procedure and add these lines:

CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
if ( !(pOwner->m_nButtons & IN_ATTACK) )
  m_iSemi = MAXBURST;

This sets the m_iSemi variable to the MAXBURST constant. It does that because the fire button is released, therefore next time you shoot you will be allowed to fire all the bullets away that tha maxburst grants.

To finish up the modification, add these bold lines into the PrimaryAttack procedure:

if (m_iSemi<=0)
  return;
m_iSemi--;
BaseClass::PrimaryAttack();

If the m_iSemi is 0 thet means you expanded all your shots in the current burst, therefore exit the procedure BEFORE it calls the actual attack procedure. So you DON'T fire. Otherwise it decreases the number of bullets to fire with the current burst and the gun WILL fire 1 bullet.

This will deduct each shot fired from the maximum number of allowed shots as long as the trigger is pushed in, and will cease when the gun has fired the defined number of bullets. The player would need to release the trigger and push it again for an other shot/burst.

To do: Write a section on selective fire. To make a gun selective fire, one would need to set up an extra bool variable (like m_bSemi) and trigger it with something (like alt fire), and then replace the m_iSemi--; line with this: if ( m_bSemi ) {m_iSemi--;}.