Full Holster Sequence (HL2MP)

From Valve Developer Community
Jump to navigation Jump to search
English (en)Русский (ru)Translate (Translate)

This tutorial provides insight on giving an active weapon time to play its holster animation before showing a newly chosen weapon in the Half-Life 2 Deathmatch SDK. A new game system is introduced to the code so the switch is predicted on the client (which would only be needed in multiplayer). The code also prevents itself from being active when a player is being equipped when spawning.

Note.pngNote:Not all of the weapons have holster animations and need to be reworked to fully implement this concept.
Note.pngNote:For single player you need miss step in hl2mp_player.cpp

c_basecombatcharacter.h

Add the following to the client combat character class:

friend class C_ShowWeapon; // This allows CShowWeapon to access whatever it needs to update for the character

basecombatcharacter.h

Add the following to the server combat character class:

friend class CShowWeapon; // This allows CShowWeapon to access whatever it needs to update for the character

player.h

Add the following to the player class:

public:
	bool IsSpawning() { return m_bSpawning; }
protected:
	bool m_bSpawning;

hl2mp_player.cpp

Add the following before the calls to GiveNamedItem in CHL2MP_Player::GiveDefaultItems:

m_bSpawning = true;

Add the following after the calls to GiveNamedItem in CHL2MP_Player::GiveDefaultItems:

m_bSpawning = false;

basecombatcharacter_shared.cpp

Remove the following from CBaseCombatCharacter::Weapon_Switch:

	m_hActiveWeapon = pWeapon;

Add the following to CBaseCombatCharacter::Weapon_CanSwitchTo:

	if (pVehicle && !pPlayer->UsingStandardWeaponsInVehicle())
		return false;
#ifndef CLIENT_DLL
	if(pPlayer->IsSpawning())
		return false;
#endif
}

basecombatweapon_shared.cpp

Add the following system to this file to act as the timer for triggering the weapon deploy:

#ifdef CLIENT_DLL
#define CShowWeapon C_ShowWeapon
#endif
class CShowWeapon : public CAutoGameSystemPerFrame
{
public:
	bool Init()
	{
		ClearShowWeapon();
		return true;
	}
	void FrameUpdatePreEntityThink()
	{
		if(m_pWeapon&&m_flTime<gpGlobals->curtime)
		{
			ShowWeapon();
		}
	}
	void Update(float frametime)
	{
		FrameUpdatePreEntityThink(); // This adds compatibility to this gamesystem on the client
	}
	void SetShowWeapon(CBaseCombatWeapon *pWeapon, int iActivity, float delta)
	{
		m_pWeapon = pWeapon;
		m_iActivity = iActivity;
		if(delta==0)
		{
			ShowWeapon();
		}
		else
		{
			m_flTime = gpGlobals->curtime + delta;
		}
	}
	void ClearShowWeapon()
	{
		m_pWeapon = NULL;
	}
private:
	void ShowWeapon()
	{
		Assert(m_pWeapon);
		m_pWeapon->SetWeaponVisible(true);
		if(m_pWeapon->GetOwner())
		{
			CBaseCombatWeapon *pLastWeapon = m_pWeapon->GetOwner()->GetActiveWeapon();
			m_pWeapon->GetOwner()->m_hActiveWeapon = m_pWeapon;
			CBasePlayer *pOwner = ToBasePlayer( m_pWeapon->GetOwner() );
			if ( pOwner )
			{
				m_pWeapon->SetViewModel();
				m_pWeapon->SendWeaponAnim( m_iActivity );

				pOwner->SetNextAttack( gpGlobals->curtime + m_pWeapon->SequenceDuration() );
				
				if ( pLastWeapon && pOwner->Weapon_ShouldSetLast( pLastWeapon, m_pWeapon ) )
				{
					pOwner->Weapon_SetLast( pLastWeapon->GetLastWeapon() );
				}

				CBaseViewModel *pViewModel = pOwner->GetViewModel();
				Assert( pViewModel );
				if ( pViewModel )
					pViewModel->RemoveEffects( EF_NODRAW );
				pOwner->ResetAutoaim( );
			}
		}

		// Can't shoot again until we've finished deploying
		m_pWeapon->m_flNextSecondaryAttack = m_pWeapon->m_flNextPrimaryAttack	= gpGlobals->curtime + m_pWeapon->SequenceDuration();

		ClearShowWeapon();
	}
	CBaseCombatWeapon *m_pWeapon;
	int m_iActivity;
	float m_flTime;
};
static CShowWeapon g_ShowWeapon;

Modify DefaultDeploy so it utilizes the new system:

bool CBaseCombatWeapon::DefaultDeploy( char *szViewModel, char *szWeaponModel, int iActivity, char *szAnimExt )
{
	// Msg( "deploy %s at %f\n", GetClassname(), gpGlobals->curtime );

	// Weapons that don't autoswitch away when they run out of ammo 
	// can still be deployed when they have no ammo.
	if ( !HasAnyAmmo() && AllowsAutoSwitchFrom() )
		return false;

	float flSequenceDuration = 0.0f;
	if(GetOwner())
	{
		if ( !GetOwner()->IsAlive() )
			return false;
		CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
		if ( pOwner )
		{
			pOwner->SetAnimationExtension( szAnimExt );
		}
		CBaseCombatWeapon *pActive = GetOwner()->GetActiveWeapon();
		if ( pActive && pActive->GetActivity() == ACT_VM_HOLSTER )
		{
			flSequenceDuration = pActive->SequenceDuration();
		}
	}
	g_ShowWeapon.SetShowWeapon( this, iActivity, flSequenceDuration );

#ifndef CLIENT_DLL
	// Cancel any pending hide events
	g_EventQueue.CancelEventOn( this, "HideWeapon" );
#endif

	return true;
}