Dual Pistols (CSS Style)

From Valve Developer Community
Revision as of 01:56, 17 May 2007 by TheRealJman (talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

This article will explain how to implement Counter-Strike Source style Dual Pistols into your Single or Multiplayer Half-Life 2 Mod.


Getting Started

First off you should copy weapon_pistol.cpp from ("game_shared/hl2mp" - For Users Editing Multiplayer Source) or ("dlls/hl2_dll" - For Users Editing Singleplayer Source) and paste it in the ("game_shared/hl2mp" - For Users Editing Multiplayer Source) or ("dlls/hl2_dll" - For Users Editing Singleplayer Source) folder.

Right after that, rename the from "weapon_pistol" to "weapon_dualies" and open the file in whatever C++ program you are using. After this, press Ctrl+F and then Replace All instances of the word "Pistol" with the word "Dualies." After this, you should notice that this messed up your act table, so replace:

#ifndef CLIENT_DLL
acttable_t CWeaponDualies::m_acttable[] = 
{
	{ ACT_HL2MP_IDLE,					ACT_HL2MP_IDLE_Dualies,					false },
	{ ACT_HL2MP_RUN,					ACT_HL2MP_RUN_Dualies,					false },
	{ ACT_HL2MP_IDLE_CROUCH,			ACT_HL2MP_IDLE_CROUCH_Dualies,			false },
	{ ACT_HL2MP_WALK_CROUCH,			ACT_HL2MP_WALK_CROUCH_Dualies,			false },
	{ ACT_HL2MP_GESTURE_RANGE_ATTACK,	ACT_HL2MP_GESTURE_RANGE_ATTACK_Dualies,	false },
	{ ACT_HL2MP_GESTURE_RELOAD,			ACT_HL2MP_GESTURE_RELOAD_Dualies,		false },
	{ ACT_HL2MP_JUMP,					ACT_HL2MP_JUMP_Dualies,					false },
	{ ACT_RANGE_ATTACK1,				ACT_RANGE_ATTACK_Dualies,				false },
};

With:

#ifndef CLIENT_DLL
acttable_t CWeaponDualies::m_acttable[] = 
{
	{ ACT_HL2MP_IDLE,					ACT_HL2MP_IDLE_PISTOL,					false },
	{ ACT_HL2MP_RUN,					ACT_HL2MP_RUN_PISTOL,					false },
	{ ACT_HL2MP_IDLE_CROUCH,			ACT_HL2MP_IDLE_CROUCH_PISTOL,			false },
	{ ACT_HL2MP_WALK_CROUCH,			ACT_HL2MP_WALK_CROUCH_PISTOL,			false },
	{ ACT_HL2MP_GESTURE_RANGE_ATTACK,	ACT_HL2MP_GESTURE_RANGE_ATTACK_PISTOL,	false },
	{ ACT_HL2MP_GESTURE_RELOAD,			ACT_HL2MP_GESTURE_RELOAD_PISTOL,		false },
	{ ACT_HL2MP_JUMP,					ACT_HL2MP_JUMP_PISTOL,					false },
	{ ACT_RANGE_ATTACK1,				ACT_RANGE_ATTACK_PISTOL,				false },
};

That should make the gun its own gun for now, but we still need to add it in the stubs file.

Adding it as a Stub

So, as I said, we need to add it as a stub. So now, go to "c_weapon__stubs_hl2.cpp" and add:

STUB_WEAPON_CLASS( weapon_dualies, WeaponDualies, C_BaseHLCombatWeapon );

Where the pistol is located.

Now you just need to drag the "weapon_dualies.cpp" file into both the client and server solutions. Where in them? ("Source Files/hl2mp/Weapons")

Now the gun is its own gun and works in game.

Coding the Flip

Now we must code the flip.

Go into "basecombatweapon_shared.cpp" and add a new function:

//-----------------------------------------------------------------------------
// Purpose: Dualies Firing
//-----------------------------------------------------------------------------
void CBaseCombatWeapon::RightDualiesAttack( void )
{
	// If my clip is empty (and I use clips) start reload
	if ( UsesClipsForAmmo1() && !m_iClip1 ) 
	{
		Reload();
		return;
	}

	// Only the player fires this way so we can cast
	CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );

	if (!pPlayer)
	{
		return;
	}

	// MUST call sound before removing a round from the clip of a CMachineGun
	WeaponSound(SINGLE);

	pPlayer->DoMuzzleFlash();

	SendWeaponAnim( GetSecondaryAttackActivity() );

	// player "shoot" animation
	pPlayer->SetAnimation( PLAYER_ATTACK1 );

	FireBulletsInfo_t info;
	info.m_vecSrc	 = pPlayer->Weapon_ShootPosition( );
	
	info.m_vecDirShooting = pPlayer->GetAutoaimVector( AUTOAIM_SCALE_DEFAULT );

	// To make the firing framerate independent, we may have to fire more than one bullet here on low-framerate systems, 
	// especially if the weapon we're firing has a really fast rate of fire.
	info.m_iShots = 0;
	float fireRate = GetFireRate();

	while ( m_flNextPrimaryAttack <= gpGlobals->curtime )
	{
		m_flNextPrimaryAttack = m_flNextPrimaryAttack + fireRate;
		info.m_iShots++;
		if ( !fireRate )
			break;
	}

	// Make sure we don't fire more than the amount in the clip
	if ( UsesClipsForAmmo1() )
	{
		info.m_iShots = min( info.m_iShots, m_iClip1 );
		m_iClip1 -= info.m_iShots;
	}
	else
	{
		info.m_iShots = min( info.m_iShots, pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) );
		pPlayer->RemoveAmmo( info.m_iShots, m_iPrimaryAmmoType );
	}

	info.m_flDistance = MAX_TRACE_LENGTH;
	info.m_iAmmoType = m_iPrimaryAmmoType;
	info.m_iTracerFreq = 2;

#if !defined( CLIENT_DLL )
	// Fire the bullets
	info.m_vecSpread = pPlayer->GetAttackSpread( this );
#else
	//!!!HACKHACK - what does the client want this function for? 
	info.m_vecSpread = GetActiveWeapon()->GetBulletSpread();
#endif // CLIENT_DLL

	pPlayer->FireBullets( info );

	if (!m_iClip1 && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
	{
		// HEV suit - indicate out of ammo condition
		pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0); 
	}

	//Add our view kick in
	AddViewKick();
}

Since that's done, go back to "weapon_dualies.cpp" and go down to:

class CWeaponDualies : public CBaseHL2MPCombatWeapon

and find:

	void	Precache( void );
	void	ItemPostFrame( void );
	void	ItemPreFrame( void );
	void	ItemBusyFrame( void );
	void	PrimaryAttack( void );
	void	AddViewKick( void );
	void	DryFire( void );

and change that to:

	void	Precache( void );
	void	ItemPostFrame( void );
	void	ItemPreFrame( void );
	void	ItemBusyFrame( void );
	void	PrimaryAttack( void );
	void	AddViewKick( void );
	void	DryFire( void );
	void	PostFireStuff( void );

then go to:

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

and change that to:

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

	bool Flip;

Now you have everything pretty well set up. Finally, go down to:

void CWeaponDualies::PrimaryAttack( void )

And make the function look like this:

void CWeaponDualies::PrimaryAttack( void )
{
	if ( ( gpGlobals->curtime - m_flLastAttackTime ) > 0.5f )
	{
		m_nNumShotsFired = 0;
	}
	else
	{
		m_nNumShotsFired++;
	}

	m_flLastAttackTime = gpGlobals->curtime;
	m_flSoonestPrimaryAttack = gpGlobals->curtime + DUALIES_FASTEST_REFIRE_TIME;

	//Flipping Code -Jman
	if ( Flip == true )
	{
		BaseClass::PrimaryAttack();
		Flip = false;
		PostFireStuff();
	}
	else
	{
		BaseClass::RightDualiesAttack();
		Flip = true;
		PostFireStuff();
	}
}

//Post Fire things.
void CWeaponPistol::PostFireStuff( void )
{
	CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
	if( pOwner )
	{
		pOwner->ViewPunchReset();
	}

	// Add an accuracy penalty which can move past our maximum penalty time if we're really spastic
	m_flAccuracyPenalty += PISTOL_ACCURACY_SHOT_PENALTY_TIME;
}

The Text Script

Now that the code's all done, you just need a text script for it, so copy "weapon_pistol.txt" in "YourModName/scripts," paste it, and rename it "weapon_dualies.txt" Now just edit the file to have a new bucket position (Inventory Position) and get a Counter Strike Source dual pistols model with the hands texture and the such, put it in your mod, and edit "weapon_dualies.txt" to use that model's name as the viewmodel.

Now when you use the guns in game, you should have a gun that swaps left and right guns each shot.