Dual Pistols (CSS Style)
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" which is in "game_shared" and add this new function after "CBaseCombatWeapon::PrimaryAttack"
//-----------------------------------------------------------------------------
// Purpose: Dualies Firing
//-----------------------------------------------------------------------------
void CBaseCombatWeapon::RightDualiesAttack()
{
// 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 ItemPostFrame();
void ItemPreFrame();
void ItemBusyFrame();
void PrimaryAttack();
void AddViewKick();
void DryFire();
and change that to:
void Precache();
void ItemPostFrame();
void ItemPreFrame();
void ItemBusyFrame();
void PrimaryAttack();
void AddViewKick();
void DryFire();
void PostFireStuff();
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 we need to define our new RightDualiesAttack function in basecombatweapon_shared.h, which in your source is located in: src\game_shared\basecombatweapon_shared.h
Scroll down until you reach
// Weapon firing
virtual void PrimaryAttack(); // do "+ATTACK"
virtual void SecondaryAttack() { return; } // do "+ATTACK2"
And change that to
// Weapon firing
virtual void PrimaryAttack(); // do "+ATTACK"
virtual void RightDualiesAttack(); // do right gun shot"
virtual void SecondaryAttack() { return; } // do "+ATTACK2"
Now you have everything pretty well set up. Finally, go down to:
void CWeaponDualies::PrimaryAttack()
And make the function look like this:
void CWeaponDualies::PrimaryAttack()
{
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 CWeaponDualies::PostFireStuff()
{
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 += DUALIES_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.