Grapple Hook

From Valve Developer Community
Jump to: navigation, search
Underlinked - Logo.png
This article needs more Wikipedia icon links to other articles to help Wikipedia icon integrate it into the encyclopedia. Please help improve this article by adding links Wikipedia icon that are relevant to the context within the existing text.
January 2024
English (en)Deutsch (de)Português do Brasil (pt-br)Translate (Translate)

This is how we made the grappling hook found in the Obsidian Conflict Mod. The code was written by Hyperjag3 and Skidz. You can find an improved version on YouTube created by user MaestroFenix, and you can grab the code from GitHub to try it out in your own mod.

If somebody would like to take this apart and write some documentation on how it works, that would be nice. I just don't have the time to do it unfortunately.

The weapon is designed for multiplayer, but should work fine in a single player mod once you swap out references to hl2mp. It uses the crossbow model.

NOTE: If you include this code in your mod in some way, please give the Obsidian Conflict team credits in your mod's readme file. Thank you :)

This code was uploaded on 2009, and outside of leaving the part of making the client version of the code, it contains some bugs like moving on all the directions the player or not pulling always. However, a new version has been made:

  • Replaced the spring method for vectors. It gives higher control to the player instead of just flying on all the directions.
  • Added a rappel mode, so now players can descend/brake their falling speed safely.
  • The grapple hook can fire now the "hook" faster (this could be done just with a viewmodel that has a fast reload, but it has been fixed at the code).
  • Replaced the bolt and rope for a beam, to make it more pretty.
  • The "hook" now can be seen right both in first and thirdperson. Before, due the way it was made, it looked fine at thirdperson, but it was moved at the viewmodel.
  • Uses the Gravity Gun as model.

Apart of giving credits to the Obsidian Conflict Team, you also have to give credits to Maestro Fénix, who is the creator of this improved version.

You can see a video demostration of it here.

Grapple Hook 2009

weapon_grapple.h

#ifndef WEAPON_GRAPPLE_H
#define WEAPON_GRAPPLE_H

#ifdef _WIN32
#pragma once
#endif

#include "weapon_hl2mpbasehlmpcombatweapon.h"

#ifndef CLIENT_DLL
	#include "rope.h"
	#include "props.h"
#endif

#include "rope_shared.h"

#ifndef CLIENT_DLL

class CWeaponGrapple;

//-----------------------------------------------------------------------------
// Grapple Hook
//-----------------------------------------------------------------------------
class CGrappleHook : public CBaseCombatCharacter
{
	DECLARE_CLASS( CGrappleHook, CBaseCombatCharacter );

public:
	CGrappleHook() { };
	~CGrappleHook();

	Class_T Classify( void ) { return CLASS_NONE; }

public:
	void Spawn( void );
	void Precache( void );
	void FlyThink( void );
	void HookedThink( void );
	void HookTouch( CBaseEntity *pOther );
	bool CreateVPhysics( void );
	unsigned int PhysicsSolidMaskForEntity() const;
	static CGrappleHook *HookCreate( const Vector &vecOrigin, const QAngle &angAngles, CBaseEntity *pentOwner = NULL );

protected:

	DECLARE_DATADESC();

private:

	void UpdatePlayerConstraint( void );

	CHandle<CWeaponGrapple>		m_hOwner;
	CHandle<CBasePlayer>		m_hPlayer;
	CHandle<CDynamicProp>		m_hBolt;
	IPhysicsSpring				*m_pSpring;
	float						m_fSpringLength;
	bool						m_bPlayerWasStanding;
};

#endif

//-----------------------------------------------------------------------------
// CWeaponGrapple
//-----------------------------------------------------------------------------

#ifdef CLIENT_DLL
#define CWeaponGrapple C_WeaponGrapple
#endif

class CWeaponGrapple : public CBaseHL2MPCombatWeapon
{
	DECLARE_CLASS( CWeaponGrapple, CBaseHL2MPCombatWeapon );
public:

	CWeaponGrapple( void );
	
	virtual void	Precache( void );
	virtual void	PrimaryAttack( void );
	virtual void	SecondaryAttack( void );
	virtual bool	Deploy( void );
	bool			CanHolster( void );
	virtual bool	Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
	void			Drop( const Vector &vecVelocity );
	virtual bool	Reload( void );
	virtual void	ItemPostFrame( void );
	virtual void	ItemBusyFrame( void );
	virtual bool	SendWeaponAnim( int iActivity );

	void			NotifyHookDied( void );

	bool			HasAnyAmmo( void );

	CBaseEntity		*GetHook( void ) { return m_hHook; }

#ifndef CLIENT_DLL
	virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator );
#endif

	DECLARE_NETWORKCLASS(); 
	DECLARE_PREDICTABLE();

private:

	void	SetSkin( int skinNum );
	void	CheckZoomToggle( void );
	void	FireHook( void );
	void	ToggleZoom( void );
	
	// Various states for the crossbow's charger
	enum ChargerState_t
	{
		CHARGER_STATE_START_LOAD,
		CHARGER_STATE_START_CHARGE,
		CHARGER_STATE_READY,
		CHARGER_STATE_DISCHARGE,
		CHARGER_STATE_OFF,
	};

	void	CreateChargerEffects( void );
	void	SetChargerState( ChargerState_t state );
	void	DoLoadEffect( void );

#ifndef CLIENT_DLL
	bool	CreateRope( void );

	DECLARE_ACTTABLE();
#endif

private:
	
	// Charger effects
	ChargerState_t		m_nChargeState;

#ifndef CLIENT_DLL
	CHandle<CSprite>	m_hChargerSprite;
	CHandle<CRopeKeyframe>		m_hRope;
#endif

	CNetworkVar( bool,	m_bInZoom );
	CNetworkVar( bool,	m_bMustReload );

	CNetworkHandle( CBaseEntity, m_hHook );

	CWeaponGrapple( const CWeaponGrapple & );
};

#endif // WEAPON_GRAPPLE_H

weapon_grapple.cpp

#include "cbase.h"
#include "npcevent.h"
#include "in_buttons.h"
#include "weapon_grapple.h"

#ifdef CLIENT_DLL
	#include "c_hl2mp_player.h"
	#include "c_te_effect_dispatch.h"
#else
	#include "game.h"
	#include "hl2mp_player.h"
	#include "te_effect_dispatch.h"
	#include "IEffects.h"
	#include "Sprite.h"
	#include "SpriteTrail.h"
	#include "beam_shared.h"
	#include "explode.h"

	#include "vphysics/constraints.h"
	#include "physics_saverestore.h"
	
#endif

//#include "effect_dispatch_data.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

#define HOOK_MODEL			"models/props_junk/rock001a.mdl"
#define BOLT_MODEL			"models/crossbow_bolt.mdl"

#define BOLT_AIR_VELOCITY	3500
#define BOLT_WATER_VELOCITY	1500
#define	BOLT_SKIN_NORMAL	0
#define BOLT_SKIN_GLOW		1

#ifndef CLIENT_DLL

LINK_ENTITY_TO_CLASS( grapple_hook, CGrappleHook );

BEGIN_DATADESC( CGrappleHook )
	// Function Pointers
	DEFINE_THINKFUNC( FlyThink ),
	DEFINE_THINKFUNC( HookedThink ),
	DEFINE_FUNCTION( HookTouch ),

	DEFINE_PHYSPTR( m_pSpring ),
	DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ),
	DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
	DEFINE_FIELD( m_hBolt, FIELD_EHANDLE ),
	DEFINE_FIELD( m_bPlayerWasStanding, FIELD_BOOLEAN ),

END_DATADESC()

CGrappleHook *CGrappleHook::HookCreate( const Vector &vecOrigin, const QAngle &angAngles, CBaseEntity *pentOwner )
{
	// Create a new entity with CGrappleHook private data
	CGrappleHook *pHook = (CGrappleHook *)CreateEntityByName( "grapple_hook" );
	UTIL_SetOrigin( pHook, vecOrigin );
	pHook->SetAbsAngles( angAngles );
	pHook->Spawn();

	CWeaponGrapple *pOwner = (CWeaponGrapple *)pentOwner;
	pHook->m_hOwner = pOwner;
	pHook->SetOwnerEntity( pOwner->GetOwner() );
	pHook->m_hPlayer = (CBasePlayer *)pOwner->GetOwner();

	return pHook;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CGrappleHook::~CGrappleHook( void )
{
	if ( m_pSpring )
	{
		physenv->DestroySpring( m_pSpring );
		m_pSpring = NULL;
	}

	if ( m_hBolt )
	{
		UTIL_Remove( m_hBolt );
		m_hBolt = NULL;
	}

	// Revert Jay's gai flag
	if ( m_hPlayer )
		m_hPlayer->SetPhysicsFlag( PFLAG_VPHYSICS_MOTIONCONTROLLER, false );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CGrappleHook::CreateVPhysics( void )
{
	// Create the object in the physics system
	VPhysicsInitNormal( SOLID_BBOX, FSOLID_NOT_STANDABLE, false );

	return true;
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
unsigned int CGrappleHook::PhysicsSolidMaskForEntity() const
{
	return ( BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_HITBOX ) & ~CONTENTS_GRATE;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CGrappleHook::Spawn( void )
{
	Precache( );

	SetModel( HOOK_MODEL );
	SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
	UTIL_SetSize( this, -Vector(1,1,1), Vector(1,1,1) );
	SetSolid( SOLID_BBOX );
	SetGravity( 0.05f );

	// The rock is invisible, the crossbow bolt is the visual representation
	AddEffects( EF_NODRAW );
	
	// Make sure we're updated if we're underwater
	UpdateWaterState();

	SetTouch( &CGrappleHook::HookTouch );

	SetThink( &CGrappleHook::FlyThink );
	SetNextThink( gpGlobals->curtime + 0.1f );

	m_pSpring		= NULL;
	m_fSpringLength = 0.0f;
	m_bPlayerWasStanding = false;

	// Create bolt model and parent it
	CBaseEntity *pBolt = CBaseEntity::CreateNoSpawn( "prop_dynamic", GetAbsOrigin(), GetAbsAngles(), this );
	pBolt->SetModelName( MAKE_STRING( BOLT_MODEL ) );
	pBolt->SetModel( BOLT_MODEL );
	DispatchSpawn( pBolt );
	pBolt->SetParent( this );
}

void CGrappleHook::Precache( void )
{
	PrecacheModel( HOOK_MODEL );
	PrecacheModel( BOLT_MODEL );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pOther - 
//-----------------------------------------------------------------------------
void CGrappleHook::HookTouch( CBaseEntity *pOther )
{
	if ( !pOther->IsSolid() || pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) )
		return;

	if ( (pOther != m_hOwner) && (pOther->m_takedamage != DAMAGE_NO) )
	{
		m_hOwner->NotifyHookDied();

		SetTouch( NULL );
		SetThink( NULL );

		UTIL_Remove( this );
	}
	else
	{
		trace_t	tr;
		tr = BaseClass::GetTouchTrace();

		// See if we struck the world
		if ( pOther->GetMoveType() == MOVETYPE_NONE && !( tr.surface.flags & SURF_SKY ) )
		{
			EmitSound( "Weapon_Crossbow.BoltHitWorld" );

			// if what we hit is static architecture, can stay around for a while.
			Vector vecDir = GetAbsVelocity();
			
			//FIXME: We actually want to stick (with hierarchy) to what we've hit
			SetMoveType( MOVETYPE_NONE );
		
			Vector vForward;

			AngleVectors( GetAbsAngles(), &vForward );
			VectorNormalize ( vForward );

			CEffectData	data;

			data.m_vOrigin = tr.endpos;
			data.m_vNormal = vForward;
			data.m_nEntIndex = 0;
		
		//	DispatchEffect( "Impact", data );
			
			UTIL_ImpactTrace( &tr, DMG_BULLET );

		//	AddEffects( EF_NODRAW );
			SetTouch( NULL );
			
			// Shoot some sparks
			if ( UTIL_PointContents( GetAbsOrigin() ) != CONTENTS_WATER)
			{
				g_pEffects->Sparks( GetAbsOrigin() );
			}

			VPhysicsDestroyObject();
			VPhysicsInitNormal( SOLID_VPHYSICS, FSOLID_NOT_STANDABLE, false );
			AddSolidFlags( FSOLID_NOT_SOLID );
		//	SetMoveType( MOVETYPE_NONE );

			if ( !m_hPlayer )
			{
				Assert( 0 );
				return;
			}

			// Set Jay's gai flag
			m_hPlayer->SetPhysicsFlag( PFLAG_VPHYSICS_MOTIONCONTROLLER, true );

			IPhysicsObject *pPhysObject = m_hPlayer->VPhysicsGetObject();
			IPhysicsObject *pRootPhysObject = VPhysicsGetObject();
			Assert( pRootPhysObject );
			Assert( pPhysObject );

			pRootPhysObject->EnableMotion( false );

			// Root has huge mass, tip has little
			pRootPhysObject->SetMass( VPHYSICS_MAX_MASS );
		//	pPhysObject->SetMass( 100 );
		//	float damping = 3;
		//	pPhysObject->SetDamping( &damping, &damping );

			Vector origin = m_hPlayer->GetAbsOrigin();
			Vector rootOrigin = GetAbsOrigin();
			m_fSpringLength = (origin - rootOrigin).Length();

			springparams_t spring;
			spring.constant = 8000;
			spring.damping = 400;
			spring.naturalLength = m_fSpringLength;
			spring.relativeDamping = 2;
			spring.startPosition = origin;
			spring.endPosition = rootOrigin;
			spring.useLocalPositions = false;
			spring.onlyStretch = false;		
			m_pSpring = physenv->CreateSpring( pPhysObject, pRootPhysObject, &spring );

			m_bPlayerWasStanding = ( ( m_hPlayer->GetFlags() & FL_DUCKING ) == 0 );

			SetThink( &CGrappleHook::HookedThink );
			SetNextThink( gpGlobals->curtime + 0.1f );
		}
		else
		{
			// Put a mark unless we've hit the sky
			if ( ( tr.surface.flags & SURF_SKY ) == false )
			{
				UTIL_ImpactTrace( &tr, DMG_BULLET );
			}

			SetTouch( NULL );
			SetThink( NULL );

			m_hOwner->NotifyHookDied();
			UTIL_Remove( this );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CGrappleHook::HookedThink( void )
{
/*	static bool omgWtf = false;

	if ( omgWtf )
	{
		omgWtf = false; // only run once

		// Destroy the current spring.
		physenv->DestroySpring( m_pSpring );
		m_pSpring = NULL;

		IPhysicsObject *pPhysObject = m_hPlayer->VPhysicsGetObject();
		IPhysicsObject *pRootPhysObject = VPhysicsGetObject();
		Assert( pRootPhysObject );
		Assert( pPhysObject );

		Vector origin = m_hPlayer->GetAbsOrigin();
		Vector rootOrigin = GetAbsOrigin();
		m_fSpringLength = (origin - rootOrigin).Length();

		springparams_t spring;
		spring.constant = 6000; //400
		spring.damping = 100;
		spring.naturalLength = m_fSpringLength;
		spring.relativeDamping = 2;
		spring.startPosition = origin;
		spring.endPosition = rootOrigin;
		spring.useLocalPositions = false;
		spring.onlyStretch = false;		
		m_pSpring = physenv->CreateSpring( pPhysObject, pRootPhysObject, &spring );
	}
*/
	UpdatePlayerConstraint();

/*	if( m_hPlayer->GetFlags() & FL_ONGROUND )
	{
		// Try to fight OnGround
		m_hPlayer->SetGravity( 0 );
		m_hPlayer->RemoveFlag( FL_ONGROUND );
	}
*/
	//set next globalthink
	SetNextThink( gpGlobals->curtime + 0.1f );

	m_fSpringLength -= 20;

	if ( m_fSpringLength < 1 )
		return;

	if ( m_pSpring )
		m_pSpring->SetSpringLength( m_fSpringLength );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGrappleHook::UpdatePlayerConstraint( void )
{
	// Check to see if the player's standing/ducking state has changed.
	bool bStanding = ( ( m_hPlayer->GetFlags() & FL_DUCKING ) == 0 );
	if ( bStanding == m_bPlayerWasStanding )
		return;

	// Destroy the current spring.
	physenv->DestroySpring( m_pSpring );
	m_pSpring = NULL;

	// Set Jay's gai flag
//	m_hPlayer->SetPhysicsFlag( PFLAG_VPHYSICS_MOTIONCONTROLLER, true );

	// Create the new constraint for the standing/ducking player physics object.
	IPhysicsObject *pPhysObject = m_hPlayer->VPhysicsGetObject();
	IPhysicsObject *pRootPhysObject = VPhysicsGetObject();
	Assert( pRootPhysObject );
	Assert( pPhysObject );

	Vector origin = m_hPlayer->GetAbsOrigin();
	Vector rootOrigin = GetAbsOrigin();
	m_fSpringLength = (origin - rootOrigin).Length();

	springparams_t spring;
	spring.constant = 6000; //400
	spring.damping = 100;
	spring.naturalLength = m_fSpringLength;
	spring.relativeDamping = 2;
	spring.startPosition = origin;
	spring.endPosition = rootOrigin;
	spring.useLocalPositions = false;
	spring.onlyStretch = false;		
	m_pSpring = physenv->CreateSpring( pPhysObject, pRootPhysObject, &spring );

	// Save state for the next check.
	m_bPlayerWasStanding = bStanding;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CGrappleHook::FlyThink( void )
{
	QAngle angNewAngles;

	VectorAngles( GetAbsVelocity(), angNewAngles );
	SetAbsAngles( angNewAngles );

	SetNextThink( gpGlobals->curtime + 0.1f );
}

#endif

IMPLEMENT_NETWORKCLASS_ALIASED( WeaponGrapple, DT_WeaponGrapple )

#ifdef CLIENT_DLL
void RecvProxy_HookDied( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
	CWeaponGrapple *pGrapple = ((CWeaponGrapple*)pStruct);

	RecvProxy_IntToEHandle( pData, pStruct, pOut );

	CBaseEntity *pNewHook = pGrapple->GetHook();

	if ( pNewHook == NULL )
	{
		if ( pGrapple->GetOwner() && pGrapple->GetOwner()->GetActiveWeapon() == pGrapple )
		{
			pGrapple->NotifyHookDied();
		}
	}
}
#endif

BEGIN_NETWORK_TABLE( CWeaponGrapple, DT_WeaponGrapple )
#ifdef CLIENT_DLL
	RecvPropBool( RECVINFO( m_bInZoom ) ),
	RecvPropBool( RECVINFO( m_bMustReload ) ),
	RecvPropEHandle( RECVINFO( m_hHook ), RecvProxy_HookDied ),
#else
	SendPropBool( SENDINFO( m_bInZoom ) ),
	SendPropBool( SENDINFO( m_bMustReload ) ),
	SendPropEHandle( SENDINFO( m_hHook ) ),
#endif
END_NETWORK_TABLE()

#ifdef CLIENT_DLL
BEGIN_PREDICTION_DATA( CWeaponGrapple )
	DEFINE_PRED_FIELD( m_bInZoom, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
	DEFINE_PRED_FIELD( m_bMustReload, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
END_PREDICTION_DATA()
#endif

LINK_ENTITY_TO_CLASS( weapon_grapple, CWeaponGrapple );

PRECACHE_WEAPON_REGISTER( weapon_grapple );

#ifndef CLIENT_DLL

acttable_t	CWeaponGrapple::m_acttable[] = 
{
	{ ACT_HL2MP_IDLE,					ACT_HL2MP_IDLE_CROSSBOW,					false },
	{ ACT_HL2MP_RUN,					ACT_HL2MP_RUN_CROSSBOW,						false },
	{ ACT_HL2MP_IDLE_CROUCH,			ACT_HL2MP_IDLE_CROUCH_CROSSBOW,				false },
	{ ACT_HL2MP_WALK_CROUCH,			ACT_HL2MP_WALK_CROUCH_CROSSBOW,				false },
	{ ACT_HL2MP_GESTURE_RANGE_ATTACK,	ACT_HL2MP_GESTURE_RANGE_ATTACK_CROSSBOW,	false },
	// Valve never made a crossbow reload anim for playermodels, I guess use the ar2 one for now
//	{ ACT_HL2MP_GESTURE_RELOAD,			ACT_HL2MP_GESTURE_RELOAD_CROSSBOW,			false },
	{ ACT_HL2MP_GESTURE_RELOAD,			ACT_HL2MP_GESTURE_RELOAD_AR2,				false },
	{ ACT_HL2MP_JUMP,					ACT_HL2MP_JUMP_CROSSBOW,					false },
};

IMPLEMENT_ACTTABLE(CWeaponGrapple);

#endif

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CWeaponGrapple::CWeaponGrapple( void )
{
	m_bReloadsSingly	= true;
	m_bFiresUnderwater	= true;
	m_bInZoom			= false;
	m_bMustReload		= false;
#ifndef CLIENT_DLL
	m_hRope				= NULL;
#endif
}

#define	CROSSBOW_GLOW_SPRITE	"sprites/light_glow02_noz.vmt"
#define	CROSSBOW_GLOW_SPRITE2	"sprites/blueflare1.vmt"

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CWeaponGrapple::Precache( void )
{
#ifndef CLIENT_DLL
	UTIL_PrecacheOther( "grapple_hook" );
#endif

//	PrecacheScriptSound( "Weapon_Crossbow.BoltHitBody" );
	PrecacheScriptSound( "Weapon_Crossbow.BoltHitWorld" );
//	PrecacheScriptSound( "Weapon_Crossbow.BoltSkewer" );

	PrecacheModel( CROSSBOW_GLOW_SPRITE );
	PrecacheModel( CROSSBOW_GLOW_SPRITE2 );

	BaseClass::Precache();
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::PrimaryAttack( void )
{
	// Can't have an active hook out
	if ( m_hHook != NULL )
		return;

	FireHook();

	SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration( ACT_VM_PRIMARYATTACK ) );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::SecondaryAttack( void )
{
	//NOTENOTE: The zooming is handled by the post/busy frames
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponGrapple::Reload( void )
{
	if ( ( m_bMustReload ) && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) )
	{
		//Redraw the weapon
		SendWeaponAnim( ACT_VM_RELOAD );

		//Update our times
		m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();

		//Mark this as done
		m_bMustReload = false;
	}

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CWeaponGrapple::CheckZoomToggle( void )
{
	CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
	
	if ( pPlayer->m_afButtonPressed & IN_ATTACK2 )
	{
		ToggleZoom();
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CWeaponGrapple::ItemBusyFrame( void )
{
	// Allow zoom toggling even when we're reloading
	CheckZoomToggle();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CWeaponGrapple::ItemPostFrame( void )
{
	// Allow zoom toggling
	CheckZoomToggle();

	CBasePlayer *pOwner = ToBasePlayer( GetOwner() );

	if ( ( pOwner->m_nButtons & IN_ATTACK ) )
	{
		if ( m_flNextPrimaryAttack < gpGlobals->curtime )
		{
			PrimaryAttack();
		}
	}
	else if ( m_bMustReload ) //&& HasWeaponIdleTimeElapsed() )
	{
		Reload();
	}

#ifndef CLIENT_DLL
	if ( m_hHook )
	{
		if ( m_hRope )
			m_hRope->RecalculateLength();

		if ( !(pOwner->m_nButtons & IN_ATTACK) )
		{
			m_hHook->SetTouch( NULL );
			m_hHook->SetThink( NULL );

			UTIL_Remove( m_hHook );
			m_hHook = NULL;

			NotifyHookDied();

			m_bMustReload = true;
		}
	}
#endif

//	BaseClass::ItemPostFrame();
}

#ifndef CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponGrapple::CreateRope( void )
{
	if ( !m_hHook )
	{
		Assert( 0 );
		return false;
	}

	m_hRope = CRopeKeyframe::Create( this, m_hHook, 1, 0 );

	if ( m_hRope )
	{
		m_hRope->m_Width = 2;
		m_hRope->m_nSegments = ROPE_MAX_SEGMENTS / 2;
		m_hRope->EnableWind( false );
	//	m_hRope->EnableCollision(); // Collision looks worse than no collision
		m_hRope->SetupHangDistance( 0 );
	}

	return true;
}
#endif

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CWeaponGrapple::FireHook( void )
{
	if ( m_bMustReload )
		return;

	CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
	
	if ( pOwner == NULL )
		return;

#ifndef CLIENT_DLL
	Vector vecAiming	= pOwner->GetAutoaimVector( 0 );	
	Vector vecSrc		= pOwner->Weapon_ShootPosition();

	QAngle angAiming;
	VectorAngles( vecAiming, angAiming );

	CGrappleHook *pHook = CGrappleHook::HookCreate( vecSrc, angAiming, this );

	if ( pOwner->GetWaterLevel() == 3 )
	{
		pHook->SetAbsVelocity( vecAiming * BOLT_WATER_VELOCITY );
	}
	else
	{
		pHook->SetAbsVelocity( vecAiming * BOLT_AIR_VELOCITY );
	}

	m_hHook = pHook;
	CreateRope();

#endif

	pOwner->ViewPunch( QAngle( -2, 0, 0 ) );

	WeaponSound( SINGLE );
	WeaponSound( SPECIAL2 );

	SendWeaponAnim( ACT_VM_PRIMARYATTACK );

	m_flNextPrimaryAttack = m_flNextSecondaryAttack	= gpGlobals->curtime + 0.75;

	DoLoadEffect();
	SetChargerState( CHARGER_STATE_DISCHARGE );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponGrapple::Deploy( void )
{
	if ( m_bMustReload )
	{
		return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_CROSSBOW_DRAW_UNLOADED, (char*)GetAnimPrefix() );
	}

	SetSkin( BOLT_SKIN_GLOW );

	return BaseClass::Deploy();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pSwitchingTo - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponGrapple::Holster( CBaseCombatWeapon *pSwitchingTo )
{
#ifndef CLIENT_DLL
	if ( m_hHook )
	{
		m_hHook->SetTouch( NULL );
		m_hHook->SetThink( NULL );

		UTIL_Remove( m_hHook );
		m_hHook = NULL;

		NotifyHookDied();

		m_bMustReload = true;
	}
#endif

	if ( m_bInZoom )
	{
		ToggleZoom();
	}

	SetChargerState( CHARGER_STATE_OFF );

	return BaseClass::Holster( pSwitchingTo );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CWeaponGrapple::ToggleZoom( void )
{
	CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
	
	if ( pPlayer == NULL )
		return;

#ifndef CLIENT_DLL

	if ( m_bInZoom )
	{
		if ( pPlayer->SetFOV( this, 0, 0.2f ) )
		{
			m_bInZoom = false;
		}
	}
	else
	{
		if ( pPlayer->SetFOV( this, 20, 0.1f ) )
		{
			m_bInZoom = true;
		}
	}
#endif
}

#define	BOLT_TIP_ATTACHMENT	2

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CWeaponGrapple::CreateChargerEffects( void )
{
#ifndef CLIENT_DLL
	CBasePlayer *pOwner = ToBasePlayer( GetOwner() );

	if ( m_hChargerSprite != NULL )
		return;

	m_hChargerSprite = CSprite::SpriteCreate( CROSSBOW_GLOW_SPRITE, GetAbsOrigin(), false );

	if ( m_hChargerSprite )
	{
		m_hChargerSprite->SetAttachment( pOwner->GetViewModel(), BOLT_TIP_ATTACHMENT );
		m_hChargerSprite->SetTransparency( kRenderTransAdd, 255, 128, 0, 255, kRenderFxNoDissipation );
		m_hChargerSprite->SetBrightness( 0 );
		m_hChargerSprite->SetScale( 0.1f );
		m_hChargerSprite->TurnOff();
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : skinNum - 
//-----------------------------------------------------------------------------
void CWeaponGrapple::SetSkin( int skinNum )
{
	CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
	
	if ( pOwner == NULL )
		return;

	CBaseViewModel *pViewModel = pOwner->GetViewModel();

	if ( pViewModel == NULL )
		return;

	pViewModel->m_nSkin = skinNum;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CWeaponGrapple::DoLoadEffect( void )
{
	SetSkin( BOLT_SKIN_GLOW );

	CBasePlayer *pOwner = ToBasePlayer( GetOwner() );

	if ( pOwner == NULL )
		return;

	CBaseViewModel *pViewModel = pOwner->GetViewModel();

	if ( pViewModel == NULL )
		return;

	CEffectData	data;

#ifdef CLIENT_DLL
	data.m_hEntity = pViewModel->GetRefEHandle();
#else
	data.m_nEntIndex = pViewModel->entindex();
#endif
	data.m_nAttachmentIndex = 1;

	DispatchEffect( "CrossbowLoad", data );

#ifndef CLIENT_DLL

	CSprite *pBlast = CSprite::SpriteCreate( CROSSBOW_GLOW_SPRITE2, GetAbsOrigin(), false );

	if ( pBlast )
	{
		pBlast->SetAttachment( pOwner->GetViewModel(), 1 );
		pBlast->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNone );
		pBlast->SetBrightness( 128 );
		pBlast->SetScale( 0.2f );
		pBlast->FadeOutFromSpawn();
	}
#endif
	
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : state - 
//-----------------------------------------------------------------------------
void CWeaponGrapple::SetChargerState( ChargerState_t state )
{
	// Make sure we're setup
	CreateChargerEffects();

	// Don't do this twice
	if ( state == m_nChargeState )
		return;

	m_nChargeState = state;

	switch( m_nChargeState )
	{
	case CHARGER_STATE_START_LOAD:
	
		WeaponSound( SPECIAL1 );
		
		// Shoot some sparks and draw a beam between the two outer points
		DoLoadEffect();
		
		break;
#ifndef CLIENT_DLL
	case CHARGER_STATE_START_CHARGE:
		{
			if ( m_hChargerSprite == NULL )
				break;
			
			m_hChargerSprite->SetBrightness( 32, 0.5f );
			m_hChargerSprite->SetScale( 0.025f, 0.5f );
			m_hChargerSprite->TurnOn();
		}

		break;

	case CHARGER_STATE_READY:
		{
			// Get fully charged
			if ( m_hChargerSprite == NULL )
				break;
			
			m_hChargerSprite->SetBrightness( 80, 1.0f );
			m_hChargerSprite->SetScale( 0.1f, 0.5f );
			m_hChargerSprite->TurnOn();
		}

		break;

	case CHARGER_STATE_DISCHARGE:
		{
			SetSkin( BOLT_SKIN_NORMAL );
			
			if ( m_hChargerSprite == NULL )
				break;
			
			m_hChargerSprite->SetBrightness( 0 );
			m_hChargerSprite->TurnOff();
		}

		break;
#endif
	case CHARGER_STATE_OFF:
		{
			SetSkin( BOLT_SKIN_NORMAL );

#ifndef CLIENT_DLL
			if ( m_hChargerSprite == NULL )
				break;
			
			m_hChargerSprite->SetBrightness( 0 );
			m_hChargerSprite->TurnOff();
#endif
		}
		break;

	default:
		break;
	}
}

#ifndef CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pEvent - 
//			*pOperator - 
//-----------------------------------------------------------------------------
void CWeaponGrapple::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
{
	switch( pEvent->event )
	{
	case EVENT_WEAPON_THROW:
		SetChargerState( CHARGER_STATE_START_LOAD );
		break;

	case EVENT_WEAPON_THROW2:
		SetChargerState( CHARGER_STATE_START_CHARGE );
		break;
	
	case EVENT_WEAPON_THROW3:
		SetChargerState( CHARGER_STATE_READY );
		break;

	default:
		BaseClass::Operator_HandleAnimEvent( pEvent, pOperator );
		break;
	}
}

#endif

//-----------------------------------------------------------------------------
// Purpose: Set the desired activity for the weapon and its viewmodel counterpart
// Input  : iActivity - activity to play
//-----------------------------------------------------------------------------
bool CWeaponGrapple::SendWeaponAnim( int iActivity )
{
	int newActivity = iActivity;

	// The last shot needs a non-loaded activity
//	if ( ( newActivity == ACT_VM_IDLE ) && ( m_iClip1 <= 0 ) )
//	{
//		newActivity = ACT_VM_FIDGET;
//	}

	//For now, just set the ideal activity and be done with it
	return BaseClass::SendWeaponAnim( newActivity );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CWeaponGrapple::Drop( const Vector &vecVelocity )
{
#ifndef CLIENT_DLL
	if ( m_hHook )
	{
		m_hHook->SetTouch( NULL );
		m_hHook->SetThink( NULL );

		UTIL_Remove( m_hHook );
		m_hHook = NULL;

		NotifyHookDied();

		m_bMustReload = true;
	}
#endif

	if ( m_bInZoom )
	{
		ToggleZoom();
	}

	SetChargerState( CHARGER_STATE_OFF );

	BaseClass::Drop( vecVelocity );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CWeaponGrapple::HasAnyAmmo( void )
{
	if ( m_hHook != NULL )
		return true;

	return BaseClass::HasAnyAmmo();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CWeaponGrapple::CanHolster( void )
{
	//Can't have an active hook out
	if ( m_hHook != NULL )
		return false;

	return BaseClass::CanHolster();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CWeaponGrapple::NotifyHookDied( void )
{
	m_hHook = NULL;

#ifndef CLIENT_DLL
	if ( m_hRope )
	{
		UTIL_Remove( m_hRope );
		m_hRope = NULL;
	}
#endif
}

Weapon script

WeaponData
{
    printname        "GRAPPLE HOOK"
    viewmodel        models/weapons/v_crossbow.mdl
    playermodel    models/weapons/w_crossbow.mdl
    anim_prefix    bow
    bucket            5
    bucket_position    2

//    clip_size        1
//    default_clip        5
//    primary_ammo        XBowBolt
//    secondary_ammo    None

    clip_size                -1
    clip2_size            -1
    primary_ammo            None
    secondary_ammo        None
    default_clip            -1
    default_clip2            -1

    autoswitchto            0
    autoswitchfrom        0
    weight        0
    item_flags        0

    // Sounds for the weapon. There is a max of 16 sounds per category (i.e.
max 16 single_shot sounds)
    SoundData
    {
        single_shot    Weapon_Crossbow.Single
        reload    Weapon_Crossbow.Reload
        special1    Weapon_Crossbow.BoltElectrify
        special2    Weapon_Crossbow.BoltFly
    }

    TextureData
    {
        weapon
        {
                font        WeaponIcons
                character    g
        }
        weapon_s
        {
                font        WeaponIconsSelected
                character    g
        }
        ammo
        {
                font        WeaponIcons
                character    w
        }
        crosshair
        {
                font        Crosshairs
                character    Q
        }
        autoaim
        {
            file        sprites/crosshairs
            x        0
            y        96
            width        24
            height    24
        }
        zoom
        {
                font        Crosshairs
                character    Q
        }
        zoom_autoaim
        {
            file        sprites/crosshairs
            x        24
            y        112
            width        104
            height    16
        }
    }
}

Grapple Hook 2014

Singleplayer code

client/hl2/c_weapon_grapple.cpp

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements the grapple hook weapon.
//
// Primary attack: fires a beam that hooks on a surface.
// Secondary attack: switches between pull and rapple modes
//
//
//=============================================================================//
#include "cbase.h"
#include "npcevent.h"
#include "in_buttons.h"
#include "c_weapon_grapple.h"
#ifdef CLIENT_DLL
// #include "c_hl2mp_player.h"
// #include "player.h"
// #include "c_te_effect_dispatch.h"
#include "c_te_effect_dispatch.h"
#else
#include "game.h"
// #include "hl2mp_player.h"
#include "player.h"
#include "c_te_effect_dispatch.h"
#include "IEffects.h"
#include "Sprite.h"
#include "SpriteTrail.h"
#include "beam_shared.h"
#include "explode.h"
#include "ammodef.h"	/* This is needed for the tracing done later */
#include "gamestats.h" //
#include "soundent.h" //
#include "vphysics/constraints.h"
#include "physics_saverestore.h"
#endif
//#include "effect_dispatch_data.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define HOOK_MODEL	"models/props_junk/rock001a.mdl"
#define BOLT_AIR_VELOCITY	3500
#define BOLT_WATER_VELOCITY	1500
#ifndef CLIENT_DLL
LINK_ENTITY_TO_CLASS( grapple_hook, CGrappleHook );
BEGIN_DATADESC( CGrappleHook )
// Function Pointers
DEFINE_THINKFUNC( FlyThink ),
DEFINE_THINKFUNC( HookedThink ),
DEFINE_FUNCTION( HookTouch ),
DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ),
DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
DEFINE_FIELD( m_hBolt, FIELD_EHANDLE ),
DEFINE_FIELD( m_bPlayerWasStanding, FIELD_BOOLEAN ),
END_DATADESC()
CGrappleHook *CGrappleHook::HookCreate( const Vector &vecOrigin, const QAngle &angAngles, CBaseEntity *pentOwner )
{
// Create a new entity with CGrappleHook private data
CGrappleHook *pHook = (CGrappleHook *)CreateEntityByName( "grapple_hook" );
UTIL_SetOrigin( pHook, vecOrigin );
pHook->SetAbsAngles( angAngles );
pHook->Spawn();
CWeaponGrapple *pOwner = (CWeaponGrapple *)pentOwner;
pHook->m_hOwner = pOwner;
pHook->SetOwnerEntity( pOwner->GetOwner() );
pHook->m_hPlayer = (CBasePlayer *)pOwner->GetOwner();
return pHook;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CGrappleHook::~CGrappleHook( void )
{
if ( m_hBolt )
{
UTIL_Remove( m_hBolt );
m_hBolt = NULL;
}
// Revert Jay's gai flag
if ( m_hPlayer )
m_hPlayer->SetPhysicsFlag( PFLAG_VPHYSICS_MOTIONCONTROLLER, false );
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CGrappleHook::CreateVPhysics( void )
{
// Create the object in the physics system
VPhysicsInitNormal( SOLID_BBOX, FSOLID_NOT_STANDABLE, false );
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
unsigned int CGrappleHook::PhysicsSolidMaskForEntity() const
{
return ( BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_HITBOX ) & ~CONTENTS_GRATE;
}
//-----------------------------------------------------------------------------
// Purpose: Spawn
//-----------------------------------------------------------------------------
void CGrappleHook::Spawn( void )
{
Precache( );
SetModel( HOOK_MODEL );
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
UTIL_SetSize( this, -Vector(1,1,1), Vector(1,1,1) );
SetSolid( SOLID_BBOX );
SetGravity( 0.05f );
// The rock is invisible, the crossbow bolt is the visual representation
AddEffects( EF_NODRAW );
// Make sure we're updated if we're underwater
UpdateWaterState();
SetTouch( &CGrappleHook::HookTouch );
SetThink( &CGrappleHook::FlyThink );
SetNextThink( gpGlobals->curtime + 0.1f );
m_pSpring = NULL;
m_fSpringLength = 0.0f;
m_bPlayerWasStanding = false;
}
void CGrappleHook::Precache( void )
{
PrecacheModel( HOOK_MODEL );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pOther -
//-----------------------------------------------------------------------------
void CGrappleHook::HookTouch( CBaseEntity *pOther )
{
if ( !pOther->IsSolid() || pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) )
return;
if ( (pOther != m_hOwner) && (pOther->m_takedamage != DAMAGE_NO) )
{
m_hOwner->NotifyHookDied();
SetTouch( NULL );
SetThink( NULL );
UTIL_Remove( this );
}
else
{
trace_t	tr;
tr = BaseClass::GetTouchTrace();
// See if we struck the world
if ( pOther->GetMoveType() == MOVETYPE_NONE && !( tr.surface.flags & SURF_SKY ) )
{
EmitSound( "Weapon_AR2.Reload_Push" );
// if what we hit is static architecture, can stay around for a while.
Vector vecDir = GetAbsVelocity();
//FIXME: We actually want to stick (with hierarchy) to what we've hit
SetMoveType( MOVETYPE_NONE );
Vector vForward;
AngleVectors( GetAbsAngles(), &vForward );
VectorNormalize ( vForward );
CEffectData data;
data.m_vOrigin = tr.endpos;
data.m_vNormal = vForward;
data.m_nEntIndex = 0;
// DispatchEffect( "Impact", data );
// AddEffects( EF_NODRAW );
SetTouch( NULL );
VPhysicsDestroyObject();
VPhysicsInitNormal( SOLID_VPHYSICS, FSOLID_NOT_STANDABLE, false );
AddSolidFlags( FSOLID_NOT_SOLID );
// SetMoveType( MOVETYPE_NONE );
if ( !m_hPlayer )
{
Assert( 0 );
return;
}
// Set Jay's gai flag
m_hPlayer->SetPhysicsFlag( PFLAG_VPHYSICS_MOTIONCONTROLLER, true );
//IPhysicsObject *pPhysObject = m_hPlayer->VPhysicsGetObject();
IPhysicsObject *pRootPhysObject = VPhysicsGetObject();
Assert( pRootPhysObject );
Assert( pPhysObject );
pRootPhysObject->EnableMotion( false );
// Root has huge mass, tip has little
pRootPhysObject->SetMass( VPHYSICS_MAX_MASS );
// pPhysObject->SetMass( 100 );
// float damping = 3;
// pPhysObject->SetDamping( &damping, &damping );
Vector origin = m_hPlayer->GetAbsOrigin();
Vector rootOrigin = GetAbsOrigin();
m_fSpringLength = (origin - rootOrigin).Length();
m_bPlayerWasStanding = ( ( m_hPlayer->GetFlags() & FL_DUCKING ) == 0 );
SetThink( &CGrappleHook::HookedThink );
SetNextThink( gpGlobals->curtime + 0.1f );
}
else
{
// Put a mark unless we've hit the sky
if ( ( tr.surface.flags & SURF_SKY ) == false )
{
UTIL_ImpactTrace( &tr, DMG_BULLET );
}
SetTouch( NULL );
SetThink( NULL );
m_hOwner->NotifyHookDied();
UTIL_Remove( this );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGrappleHook::HookedThink( void )
{
//set next globalthink
SetNextThink( gpGlobals->curtime + 0.05f ); //0.1f
//All of this push the player far from the hook
Vector tempVec1 = m_hPlayer->GetAbsOrigin() - GetAbsOrigin();
VectorNormalize(tempVec1);
int temp_multiplier = -1;
m_hPlayer->SetGravity(0.0f);
m_hPlayer->SetGroundEntity(NULL);
if (m_hOwner->m_bHook){
temp_multiplier = 1;
m_hPlayer->SetAbsVelocity(tempVec1*temp_multiplier*50);//400
}
else
{
m_hPlayer->SetAbsVelocity(tempVec1*temp_multiplier*400);//400
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGrappleHook::FlyThink( void )
{
QAngle angNewAngles;
VectorAngles( GetAbsVelocity(), angNewAngles );
SetAbsAngles( angNewAngles );
SetNextThink( gpGlobals->curtime + 0.1f );
}
#endif
IMPLEMENT_NETWORKCLASS_ALIASED( WeaponGrapple, DT_WeaponGrapple )
#ifdef CLIENT_DLL
void RecvProxy_HookDied( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
CWeaponGrapple *pGrapple = ((CWeaponGrapple*)pStruct);
RecvProxy_IntToEHandle( pData, pStruct, pOut );
CBaseEntity *pNewHook = pGrapple->GetHook();
if ( pNewHook == NULL )
{
if ( pGrapple->GetOwner() && pGrapple->GetOwner()->GetActiveWeapon() == pGrapple )
{
pGrapple->NotifyHookDied();
}
}
}
#endif
BEGIN_NETWORK_TABLE( CWeaponGrapple, DT_WeaponGrapple )
#ifdef CLIENT_DLL
RecvPropBool( RECVINFO( m_bInZoom ) ),
RecvPropBool( RECVINFO( m_bMustReload ) ),
RecvPropEHandle( RECVINFO( m_hHook ), RecvProxy_HookDied ),
RecvPropInt ( RECVINFO (m_nBulletType)),
#else
SendPropBool( SENDINFO( m_bInZoom ) ),
SendPropBool( SENDINFO( m_bMustReload ) ),
SendPropEHandle( SENDINFO( m_hHook ) ),
SendPropInt ( SENDINFO (m_nBulletType)),
#endif
END_NETWORK_TABLE()
#ifdef CLIENT_DLL
BEGIN_PREDICTION_DATA( CWeaponGrapple )
DEFINE_PRED_FIELD( m_bInZoom, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_bMustReload, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
END_PREDICTION_DATA()
#endif
LINK_ENTITY_TO_CLASS( weapon_grapple, CWeaponGrapple );
PRECACHE_WEAPON_REGISTER( weapon_grapple );
#ifndef CLIENT_DLL
acttable_t	CWeaponGrapple::m_acttable[] =
{
// { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_CROSSBOW, false },
// { ACT_HL2MP_RUN, ACT_HL2MP_RUN_CROSSBOW, false },
// { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_CROSSBOW, false },
// { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_CROSSBOW, false },
// { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_CROSSBOW, false },
// // Valve never made a crossbow reload anim for playermodels, I guess use the ar2 one for now
// //{ ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_CROSSBOW, false },
// { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_AR2, false },
// { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_CROSSBOW, false },
{ ACT_IDLE, ACT_IDLE, false },
{ ACT_RUN, ACT_RUN, false },
{ ACT_WALK_CROUCH, ACT_WALK_CROUCH, false },
{ ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD, false },
{ ACT_JUMP, ACT_JUMP, false },
};
IMPLEMENT_ACTTABLE(CWeaponGrapple);
#endif
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CWeaponGrapple::CWeaponGrapple( void )
{
m_bReloadsSingly = true;
m_bFiresUnderwater = true;
m_bInZoom = false;
m_bMustReload = false;
m_nBulletType = -1;
#ifndef CLIENT_DLL
m_pLightGlow= NULL;
pBeam = NULL;
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Precache
//-----------------------------------------------------------------------------
void CWeaponGrapple::Precache( void )
{
#ifndef CLIENT_DLL
UTIL_PrecacheOther( "grapple_hook" );
#endif
// PrecacheScriptSound( "Weapon_Crossbow.BoltHitBody" );
// PrecacheScriptSound( "Weapon_Crossbow.BoltHitWorld" );
// PrecacheScriptSound( "Weapon_Crossbow.BoltSkewer" );
PrecacheModel( "sprites/physbeam.vmt" );
PrecacheModel( "sprites/physcannon_bluecore2b.vmt" );
BaseClass::Precache();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::PrimaryAttack( void )
{
// Can't have an active hook out
if ( m_hHook != NULL )
return;
#ifndef CLIENT_DLL
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( !pPlayer )
{
return;
}
if ( m_iClip1 <= 0 )
{
if ( !m_bFireOnEmpty )
{
Reload();
}
else
{
WeaponSound( EMPTY );
m_flNextPrimaryAttack = 0.15;
}
return;
}
m_iPrimaryAttacks++;
gamestats->Event_WeaponFired( pPlayer, true, GetClassname() );
WeaponSound( SINGLE );
pPlayer->DoMuzzleFlash();
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
pPlayer->SetAnimation( PLAYER_ATTACK1 );
m_flNextPrimaryAttack = gpGlobals->curtime + 0.75;
m_flNextSecondaryAttack = gpGlobals->curtime + 0.75;
//Disabled so we can shoot all the time that we want
//m_iClip1--;
Vector vecSrc = pPlayer->Weapon_ShootPosition();
Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_SCALE_DEFAULT );
//We will not shoot bullets anymore
//pPlayer->FireBullets( 1, vecSrc, vecAiming, vec3_origin, MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 0 );
pPlayer->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 );
CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 600, 0.2, GetOwner() );
if ( !m_iClip1 && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
{
// HEV suit - indicate out of ammo condition
pPlayer->SetSuitUpdate( "!HEV_AMO0", FALSE, 0 );
}
trace_t tr;
Vector vecShootOrigin, vecShootDir, vecDir, vecEnd;
//Gets the direction where the player is aiming
AngleVectors (pPlayer->EyeAngles(), &vecDir);
//Gets the position of the player
vecShootOrigin = pPlayer->Weapon_ShootPosition();
//Gets the position where the hook will hit
vecEnd = vecShootOrigin + (vecDir * MAX_TRACE_LENGTH);
//Traces a line between the two vectors
UTIL_TraceLine( vecShootOrigin, vecEnd, MASK_SHOT, pPlayer, COLLISION_GROUP_NONE, &tr);
//Draws the beam
DrawBeam( vecShootOrigin, tr.endpos, 15.5 );
//Creates an energy impact effect if we don't hit the sky or other places
if ( (tr.surface.flags & SURF_SKY) == false )
{
CPVSFilter filter( tr.endpos );
te->GaussExplosion( filter, 0.0f, tr.endpos, tr.plane.normal, 0 );
m_nBulletType = GetAmmoDef()->Index("GaussEnergy");
UTIL_ImpactTrace( &tr, m_nBulletType );
//Makes a sprite at the end of the beam
m_pLightGlow = CSprite::SpriteCreate( "sprites/physcannon_bluecore2b.vmt", GetAbsOrigin(), TRUE);
//Sets FX render and color
m_pLightGlow->SetTransparency( 9, 255, 255, 255, 200, kRenderFxNoDissipation );
//Sets the position
m_pLightGlow->SetAbsOrigin(tr.endpos);
//Bright
m_pLightGlow->SetBrightness( 255 );
//Scale
m_pLightGlow->SetScale( 0.65 );
}
#endif
FireHook();
SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration( ACT_VM_PRIMARYATTACK ) );
}
//-----------------------------------------------------------------------------
// Purpose: Toggles the grapple hook modes
//-----------------------------------------------------------------------------
void CWeaponGrapple::SecondaryAttack( void )
{
ToggleHook();
WeaponSound( EMPTY );
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponGrapple::Reload( void )
{
if ( ( m_bMustReload ) && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) )
{
//Redraw the weapon
SendWeaponAnim( ACT_VM_IDLE ); //ACT_VM_RELOAD
//Update our times
m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
//Mark this as done
m_bMustReload = false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Toggles between pull and rappel mode
//-----------------------------------------------------------------------------
bool CWeaponGrapple::ToggleHook( void )
{
#ifndef CLIENT_DLL
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( m_bHook )
{
m_bHook = false;
ClientPrint(pPlayer,HUD_PRINTCENTER, "Pull mode");
return m_bHook;
}
else
{
m_bHook = true;
ClientPrint(pPlayer,HUD_PRINTCENTER, "Rappel mode");
return m_bHook;
}
#endif
return m_bHook;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::ItemBusyFrame( void )
{
// Allow zoom toggling even when we're reloading
//CheckZoomToggle();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::ItemPostFrame( void )
{
//Enforces being able to use PrimaryAttack and Secondary Attack
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
if ( ( pOwner->m_nButtons & IN_ATTACK ) )
{
if ( m_flNextPrimaryAttack < gpGlobals->curtime )
{
PrimaryAttack();
}
}
else if ( m_bMustReload ) //&& HasWeaponIdleTimeElapsed() )
{
Reload();
}
if ( ( pOwner->m_afButtonPressed & IN_ATTACK2 ) )
{
if ( m_flNextPrimaryAttack < gpGlobals->curtime )
{
SecondaryAttack();
}
}
else if ( m_bMustReload ) //&& HasWeaponIdleTimeElapsed() )
{
Reload();
}
//Allow a refire as fast as the player can click
if ( ( ( pOwner->m_nButtons & IN_ATTACK ) == false ) )
{
m_flNextPrimaryAttack = gpGlobals->curtime - 0.1f;
}
#ifndef CLIENT_DLL
if ( m_hHook )
{
if ( !(pOwner->m_nButtons & IN_ATTACK) && !(pOwner->m_nButtons & IN_ATTACK2))
{
m_hHook->SetTouch( NULL );
m_hHook->SetThink( NULL );
UTIL_Remove( m_hHook );
m_hHook = NULL;
NotifyHookDied();
m_bMustReload = true;
}
}
#endif
// BaseClass::ItemPostFrame();
}
//-----------------------------------------------------------------------------
// Purpose: Fires the hook
//-----------------------------------------------------------------------------
void CWeaponGrapple::FireHook( void )
{
if ( m_bMustReload )
return;
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
if ( pOwner == NULL )
return;
#ifndef CLIENT_DLL
Vector vecAiming = pOwner->GetAutoaimVector( 0 );
Vector vecSrc = pOwner->Weapon_ShootPosition();
QAngle angAiming;
VectorAngles( vecAiming, angAiming );
CGrappleHook *pHook = CGrappleHook::HookCreate( vecSrc, angAiming, this );
if ( pOwner->GetWaterLevel() == 3 )
{
pHook->SetAbsVelocity( vecAiming * BOLT_WATER_VELOCITY );
}
else
{
pHook->SetAbsVelocity( vecAiming * BOLT_AIR_VELOCITY );
}
m_hHook = pHook;
#endif
pOwner->ViewPunch( QAngle( -2, 0, 0 ) );
//WeaponSound( SINGLE );
//WeaponSound( SPECIAL2 );
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + 0.75;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponGrapple::Deploy( void )
{
if ( m_bMustReload )
{
return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_CROSSBOW_DRAW_UNLOADED, (char*)GetAnimPrefix() );
}
return BaseClass::Deploy();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pSwitchingTo -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponGrapple::Holster( CBaseCombatWeapon *pSwitchingTo )
{
#ifndef CLIENT_DLL
if ( m_hHook )
{
m_hHook->SetTouch( NULL );
m_hHook->SetThink( NULL );
UTIL_Remove( m_hHook );
m_hHook = NULL;
NotifyHookDied();
m_bMustReload = true;
}
#endif
return BaseClass::Holster( pSwitchingTo );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::Drop( const Vector &vecVelocity )
{
#ifndef CLIENT_DLL
if ( m_hHook )
{
m_hHook->SetTouch( NULL );
m_hHook->SetThink( NULL );
UTIL_Remove( m_hHook );
m_hHook = NULL;
NotifyHookDied();
m_bMustReload = true;
}
#endif
BaseClass::Drop( vecVelocity );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CWeaponGrapple::HasAnyAmmo( void )
{
if ( m_hHook != NULL )
return true;
return BaseClass::HasAnyAmmo();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CWeaponGrapple::CanHolster( void )
{
//Can't have an active hook out
if ( m_hHook != NULL )
return false;
return BaseClass::CanHolster();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::NotifyHookDied( void )
{
m_hHook = NULL;
#ifndef CLIENT_DLL
if ( pBeam )
{
UTIL_Remove( pBeam ); //Kill beam
pBeam = NULL;
UTIL_Remove( m_pLightGlow ); //Kill sprite
m_pLightGlow = NULL;
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Draws a beam
// Input : &startPos - where the beam should begin
// &endPos - where the beam should end
// width - what the diameter of the beam should be (units?)
//-----------------------------------------------------------------------------
void CWeaponGrapple::DrawBeam( const Vector &startPos, const Vector &endPos, float width )
{
#ifndef CLIENT_DLL
//Tracer down the middle (NOT NEEDED, IT WILL FIRE A TRACER)
//UTIL_Tracer( startPos, endPos, 0, TRACER_DONT_USE_ATTACHMENT, 6500, false, "GaussTracer" );
trace_t tr;
//Draw the main beam shaft
pBeam = CBeam::BeamCreate( "sprites/physbeam.vmt", 15.5 );
// It starts at startPos
pBeam->SetStartPos( startPos );
// This sets up some things that the beam uses to figure out where
// it should start and end
pBeam->PointEntInit( endPos, this );
// This makes it so that the beam appears to come from the muzzle of the pistol
pBeam->SetEndAttachment( LookupAttachment("Muzzle") );
pBeam->SetWidth( width );
// pBeam->SetEndWidth( 0.05f );
// Higher brightness means less transparent
pBeam->SetBrightness( 255 );
//pBeam->SetColor( 255, 185+random->RandomInt( -16, 16 ), 40 );
pBeam->RelinkBeam();
//Sets scrollrate of the beam sprite
float scrollOffset = gpGlobals->curtime + 5.5;
pBeam->SetScrollRate(scrollOffset);
// The beam should only exist for a very short time
//pBeam->LiveForTime( 0.1f );
UpdateWaterState();
SetTouch( &CGrappleHook::HookTouch );
SetThink( &CGrappleHook::FlyThink );
SetNextThink( gpGlobals->curtime + 0.1f );
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &tr - used to figure out where to do the effect
// nDamageType - ???
//-----------------------------------------------------------------------------
void CWeaponGrapple::DoImpactEffect( trace_t &tr, int nDamageType )
{
#ifndef CLIENT_DLL
if ( (tr.surface.flags & SURF_SKY) == false )
{
CPVSFilter filter( tr.endpos );
te->GaussExplosion( filter, 0.0f, tr.endpos, tr.plane.normal, 0 );
m_nBulletType = GetAmmoDef()->Index("GaussEnergy");
UTIL_ImpactTrace( &tr, m_nBulletType );
}
#endif
}

client/hl2/c_weapon_grapple.h

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements the grapple hook weapon.
//
// Primary attack: fires a beam that hooks on a surface.
// Secondary attack: switches between pull and rapple modes
//
//
//=============================================================================//
#ifndef WEAPON_GRAPPLE_H
#define WEAPON_GRAPPLE_H
#ifdef _WIN32
#pragma once
#endif
//#include "weapon_hl2mpbasehlmpcombatweapon.h"
#include "c_basehlcombatweapon.h"
#include "sprite.h"
#ifndef CLIENT_DLL
#include "rope.h"
#include "props.h"
#endif
#include "rope_shared.h"
#include "c_te_effect_dispatch.h"
#include "beam_shared.h"
#ifndef CLIENT_DLL
class CWeaponGrapple;
//-----------------------------------------------------------------------------
// Grapple Hook
//-----------------------------------------------------------------------------
class CGrappleHook : public CBaseCombatCharacter
{
DECLARE_CLASS( CGrappleHook, CBaseCombatCharacter );
public:
CGrappleHook() { };
~CGrappleHook();
Class_T Classify( void ) { return CLASS_NONE; }
public:
void Spawn( void );
void Precache( void );
void FlyThink( void );
void HookedThink( void );
void HookTouch( CBaseEntity *pOther );
bool CreateVPhysics( void );
unsigned int PhysicsSolidMaskForEntity() const;
static CGrappleHook *HookCreate( const Vector &vecOrigin, const QAngle &angAngles, CBaseEntity *pentOwner = NULL );
protected:
DECLARE_DATADESC();
private:
CHandle<CWeaponGrapple> m_hOwner;
CHandle<CBasePlayer> m_hPlayer;
CHandle<CDynamicProp> m_hBolt;
IPhysicsSpring *m_pSpring;
float	m_fSpringLength;
bool	m_bPlayerWasStanding;
};
#endif
//-----------------------------------------------------------------------------
// CWeaponGrapple
//-----------------------------------------------------------------------------
#ifdef CLIENT_DLL
#define CWeaponGrapple C_WeaponGrapple
#endif
//class CWeaponGrapple : public CBaseHL2MPCombatWeapon
class CWeaponGrapple : public CBaseHLCombatWeapon
{
// DECLARE_CLASS( CWeaponGrapple, CBaseHL2MPCombatWeapon );
DECLARE_CLASS( CWeaponGrapple, CBaseHLCombatWeapon );
public:
CWeaponGrapple( void );
virtual void	Precache( void );
virtual void	PrimaryAttack( void );
virtual void	SecondaryAttack( void );
virtual bool	Deploy( void );
bool	CanHolster( void );
virtual bool	Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
void	Drop( const Vector &vecVelocity );
virtual bool	Reload( void );
virtual void	ItemPostFrame( void );
virtual void	ItemBusyFrame( void );
void	NotifyHookDied( void );
bool	HasAnyAmmo( void );
CBaseEntity *GetHook( void ) { return m_hHook; }
bool ToggleHook( void );
void DrawBeam( const Vector &startPos, const Vector &endPos, float width );
void	DoImpactEffect( trace_t &tr, int nDamageType );
bool m_bHook;
DECLARE_NETWORKCLASS();
DECLARE_PREDICTABLE();
private:
void	FireHook( void );
#ifndef CLIENT_DLL
DECLARE_ACTTABLE();
#endif
private:
#ifndef CLIENT_DLL
CHandle<CBeam> pBeam;
CHandle<CSprite> m_pLightGlow;
#endif
CNetworkVar( bool, m_bInZoom );
CNetworkVar( bool, m_bMustReload );
CNetworkHandle( CBaseEntity, m_hHook );
CWeaponGrapple( const CWeaponGrapple & );
CNetworkVar( int, m_nBulletType );
};
#endif // WEAPON_GRAPPLE_H

server/hl2/weapon_grapple.cpp

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements the grapple hook weapon.
//
// Primary attack: fires a beam that hooks on a surface.
// Secondary attack: switches between pull and rapple modes
//
//
//=============================================================================//
#include "cbase.h"
#include "npcevent.h"
#include "in_buttons.h"
#include "weapon_grapple.h"
#ifdef CLIENT_DLL
//#include "c_hl2mp_player.h"
#include "player.h"
//#include "c_te_effect_dispatch.h"
#include "te_effect_dispatch.h"
#else
#include "game.h"
//#include "hl2mp_player.h"
#include "player.h"
#include "te_effect_dispatch.h"
#include "IEffects.h"
#include "Sprite.h"
#include "SpriteTrail.h"
#include "beam_shared.h"
#include "explode.h"
#include "ammodef.h"	/* This is needed for the tracing done later */
#include "gamestats.h" //
#include "soundent.h" //
#include "vphysics/constraints.h"
#include "physics_saverestore.h"
#endif
//#include "effect_dispatch_data.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define HOOK_MODEL	"models/props_junk/rock001a.mdl"
#define BOLT_AIR_VELOCITY	3500
#define BOLT_WATER_VELOCITY	1500
#ifndef CLIENT_DLL
LINK_ENTITY_TO_CLASS( grapple_hook, CGrappleHook );
BEGIN_DATADESC( CGrappleHook )
// Function Pointers
DEFINE_THINKFUNC( FlyThink ),
DEFINE_THINKFUNC( HookedThink ),
DEFINE_FUNCTION( HookTouch ),
DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ),
DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
DEFINE_FIELD( m_hBolt, FIELD_EHANDLE ),
DEFINE_FIELD( m_bPlayerWasStanding, FIELD_BOOLEAN ),
END_DATADESC()
CGrappleHook *CGrappleHook::HookCreate( const Vector &vecOrigin, const QAngle &angAngles, CBaseEntity *pentOwner )
{
// Create a new entity with CGrappleHook private data
CGrappleHook *pHook = (CGrappleHook *)CreateEntityByName( "grapple_hook" );
UTIL_SetOrigin( pHook, vecOrigin );
pHook->SetAbsAngles( angAngles );
pHook->Spawn();
CWeaponGrapple *pOwner = (CWeaponGrapple *)pentOwner;
pHook->m_hOwner = pOwner;
pHook->SetOwnerEntity( pOwner->GetOwner() );
pHook->m_hPlayer = (CBasePlayer *)pOwner->GetOwner();
return pHook;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CGrappleHook::~CGrappleHook( void )
{
if ( m_hBolt )
{
UTIL_Remove( m_hBolt );
m_hBolt = NULL;
}
// Revert Jay's gai flag
if ( m_hPlayer )
m_hPlayer->SetPhysicsFlag( PFLAG_VPHYSICS_MOTIONCONTROLLER, false );
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CGrappleHook::CreateVPhysics( void )
{
// Create the object in the physics system
VPhysicsInitNormal( SOLID_BBOX, FSOLID_NOT_STANDABLE, false );
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
unsigned int CGrappleHook::PhysicsSolidMaskForEntity() const
{
return ( BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_HITBOX ) & ~CONTENTS_GRATE;
}
//-----------------------------------------------------------------------------
// Purpose: Spawn
//-----------------------------------------------------------------------------
void CGrappleHook::Spawn( void )
{
Precache( );
SetModel( HOOK_MODEL );
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
UTIL_SetSize( this, -Vector(1,1,1), Vector(1,1,1) );
SetSolid( SOLID_BBOX );
SetGravity( 0.05f );
// The rock is invisible, the crossbow bolt is the visual representation
AddEffects( EF_NODRAW );
// Make sure we're updated if we're underwater
UpdateWaterState();
SetTouch( &CGrappleHook::HookTouch );
SetThink( &CGrappleHook::FlyThink );
SetNextThink( gpGlobals->curtime + 0.1f );
m_pSpring = NULL;
m_fSpringLength = 0.0f;
m_bPlayerWasStanding = false;
}
void CGrappleHook::Precache( void )
{
PrecacheModel( HOOK_MODEL );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pOther -
//-----------------------------------------------------------------------------
void CGrappleHook::HookTouch( CBaseEntity *pOther )
{
if ( !pOther->IsSolid() || pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) )
return;
if ( (pOther != m_hOwner) && (pOther->m_takedamage != DAMAGE_NO) )
{
m_hOwner->NotifyHookDied();
SetTouch( NULL );
SetThink( NULL );
UTIL_Remove( this );
}
else
{
trace_t	tr;
tr = BaseClass::GetTouchTrace();
// See if we struck the world
if ( pOther->GetMoveType() == MOVETYPE_NONE && !( tr.surface.flags & SURF_SKY ) )
{
EmitSound( "Weapon_AR2.Reload_Push" );
// if what we hit is static architecture, can stay around for a while.
Vector vecDir = GetAbsVelocity();
//FIXME: We actually want to stick (with hierarchy) to what we've hit
SetMoveType( MOVETYPE_NONE );
Vector vForward;
AngleVectors( GetAbsAngles(), &vForward );
VectorNormalize ( vForward );
CEffectData data;
data.m_vOrigin = tr.endpos;
data.m_vNormal = vForward;
data.m_nEntIndex = 0;
// DispatchEffect( "Impact", data );
// AddEffects( EF_NODRAW );
SetTouch( NULL );
VPhysicsDestroyObject();
VPhysicsInitNormal( SOLID_VPHYSICS, FSOLID_NOT_STANDABLE, false );
AddSolidFlags( FSOLID_NOT_SOLID );
// SetMoveType( MOVETYPE_NONE );
if ( !m_hPlayer )
{
Assert( 0 );
return;
}
// Set Jay's gai flag
m_hPlayer->SetPhysicsFlag( PFLAG_VPHYSICS_MOTIONCONTROLLER, true );
//IPhysicsObject *pPhysObject = m_hPlayer->VPhysicsGetObject();
IPhysicsObject *pRootPhysObject = VPhysicsGetObject();
Assert( pRootPhysObject );
Assert( pPhysObject );
pRootPhysObject->EnableMotion( false );
// Root has huge mass, tip has little
pRootPhysObject->SetMass( VPHYSICS_MAX_MASS );
// pPhysObject->SetMass( 100 );
// float damping = 3;
// pPhysObject->SetDamping( &damping, &damping );
Vector origin = m_hPlayer->GetAbsOrigin();
Vector rootOrigin = GetAbsOrigin();
m_fSpringLength = (origin - rootOrigin).Length();
m_bPlayerWasStanding = ( ( m_hPlayer->GetFlags() & FL_DUCKING ) == 0 );
SetThink( &CGrappleHook::HookedThink );
SetNextThink( gpGlobals->curtime + 0.1f );
}
else
{
// Put a mark unless we've hit the sky
if ( ( tr.surface.flags & SURF_SKY ) == false )
{
UTIL_ImpactTrace( &tr, DMG_BULLET );
}
SetTouch( NULL );
SetThink( NULL );
m_hOwner->NotifyHookDied();
UTIL_Remove( this );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGrappleHook::HookedThink( void )
{
//set next globalthink
SetNextThink( gpGlobals->curtime + 0.05f ); //0.1f
//All of this push the player far from the hook
Vector tempVec1 = m_hPlayer->GetAbsOrigin() - GetAbsOrigin();
VectorNormalize(tempVec1);
int temp_multiplier = -1;
m_hPlayer->SetGravity(0.0f);
m_hPlayer->SetGroundEntity(NULL);
if (m_hOwner->m_bHook){
temp_multiplier = 1;
m_hPlayer->SetAbsVelocity(tempVec1*temp_multiplier*50);//400
}
else
{
m_hPlayer->SetAbsVelocity(tempVec1*temp_multiplier*400);//400
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGrappleHook::FlyThink( void )
{
QAngle angNewAngles;
VectorAngles( GetAbsVelocity(), angNewAngles );
SetAbsAngles( angNewAngles );
SetNextThink( gpGlobals->curtime + 0.1f );
}
#endif
IMPLEMENT_NETWORKCLASS_ALIASED( WeaponGrapple, DT_WeaponGrapple )
#ifdef CLIENT_DLL
void RecvProxy_HookDied( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
CWeaponGrapple *pGrapple = ((CWeaponGrapple*)pStruct);
RecvProxy_IntToEHandle( pData, pStruct, pOut );
CBaseEntity *pNewHook = pGrapple->GetHook();
if ( pNewHook == NULL )
{
if ( pGrapple->GetOwner() && pGrapple->GetOwner()->GetActiveWeapon() == pGrapple )
{
pGrapple->NotifyHookDied();
}
}
}
#endif
BEGIN_NETWORK_TABLE( CWeaponGrapple, DT_WeaponGrapple )
#ifdef CLIENT_DLL
RecvPropBool( RECVINFO( m_bInZoom ) ),
RecvPropBool( RECVINFO( m_bMustReload ) ),
RecvPropEHandle( RECVINFO( m_hHook ), RecvProxy_HookDied ),
RecvPropInt ( RECVINFO (m_nBulletType)),
#else
SendPropBool( SENDINFO( m_bInZoom ) ),
SendPropBool( SENDINFO( m_bMustReload ) ),
SendPropEHandle( SENDINFO( m_hHook ) ),
SendPropInt ( SENDINFO (m_nBulletType)),
#endif
END_NETWORK_TABLE()
#ifdef CLIENT_DLL
BEGIN_PREDICTION_DATA( CWeaponGrapple )
DEFINE_PRED_FIELD( m_bInZoom, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_bMustReload, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
END_PREDICTION_DATA()
#endif
LINK_ENTITY_TO_CLASS( weapon_grapple, CWeaponGrapple );
PRECACHE_WEAPON_REGISTER( weapon_grapple );
#ifndef CLIENT_DLL
acttable_t	CWeaponGrapple::m_acttable[] =
{
// { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_CROSSBOW, false },
// { ACT_HL2MP_RUN, ACT_HL2MP_RUN_CROSSBOW, false },
// { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_CROSSBOW, false },
// { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_CROSSBOW, false },
// { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_CROSSBOW, false },
// // Valve never made a crossbow reload anim for playermodels, I guess use the ar2 one for now
// //{ ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_CROSSBOW, false },
// { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_AR2, false },
// { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_CROSSBOW, false },
{ ACT_IDLE, ACT_IDLE, false },
{ ACT_RUN, ACT_RUN, false },
{ ACT_WALK_CROUCH, ACT_WALK_CROUCH, false },
{ ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD, false },
{ ACT_JUMP, ACT_JUMP, false },
};
IMPLEMENT_ACTTABLE(CWeaponGrapple);
#endif
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CWeaponGrapple::CWeaponGrapple( void )
{
m_bReloadsSingly = true;
m_bFiresUnderwater = true;
m_bInZoom = false;
m_bMustReload = false;
m_nBulletType = -1;
#ifndef CLIENT_DLL
m_pLightGlow= NULL;
pBeam = NULL;
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Precache
//-----------------------------------------------------------------------------
void CWeaponGrapple::Precache( void )
{
#ifndef CLIENT_DLL
UTIL_PrecacheOther( "grapple_hook" );
#endif
// PrecacheScriptSound( "Weapon_Crossbow.BoltHitBody" );
// PrecacheScriptSound( "Weapon_Crossbow.BoltHitWorld" );
// PrecacheScriptSound( "Weapon_Crossbow.BoltSkewer" );
PrecacheModel( "sprites/physbeam.vmt" );
PrecacheModel( "sprites/physcannon_bluecore2b.vmt" );
BaseClass::Precache();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::PrimaryAttack( void )
{
// Can't have an active hook out
if ( m_hHook != NULL )
return;
#ifndef CLIENT_DLL
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( !pPlayer )
{
return;
}
if ( m_iClip1 <= 0 )
{
if ( !m_bFireOnEmpty )
{
Reload();
}
else
{
WeaponSound( EMPTY );
m_flNextPrimaryAttack = 0.15;
}
return;
}
m_iPrimaryAttacks++;
gamestats->Event_WeaponFired( pPlayer, true, GetClassname() );
WeaponSound( SINGLE );
pPlayer->DoMuzzleFlash();
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
pPlayer->SetAnimation( PLAYER_ATTACK1 );
m_flNextPrimaryAttack = gpGlobals->curtime + 0.75;
m_flNextSecondaryAttack = gpGlobals->curtime + 0.75;
//Disabled so we can shoot all the time that we want
//m_iClip1--;
Vector vecSrc = pPlayer->Weapon_ShootPosition();
Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_SCALE_DEFAULT );
//We will not shoot bullets anymore
//pPlayer->FireBullets( 1, vecSrc, vecAiming, vec3_origin, MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 0 );
pPlayer->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 );
CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 600, 0.2, GetOwner() );
if ( !m_iClip1 && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
{
// HEV suit - indicate out of ammo condition
pPlayer->SetSuitUpdate( "!HEV_AMO0", FALSE, 0 );
}
trace_t tr;
Vector vecShootOrigin, vecShootDir, vecDir, vecEnd;
//Gets the direction where the player is aiming
AngleVectors (pPlayer->EyeAngles(), &vecDir);
//Gets the position of the player
vecShootOrigin = pPlayer->Weapon_ShootPosition();
//Gets the position where the hook will hit
vecEnd = vecShootOrigin + (vecDir * MAX_TRACE_LENGTH);
//Traces a line between the two vectors
UTIL_TraceLine( vecShootOrigin, vecEnd, MASK_SHOT, pPlayer, COLLISION_GROUP_NONE, &tr);
//Draws the beam
DrawBeam( vecShootOrigin, tr.endpos, 15.5 );
//Creates an energy impact effect if we don't hit the sky or other places
if ( (tr.surface.flags & SURF_SKY) == false )
{
CPVSFilter filter( tr.endpos );
te->GaussExplosion( filter, 0.0f, tr.endpos, tr.plane.normal, 0 );
m_nBulletType = GetAmmoDef()->Index("GaussEnergy");
UTIL_ImpactTrace( &tr, m_nBulletType );
//Makes a sprite at the end of the beam
m_pLightGlow = CSprite::SpriteCreate( "sprites/physcannon_bluecore2b.vmt", GetAbsOrigin(), TRUE);
//Sets FX render and color
m_pLightGlow->SetTransparency( 9, 255, 255, 255, 200, kRenderFxNoDissipation );
//Sets the position
m_pLightGlow->SetAbsOrigin(tr.endpos);
//Bright
m_pLightGlow->SetBrightness( 255 );
//Scale
m_pLightGlow->SetScale( 0.65 );
}
#endif
FireHook();
SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration( ACT_VM_PRIMARYATTACK ) );
}
//-----------------------------------------------------------------------------
// Purpose: Toggles the grapple hook modes
//-----------------------------------------------------------------------------
void CWeaponGrapple::SecondaryAttack( void )
{
ToggleHook();
WeaponSound( EMPTY );
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponGrapple::Reload( void )
{
if ( ( m_bMustReload ) && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) )
{
//Redraw the weapon
SendWeaponAnim( ACT_VM_IDLE ); //ACT_VM_RELOAD
//Update our times
m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
//Mark this as done
m_bMustReload = false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Toggles between pull and rappel mode
//-----------------------------------------------------------------------------
bool CWeaponGrapple::ToggleHook( void )
{
#ifndef CLIENT_DLL
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( m_bHook )
{
m_bHook = false;
ClientPrint(pPlayer,HUD_PRINTCENTER, "Pull mode");
return m_bHook;
}
else
{
m_bHook = true;
ClientPrint(pPlayer,HUD_PRINTCENTER, "Rappel mode");
return m_bHook;
}
#endif
return m_bHook;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::ItemBusyFrame( void )
{
// Allow zoom toggling even when we're reloading
//CheckZoomToggle();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::ItemPostFrame( void )
{
//Enforces being able to use PrimaryAttack and Secondary Attack
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
if ( ( pOwner->m_nButtons & IN_ATTACK ) )
{
if ( m_flNextPrimaryAttack < gpGlobals->curtime )
{
PrimaryAttack();
}
}
else if ( m_bMustReload ) //&& HasWeaponIdleTimeElapsed() )
{
Reload();
}
if ( ( pOwner->m_afButtonPressed & IN_ATTACK2 ) )
{
if ( m_flNextPrimaryAttack < gpGlobals->curtime )
{
SecondaryAttack();
}
}
else if ( m_bMustReload ) //&& HasWeaponIdleTimeElapsed() )
{
Reload();
}
//Allow a refire as fast as the player can click
if ( ( ( pOwner->m_nButtons & IN_ATTACK ) == false ) )
{
m_flNextPrimaryAttack = gpGlobals->curtime - 0.1f;
}
#ifndef CLIENT_DLL
if ( m_hHook )
{
if ( !(pOwner->m_nButtons & IN_ATTACK) && !(pOwner->m_nButtons & IN_ATTACK2))
{
m_hHook->SetTouch( NULL );
m_hHook->SetThink( NULL );
UTIL_Remove( m_hHook );
m_hHook = NULL;
NotifyHookDied();
m_bMustReload = true;
}
}
#endif
// BaseClass::ItemPostFrame();
}
//-----------------------------------------------------------------------------
// Purpose: Fires the hook
//-----------------------------------------------------------------------------
void CWeaponGrapple::FireHook( void )
{
if ( m_bMustReload )
return;
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
if ( pOwner == NULL )
return;
#ifndef CLIENT_DLL
Vector vecAiming = pOwner->GetAutoaimVector( 0 );
Vector vecSrc = pOwner->Weapon_ShootPosition();
QAngle angAiming;
VectorAngles( vecAiming, angAiming );
CGrappleHook *pHook = CGrappleHook::HookCreate( vecSrc, angAiming, this );
if ( pOwner->GetWaterLevel() == 3 )
{
pHook->SetAbsVelocity( vecAiming * BOLT_WATER_VELOCITY );
}
else
{
pHook->SetAbsVelocity( vecAiming * BOLT_AIR_VELOCITY );
}
m_hHook = pHook;
#endif
pOwner->ViewPunch( QAngle( -2, 0, 0 ) );
//WeaponSound( SINGLE );
//WeaponSound( SPECIAL2 );
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + 0.75;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponGrapple::Deploy( void )
{
if ( m_bMustReload )
{
return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_CROSSBOW_DRAW_UNLOADED, (char*)GetAnimPrefix() );
}
return BaseClass::Deploy();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pSwitchingTo -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponGrapple::Holster( CBaseCombatWeapon *pSwitchingTo )
{
#ifndef CLIENT_DLL
if ( m_hHook )
{
m_hHook->SetTouch( NULL );
m_hHook->SetThink( NULL );
UTIL_Remove( m_hHook );
m_hHook = NULL;
NotifyHookDied();
m_bMustReload = true;
}
#endif
return BaseClass::Holster( pSwitchingTo );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::Drop( const Vector &vecVelocity )
{
#ifndef CLIENT_DLL
if ( m_hHook )
{
m_hHook->SetTouch( NULL );
m_hHook->SetThink( NULL );
UTIL_Remove( m_hHook );
m_hHook = NULL;
NotifyHookDied();
m_bMustReload = true;
}
#endif
BaseClass::Drop( vecVelocity );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CWeaponGrapple::HasAnyAmmo( void )
{
if ( m_hHook != NULL )
return true;
return BaseClass::HasAnyAmmo();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CWeaponGrapple::CanHolster( void )
{
//Can't have an active hook out
if ( m_hHook != NULL )
return false;
return BaseClass::CanHolster();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::NotifyHookDied( void )
{
m_hHook = NULL;
#ifndef CLIENT_DLL
if ( pBeam )
{
UTIL_Remove( pBeam ); //Kill beam
pBeam = NULL;
UTIL_Remove( m_pLightGlow ); //Kill sprite
m_pLightGlow = NULL;
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Draws a beam
// Input : &startPos - where the beam should begin
// &endPos - where the beam should end
// width - what the diameter of the beam should be (units?)
//-----------------------------------------------------------------------------
void CWeaponGrapple::DrawBeam( const Vector &startPos, const Vector &endPos, float width )
{
#ifndef CLIENT_DLL
//Tracer down the middle (NOT NEEDED, IT WILL FIRE A TRACER)
//UTIL_Tracer( startPos, endPos, 0, TRACER_DONT_USE_ATTACHMENT, 6500, false, "GaussTracer" );
trace_t tr;
//Draw the main beam shaft
pBeam = CBeam::BeamCreate( "sprites/physbeam.vmt", 15.5 );
// It starts at startPos
pBeam->SetStartPos( startPos );
// This sets up some things that the beam uses to figure out where
// it should start and end
pBeam->PointEntInit( endPos, this );
// This makes it so that the beam appears to come from the muzzle of the pistol
pBeam->SetEndAttachment( LookupAttachment("Muzzle") );
pBeam->SetWidth( width );
// pBeam->SetEndWidth( 0.05f );
// Higher brightness means less transparent
pBeam->SetBrightness( 255 );
//pBeam->SetColor( 255, 185+random->RandomInt( -16, 16 ), 40 );
pBeam->RelinkBeam();
//Sets scrollrate of the beam sprite
float scrollOffset = gpGlobals->curtime + 5.5;
pBeam->SetScrollRate(scrollOffset);
// The beam should only exist for a very short time
//pBeam->LiveForTime( 0.1f );
UpdateWaterState();
SetTouch( &CGrappleHook::HookTouch );
SetThink( &CGrappleHook::FlyThink );
SetNextThink( gpGlobals->curtime + 0.1f );
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &tr - used to figure out where to do the effect
// nDamageType - ???
//-----------------------------------------------------------------------------
void CWeaponGrapple::DoImpactEffect( trace_t &tr, int nDamageType )
{
#ifndef CLIENT_DLL
if ( (tr.surface.flags & SURF_SKY) == false )
{
CPVSFilter filter( tr.endpos );
te->GaussExplosion( filter, 0.0f, tr.endpos, tr.plane.normal, 0 );
m_nBulletType = GetAmmoDef()->Index("GaussEnergy");
UTIL_ImpactTrace( &tr, m_nBulletType );
}
#endif
}

server/hl2/weapon_grapple.h

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements the grapple hook weapon.
//
// Primary attack: fires a beam that hooks on a surface.
// Secondary attack: switches between pull and rapple modes
//
//
//=============================================================================//
#ifndef WEAPON_GRAPPLE_H
#define WEAPON_GRAPPLE_H
#ifdef _WIN32
#pragma once
#endif
//#include "weapon_hl2mpbasehlmpcombatweapon.h"
#include "basehlcombatweapon.h"
#include "sprite.h"
#include "props.h"
#ifndef CLIENT_DLL
#include "rope.h"
#include "props.h"
#endif
#include "rope_shared.h"
#include "te_effect_dispatch.h"
#include "beam_shared.h"
#ifndef CLIENT_DLL
class CWeaponGrapple;
//-----------------------------------------------------------------------------
// Grapple Hook
//-----------------------------------------------------------------------------
class CGrappleHook : public CBaseCombatCharacter
{
DECLARE_CLASS( CGrappleHook, CBaseCombatCharacter );
public:
CGrappleHook() { };
~CGrappleHook();
Class_T Classify( void ) { return CLASS_NONE; }
public:
void Spawn( void );
void Precache( void );
void FlyThink( void );
void HookedThink( void );
void HookTouch( CBaseEntity *pOther );
bool CreateVPhysics( void );
unsigned int PhysicsSolidMaskForEntity() const;
static CGrappleHook *HookCreate( const Vector &vecOrigin, const QAngle &angAngles, CBaseEntity *pentOwner = NULL );
protected:
DECLARE_DATADESC();
private:
CHandle<CWeaponGrapple> m_hOwner;
CHandle<CBasePlayer> m_hPlayer;
CHandle<CDynamicProp> m_hBolt;
IPhysicsSpring *m_pSpring;
float m_fSpringLength;
bool m_bPlayerWasStanding;
};
#endif
//-----------------------------------------------------------------------------
// CWeaponGrapple
//-----------------------------------------------------------------------------
#ifdef CLIENT_DLL
#define CWeaponGrapple C_WeaponGrapple
#endif
//class CWeaponGrapple : public CBaseHL2MPCombatWeapon
class CWeaponGrapple : public CBaseHLCombatWeapon
{
// DECLARE_CLASS( CWeaponGrapple, CBaseHL2MPCombatWeapon );
DECLARE_CLASS( CWeaponGrapple, CBaseHLCombatWeapon );
public:
CWeaponGrapple( void );
virtual void Precache( void );
virtual void PrimaryAttack( void );
virtual void SecondaryAttack( void );
virtual bool Deploy( void );
bool CanHolster( void );
virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
void Drop( const Vector &vecVelocity );
virtual bool Reload( void );
virtual void ItemPostFrame( void );
virtual void ItemBusyFrame( void );
void NotifyHookDied( void );
bool HasAnyAmmo( void );
CBaseEntity *GetHook( void ) { return m_hHook; }
bool ToggleHook( void );
void DrawBeam( const Vector &startPos, const Vector &endPos, float width );
void	DoImpactEffect( trace_t &tr, int nDamageType );
bool m_bHook;
DECLARE_NETWORKCLASS();
DECLARE_PREDICTABLE();
private:
void FireHook( void );
#ifndef CLIENT_DLL
DECLARE_ACTTABLE();
#endif
private:
#ifndef CLIENT_DLL
CHandle<CBeam> pBeam;
CHandle<CSprite> m_pLightGlow;
#endif
CNetworkVar( bool, m_bInZoom );
CNetworkVar( bool, m_bMustReload );
CNetworkHandle( CBaseEntity, m_hHook );
CWeaponGrapple( const CWeaponGrapple & );
CNetworkVar( int, m_nBulletType );
};
#endif // WEAPON_GRAPPLE_H

Multiplayer code

client/hl2/c_weapon_grapple.cpp

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements the grapple hook weapon.
//
// Primary attack: fires a beam that hooks on a surface.
// Secondary attack: switches between pull and rapple modes
//
//
//=============================================================================//
#include "cbase.h"
#include "npcevent.h"
#include "in_buttons.h"
#include "c_weapon_grapple.h"
#ifdef CLIENT_DLL
#include "c_hl2mp_player.h"
// #include "player.h"
#include "c_te_effect_dispatch.h"
// #include "c_te_effect_dispatch.h"
#else
#include "game.h"
#include "hl2mp_player.h"
#include "player.h"
#include "c_te_effect_dispatch.h"
#include "IEffects.h"
#include "Sprite.h"
#include "SpriteTrail.h"
#include "beam_shared.h"
//#include "explode.h"
#include "ammodef.h"	/* This is needed for the tracing done later */
#include "gamestats.h" //
#include "soundent.h" //
#include "vphysics/constraints.h"
#include "physics_saverestore.h"
#endif
//#include "effect_dispatch_data.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define HOOK_MODEL	"models/props_junk/rock001a.mdl"
#define BOLT_AIR_VELOCITY	3500
#define BOLT_WATER_VELOCITY	1500
#ifndef CLIENT_DLL
LINK_ENTITY_TO_CLASS( grapple_hook, CGrappleHook );
BEGIN_DATADESC( CGrappleHook )
// Function Pointers
DEFINE_THINKFUNC( FlyThink ),
DEFINE_THINKFUNC( HookedThink ),
DEFINE_FUNCTION( HookTouch ),
DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ),
DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
DEFINE_FIELD( m_hBolt, FIELD_EHANDLE ),
DEFINE_FIELD( m_bPlayerWasStanding, FIELD_BOOLEAN ),
END_DATADESC()
CGrappleHook *CGrappleHook::HookCreate( const Vector &vecOrigin, const QAngle &angAngles, CBaseEntity *pentOwner )
{
// Create a new entity with CGrappleHook private data
CGrappleHook *pHook = (CGrappleHook *)CreateEntityByName( "grapple_hook" );
UTIL_SetOrigin( pHook, vecOrigin );
pHook->SetAbsAngles( angAngles );
pHook->Spawn();
CWeaponGrapple *pOwner = (CWeaponGrapple *)pentOwner;
pHook->m_hOwner = pOwner;
pHook->SetOwnerEntity( pOwner->GetOwner() );
pHook->m_hPlayer = (CBasePlayer *)pOwner->GetOwner();
return pHook;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CGrappleHook::~CGrappleHook( void )
{
if ( m_hBolt )
{
UTIL_Remove( m_hBolt );
m_hBolt = NULL;
}
// Revert Jay's gai flag
if ( m_hPlayer )
m_hPlayer->SetPhysicsFlag( PFLAG_VPHYSICS_MOTIONCONTROLLER, false );
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CGrappleHook::CreateVPhysics( void )
{
// Create the object in the physics system
VPhysicsInitNormal( SOLID_BBOX, FSOLID_NOT_STANDABLE, false );
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
unsigned int CGrappleHook::PhysicsSolidMaskForEntity() const
{
return ( BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_HITBOX ) & ~CONTENTS_GRATE;
}
//-----------------------------------------------------------------------------
// Purpose: Spawn
//-----------------------------------------------------------------------------
void CGrappleHook::Spawn( void )
{
Precache( );
SetModel( HOOK_MODEL );
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
UTIL_SetSize( this, -Vector(1,1,1), Vector(1,1,1) );
SetSolid( SOLID_BBOX );
SetGravity( 0.05f );
// The rock is invisible, the crossbow bolt is the visual representation
AddEffects( EF_NODRAW );
// Make sure we're updated if we're underwater
UpdateWaterState();
SetTouch( &CGrappleHook::HookTouch );
SetThink( &CGrappleHook::FlyThink );
SetNextThink( gpGlobals->curtime + 0.1f );
m_pSpring = NULL;
m_fSpringLength = 0.0f;
m_bPlayerWasStanding = false;
}
void CGrappleHook::Precache( void )
{
PrecacheModel( HOOK_MODEL );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pOther -
//-----------------------------------------------------------------------------
void CGrappleHook::HookTouch( CBaseEntity *pOther )
{
if ( !pOther->IsSolid() || pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) )
return;
if ( (pOther != m_hOwner) && (pOther->m_takedamage != DAMAGE_NO) )
{
m_hOwner->NotifyHookDied();
SetTouch( NULL );
SetThink( NULL );
UTIL_Remove( this );
}
else
{
trace_t	tr;
tr = BaseClass::GetTouchTrace();
// See if we struck the world
if ( pOther->GetMoveType() == MOVETYPE_NONE && !( tr.surface.flags & SURF_SKY ) )
{
EmitSound( "Weapon_AR2.Reload_Push" );
// if what we hit is static architecture, can stay around for a while.
Vector vecDir = GetAbsVelocity();
//FIXME: We actually want to stick (with hierarchy) to what we've hit
SetMoveType( MOVETYPE_NONE );
Vector vForward;
AngleVectors( GetAbsAngles(), &vForward );
VectorNormalize ( vForward );
CEffectData data;
data.m_vOrigin = tr.endpos;
data.m_vNormal = vForward;
data.m_nEntIndex = 0;
// DispatchEffect( "Impact", data );
// AddEffects( EF_NODRAW );
SetTouch( NULL );
VPhysicsDestroyObject();
VPhysicsInitNormal( SOLID_VPHYSICS, FSOLID_NOT_STANDABLE, false );
AddSolidFlags( FSOLID_NOT_SOLID );
// SetMoveType( MOVETYPE_NONE );
if ( !m_hPlayer )
{
Assert( 0 );
return;
}
// Set Jay's gai flag
m_hPlayer->SetPhysicsFlag( PFLAG_VPHYSICS_MOTIONCONTROLLER, true );
//IPhysicsObject *pPhysObject = m_hPlayer->VPhysicsGetObject();
IPhysicsObject *pRootPhysObject = VPhysicsGetObject();
Assert( pRootPhysObject );
Assert( pPhysObject );
pRootPhysObject->EnableMotion( false );
// Root has huge mass, tip has little
pRootPhysObject->SetMass( VPHYSICS_MAX_MASS );
// pPhysObject->SetMass( 100 );
// float damping = 3;
// pPhysObject->SetDamping( &damping, &damping );
Vector origin = m_hPlayer->GetAbsOrigin();
Vector rootOrigin = GetAbsOrigin();
m_fSpringLength = (origin - rootOrigin).Length();
m_bPlayerWasStanding = ( ( m_hPlayer->GetFlags() & FL_DUCKING ) == 0 );
SetThink( &CGrappleHook::HookedThink );
SetNextThink( gpGlobals->curtime + 0.1f );
}
else
{
// Put a mark unless we've hit the sky
/*if ( ( tr.surface.flags & SURF_SKY ) == false )
{
UTIL_ImpactTrace( &tr, DMG_BULLET );
}*/
SetTouch( NULL );
SetThink( NULL );
m_hOwner->NotifyHookDied();
UTIL_Remove( this );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGrappleHook::HookedThink( void )
{
//set next globalthink
SetNextThink( gpGlobals->curtime + 0.05f ); //0.1f
//All of this push the player far from the hook
Vector tempVec1 = m_hPlayer->GetAbsOrigin() - GetAbsOrigin();
VectorNormalize(tempVec1);
int temp_multiplier = -1;
m_hPlayer->SetGravity(0.0f);
m_hPlayer->SetGroundEntity(NULL);
if (m_hOwner->m_bHook){
//temp_multiplier = 1;
m_hPlayer->SetAbsVelocity(tempVec1*temp_multiplier*15);//50
}
else
{
m_hPlayer->SetAbsVelocity(tempVec1*temp_multiplier*300);//400
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGrappleHook::FlyThink( void )
{
QAngle angNewAngles;
VectorAngles( GetAbsVelocity(), angNewAngles );
SetAbsAngles( angNewAngles );
SetNextThink( gpGlobals->curtime + 0.1f );
}
#endif
//LINK_ENTITY_TO_CLASS(weapon_grapple, CWeaponGrapple);
IMPLEMENT_NETWORKCLASS_ALIASED( WeaponGrapple, DT_WeaponGrapple )
/*IMPLEMENT_CLIENTCLASS_DT(CWeaponGrapple, DT_WeaponGrapple)
RecvPropBool(RECVINFO(m_bInZoom)),
RecvPropBool(RECVINFO(m_bMustReload)),
RecvPropEHandle(RECVINFO(m_hHook), RecvProxy_HookDied),
RecvPropInt(RECVINFO(m_nBulletType)),
END_RECV_TABLE()*/
#ifdef CLIENT_DLL
void RecvProxy_HookDied( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
CWeaponGrapple *pGrapple = ((CWeaponGrapple*)pStruct);
RecvProxy_IntToEHandle( pData, pStruct, pOut );
CBaseEntity *pNewHook = pGrapple->GetHook();
if ( pNewHook == NULL )
{
if ( pGrapple->GetOwner() && pGrapple->GetOwner()->GetActiveWeapon() == pGrapple )
{
pGrapple->NotifyHookDied();
}
}
}
#endif
BEGIN_NETWORK_TABLE( CWeaponGrapple, DT_WeaponGrapple )
#ifdef CLIENT_DLL
RecvPropBool( RECVINFO( m_bInZoom ) ),
RecvPropBool( RECVINFO( m_bMustReload ) ),
RecvPropEHandle( RECVINFO( m_hHook ), RecvProxy_HookDied ),
RecvPropInt ( RECVINFO (m_nBulletType)),
#else
SendPropBool( SENDINFO( m_bInZoom ) ),
SendPropBool( SENDINFO( m_bMustReload ) ),
SendPropEHandle( SENDINFO( m_hHook ) ),
SendPropInt ( SENDINFO (m_nBulletType)),
#endif
END_NETWORK_TABLE()
#ifdef CLIENT_DLL
BEGIN_PREDICTION_DATA( CWeaponGrapple )
DEFINE_PRED_FIELD( m_bInZoom, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_bMustReload, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
END_PREDICTION_DATA()
#endif
LINK_ENTITY_TO_CLASS( weapon_grapple, CWeaponGrapple );
PRECACHE_WEAPON_REGISTER( weapon_grapple );
#ifndef CLIENT_DLL
acttable_t	CWeaponGrapple::m_acttable[] =
{
{ ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_PHYSGUN, false },
{ ACT_HL2MP_RUN, ACT_HL2MP_RUN_PHYSGUN, false },
{ ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_PHYSGUN, false },
{ ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_PHYSGUN, false },
{ ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_PHYSGUN, false },
{ ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_PHYSGUN, false },
{ ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_PHYSGUN, false },
// { ACT_IDLE, ACT_IDLE, false },
// { ACT_RUN, ACT_RUN, false },
// { ACT_WALK_CROUCH, ACT_WALK_CROUCH, false },
// { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD, false },
// { ACT_JUMP, ACT_JUMP, false },
};
IMPLEMENT_ACTTABLE(CWeaponGrapple);
#endif
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CWeaponGrapple::CWeaponGrapple( void )
{
m_bReloadsSingly = true;
m_bFiresUnderwater = true;
m_bInZoom = false;
m_bMustReload = false;
m_nBulletType = -1;
#ifndef CLIENT_DLL
m_pLightGlow= NULL;
pBeam = NULL;
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Precache
//-----------------------------------------------------------------------------
void CWeaponGrapple::Precache( void )
{
#ifndef CLIENT_DLL
UTIL_PrecacheOther( "grapple_hook" );
#endif
PrecacheModel( "sprites/physbeam.vmt" );
PrecacheModel( "sprites/physcannon_bluecore2b.vmt" );
BaseClass::Precache();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::PrimaryAttack( void )
{
// Can't have an active hook out
if ( m_hHook != NULL )
return;
#ifndef CLIENT_DLL
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( !pPlayer )
{
return;
}
//Disabled on MP it makes think the weapon that it needs to reload
/*if ( m_iClip1 <= 0 )
{
if ( !m_bFireOnEmpty )
{
Reload();
}
else
{
WeaponSound( EMPTY );
m_flNextPrimaryAttack = 0.15;
}
return;
}*/
//m_iPrimaryAttacks++;
gamestats->Event_WeaponFired( pPlayer, true, GetClassname() );
//Obligatory for MP so the sound can be played
CDisablePredictionFiltering disabler;
WeaponSound( SINGLE );
pPlayer->DoMuzzleFlash();
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
pPlayer->SetAnimation( PLAYER_ATTACK1 );
m_flNextPrimaryAttack = gpGlobals->curtime + 0.75;
m_flNextSecondaryAttack = gpGlobals->curtime + 0.75;
//Disabled so we can shoot all the time that we want
//m_iClip1--;
Vector vecSrc = pPlayer->Weapon_ShootPosition();
Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_SCALE_DEFAULT );
//We will not shoot bullets anymore
//pPlayer->FireBullets( 1, vecSrc, vecAiming, vec3_origin, MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 0 );
pPlayer->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 );
CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 600, 0.2, GetOwner() );
if ( !m_iClip1 && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
{
// HEV suit - indicate out of ammo condition
pPlayer->SetSuitUpdate( "!HEV_AMO0", FALSE, 0 );
}
trace_t tr;
Vector vecShootOrigin, vecShootDir, vecDir, vecEnd;
//Gets the direction where the player is aiming
AngleVectors (pPlayer->EyeAngles(), &vecDir);
//Gets the position of the player
vecShootOrigin = pPlayer->Weapon_ShootPosition();
//Gets the position where the hook will hit
vecEnd = vecShootOrigin + (vecDir * MAX_TRACE_LENGTH);
//Traces a line between the two vectors
UTIL_TraceLine( vecShootOrigin, vecEnd, MASK_SHOT, pPlayer, COLLISION_GROUP_NONE, &tr);
//Draws the beam
DrawBeam( vecShootOrigin, tr.endpos, 15.5 );
//Creates an energy impact effect if we don't hit the sky or other places
if ( (tr.surface.flags & SURF_SKY) == false )
{
/*CPVSFilter filter( tr.endpos );
te->GaussExplosion( filter, 0.0f, tr.endpos, tr.plane.normal, 0 );
m_nBulletType = GetAmmoDef()->Index("GaussEnergy");
UTIL_ImpactTrace( &tr, m_nBulletType );*/
//Makes a sprite at the end of the beam
m_pLightGlow = CSprite::SpriteCreate( "sprites/physcannon_bluecore2b.vmt", GetAbsOrigin(), TRUE);
//Sets FX render and color
m_pLightGlow->SetTransparency( 9, 255, 255, 255, 200, kRenderFxNoDissipation );
//Sets the position
m_pLightGlow->SetAbsOrigin(tr.endpos);
//Bright
m_pLightGlow->SetBrightness( 255 );
//Scale
m_pLightGlow->SetScale( 0.65 );
}
#endif
FireHook();
SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration( ACT_VM_PRIMARYATTACK ) );
}
//-----------------------------------------------------------------------------
// Purpose: Toggles the grapple hook modes
//-----------------------------------------------------------------------------
void CWeaponGrapple::SecondaryAttack( void )
{
ToggleHook();
WeaponSound(EMPTY);
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponGrapple::Reload( void )
{
if ( ( m_bMustReload ) && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) )
{
//Redraw the weapon
SendWeaponAnim( ACT_VM_IDLE ); //ACT_VM_RELOAD
//Update our times
m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
//Mark this as done
m_bMustReload = false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Toggles between pull and rappel mode
//-----------------------------------------------------------------------------
bool CWeaponGrapple::ToggleHook( void )
{
#ifndef CLIENT_DLL
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( m_bHook )
{
m_bHook = false;
ClientPrint(pPlayer,HUD_PRINTCENTER, "Pull mode");
return m_bHook;
}
else
{
m_bHook = true;
ClientPrint(pPlayer,HUD_PRINTCENTER, "Rappel mode");
return m_bHook;
}
#endif
return m_bHook;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::ItemBusyFrame( void )
{
// Allow zoom toggling even when we're reloading
//CheckZoomToggle();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::ItemPostFrame( void )
{
//Enforces being able to use PrimaryAttack and Secondary Attack
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
if ( ( pOwner->m_nButtons & IN_ATTACK ) )
{
if ( m_flNextPrimaryAttack < gpGlobals->curtime )
{
PrimaryAttack();
}
}
else if ( m_bMustReload ) //&& HasWeaponIdleTimeElapsed() )
{
Reload();
}
if ( ( pOwner->m_afButtonPressed & IN_ATTACK2 ) )
{
if ( m_flNextPrimaryAttack < gpGlobals->curtime )
{
SecondaryAttack();
}
}
else if ( m_bMustReload ) //&& HasWeaponIdleTimeElapsed() )
{
Reload();
}
//Allow a refire as fast as the player can click
if ( ( ( pOwner->m_nButtons & IN_ATTACK ) == false ) )
{
m_flNextPrimaryAttack = gpGlobals->curtime - 0.1f;
}
#ifndef CLIENT_DLL
if ( m_hHook )
{
if ( !(pOwner->m_nButtons & IN_ATTACK) && !(pOwner->m_nButtons & IN_ATTACK2))
{
m_hHook->SetTouch( NULL );
m_hHook->SetThink( NULL );
UTIL_Remove( m_hHook );
m_hHook = NULL;
NotifyHookDied();
m_bMustReload = true;
}
}
#endif
// BaseClass::ItemPostFrame();
}
//-----------------------------------------------------------------------------
// Purpose: Fires the hook
//-----------------------------------------------------------------------------
void CWeaponGrapple::FireHook( void )
{
if ( m_bMustReload )
return;
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
if ( pOwner == NULL )
return;
#ifndef CLIENT_DLL
Vector vecAiming = pOwner->GetAutoaimVector( 0 );
Vector vecSrc = pOwner->Weapon_ShootPosition();
QAngle angAiming;
VectorAngles( vecAiming, angAiming );
CGrappleHook *pHook = CGrappleHook::HookCreate( vecSrc, angAiming, this );
if ( pOwner->GetWaterLevel() == 3 )
{
pHook->SetAbsVelocity( vecAiming * BOLT_WATER_VELOCITY );
}
else
{
pHook->SetAbsVelocity( vecAiming * BOLT_AIR_VELOCITY );
}
m_hHook = pHook;
#endif
pOwner->ViewPunch( QAngle( -2, 0, 0 ) );
//WeaponSound( SINGLE );
//WeaponSound( SPECIAL2 );
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + 0.75;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponGrapple::Deploy( void )
{
if ( m_bMustReload )
{
return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_CROSSBOW_DRAW_UNLOADED, (char*)GetAnimPrefix() );
}
return BaseClass::Deploy();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pSwitchingTo -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponGrapple::Holster( CBaseCombatWeapon *pSwitchingTo )
{
#ifndef CLIENT_DLL
if ( m_hHook )
{
m_hHook->SetTouch( NULL );
m_hHook->SetThink( NULL );
UTIL_Remove( m_hHook );
m_hHook = NULL;
NotifyHookDied();
m_bMustReload = true;
}
#endif
return BaseClass::Holster( pSwitchingTo );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::Drop( const Vector &vecVelocity )
{
#ifndef CLIENT_DLL
if ( m_hHook )
{
m_hHook->SetTouch( NULL );
m_hHook->SetThink( NULL );
UTIL_Remove( m_hHook );
m_hHook = NULL;
NotifyHookDied();
m_bMustReload = true;
}
#endif
BaseClass::Drop( vecVelocity );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CWeaponGrapple::HasAnyAmmo( void )
{
if ( m_hHook != NULL )
return true;
return BaseClass::HasAnyAmmo();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CWeaponGrapple::CanHolster( void )
{
//Can't have an active hook out
if ( m_hHook != NULL )
return false;
return BaseClass::CanHolster();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::NotifyHookDied( void )
{
m_hHook = NULL;
#ifndef CLIENT_DLL
if ( pBeam )
{
UTIL_Remove( pBeam ); //Kill beam
pBeam = NULL;
UTIL_Remove( m_pLightGlow ); //Kill sprite
m_pLightGlow = NULL;
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Draws a beam
// Input : &startPos - where the beam should begin
// &endPos - where the beam should end
// width - what the diameter of the beam should be (units?)
//-----------------------------------------------------------------------------
void CWeaponGrapple::DrawBeam( const Vector &startPos, const Vector &endPos, float width )
{
#ifndef CLIENT_DLL
//Tracer down the middle (NOT NEEDED, IT WILL FIRE A TRACER)
//UTIL_Tracer( startPos, endPos, 0, TRACER_DONT_USE_ATTACHMENT, 6500, false, "GaussTracer" );
trace_t tr;
//Draw the main beam shaft
pBeam = CBeam::BeamCreate( "sprites/physbeam.vmt", 15.5 );
// It starts at startPos
pBeam->SetStartPos( startPos );
// This sets up some things that the beam uses to figure out where
// it should start and end
pBeam->PointEntInit( endPos, this );
// This makes it so that the beam appears to come from the muzzle of the pistol
pBeam->SetEndAttachment( LookupAttachment("Muzzle") );
pBeam->SetWidth( width );
// pBeam->SetEndWidth( 0.05f );
// Higher brightness means less transparent
pBeam->SetBrightness( 255 );
//pBeam->SetColor( 255, 185+random->RandomInt( -16, 16 ), 40 );
pBeam->RelinkBeam();
//Sets scrollrate of the beam sprite
float scrollOffset = gpGlobals->curtime + 5.5;
pBeam->SetScrollRate(scrollOffset);
// The beam should only exist for a very short time
//pBeam->LiveForTime( 0.1f );
UpdateWaterState();
SetTouch( &CGrappleHook::HookTouch );
SetThink( &CGrappleHook::FlyThink );
SetNextThink( gpGlobals->curtime + 0.1f );
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &tr - used to figure out where to do the effect
// nDamageType - ???
//-----------------------------------------------------------------------------
void CWeaponGrapple::DoImpactEffect( trace_t &tr, int nDamageType )
{
#ifndef CLIENT_DLL
if ( (tr.surface.flags & SURF_SKY) == false )
{
CPVSFilter filter( tr.endpos );
te->GaussExplosion( filter, 0.0f, tr.endpos, tr.plane.normal, 0 );
m_nBulletType = GetAmmoDef()->Index("GaussEnergy");
UTIL_ImpactTrace( &tr, m_nBulletType );
}
#endif
}

client/hl2/c_weapon_grapple.h

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements the grapple hook weapon.
//
// Primary attack: fires a beam that hooks on a surface.
// Secondary attack: switches between pull and rapple modes
//
//
//=============================================================================//
#ifndef WEAPON_GRAPPLE_H
#define WEAPON_GRAPPLE_H
#ifdef _WIN32
#pragma once
#endif
#include "weapon_hl2mpbasehlmpcombatweapon.h"
//#include "c_basehlcombatweapon.h"
#include "sprite.h"
#ifndef CLIENT_DLL
#include "rope.h"
#include "props.h"
#endif
#include "rope_shared.h"
#include "c_te_effect_dispatch.h"
#include "beam_shared.h"
#ifndef CLIENT_DLL
class CWeaponGrapple;
//-----------------------------------------------------------------------------
// Grapple Hook
//-----------------------------------------------------------------------------
class CGrappleHook : public CBaseCombatCharacter
{
DECLARE_CLASS( CGrappleHook, CBaseCombatCharacter );
public:
CGrappleHook() { };
~CGrappleHook();
Class_T Classify( void ) { return CLASS_NONE; }
public:
void Spawn( void );
void Precache( void );
void FlyThink( void );
void HookedThink( void );
void HookTouch( CBaseEntity *pOther );
bool CreateVPhysics( void );
unsigned int PhysicsSolidMaskForEntity() const;
static CGrappleHook *HookCreate( const Vector &vecOrigin, const QAngle &angAngles, CBaseEntity *pentOwner = NULL );
protected:
DECLARE_DATADESC();
private:
CHandle<CWeaponGrapple> m_hOwner;
CHandle<CBasePlayer> m_hPlayer;
CHandle<CDynamicProp> m_hBolt;
IPhysicsSpring *m_pSpring;
float	m_fSpringLength;
bool	m_bPlayerWasStanding;
};
#endif
//-----------------------------------------------------------------------------
// CWeaponGrapple
//-----------------------------------------------------------------------------
#ifdef CLIENT_DLL
#define CWeaponGrapple C_WeaponGrapple
#endif
class CWeaponGrapple : public CBaseHL2MPCombatWeapon
//class CWeaponGrapple : public CBaseHLCombatWeapon
{
DECLARE_CLASS( CWeaponGrapple, CBaseHL2MPCombatWeapon );
// DECLARE_CLASS( CWeaponGrapple, CBaseHLCombatWeapon );
public:
CWeaponGrapple( void );
virtual void	Precache( void );
virtual void	PrimaryAttack( void );
virtual void	SecondaryAttack( void );
virtual bool	Deploy( void );
bool	CanHolster( void );
virtual bool	Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
void	Drop( const Vector &vecVelocity );
virtual bool	Reload( void );
virtual void	ItemPostFrame( void );
virtual void	ItemBusyFrame( void );
void	NotifyHookDied( void );
bool	HasAnyAmmo( void );
CBaseEntity *GetHook( void ) { return m_hHook; }
bool ToggleHook( void );
void DrawBeam( const Vector &startPos, const Vector &endPos, float width );
void	DoImpactEffect( trace_t &tr, int nDamageType );
bool m_bHook;
DECLARE_NETWORKCLASS();
DECLARE_PREDICTABLE();
//DECLARE_CLIENTCLASS();
private:
void	FireHook( void );
#ifndef CLIENT_DLL
DECLARE_ACTTABLE();
#endif
private:
#ifndef CLIENT_DLL
CHandle<CBeam> pBeam;
CHandle<CSprite> m_pLightGlow;
#endif
bool	m_bInZoom;
bool m_bMustReload;
CBaseEntity *m_hHook;
CWeaponGrapple( const CWeaponGrapple & );
int m_nBulletType;
};
#endif // WEAPON_GRAPPLE_H

server/hl2/weapon_grapple.cpp

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements the grapple hook weapon.
//
// Primary attack: fires a beam that hooks on a surface.
// Secondary attack: switches between pull and rapple modes
//
//
//=============================================================================//
#include "cbase.h"
#include "npcevent.h"
#include "in_buttons.h"
#include "weapon_grapple.h"
#ifdef CLIENT_DLL
#include "c_hl2mp_player.h"
//#include "player.h"
#include "c_te_effect_dispatch.h"
//#include "te_effect_dispatch.h"
#else
#include "game.h"
#include "hl2mp_player.h"
#include "player.h"
#include "te_effect_dispatch.h"
#include "IEffects.h"
#include "Sprite.h"
#include "SpriteTrail.h"
#include "beam_shared.h"
#include "explode.h"
#include "ammodef.h"	/* This is needed for the tracing done later */
#include "gamestats.h" //
#include "soundent.h" //
#include "vphysics/constraints.h"
#include "physics_saverestore.h"
#endif
//#include "effect_dispatch_data.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define HOOK_MODEL	"models/props_junk/rock001a.mdl"
#define BOLT_AIR_VELOCITY	3500
#define BOLT_WATER_VELOCITY	1500
#ifndef CLIENT_DLL
LINK_ENTITY_TO_CLASS( grapple_hook, CGrappleHook );
BEGIN_DATADESC( CGrappleHook )
// Function Pointers
DEFINE_THINKFUNC( FlyThink ),
DEFINE_THINKFUNC( HookedThink ),
DEFINE_FUNCTION( HookTouch ),
DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ),
DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
DEFINE_FIELD( m_hBolt, FIELD_EHANDLE ),
DEFINE_FIELD( m_bPlayerWasStanding, FIELD_BOOLEAN ),
END_DATADESC()
CGrappleHook *CGrappleHook::HookCreate( const Vector &vecOrigin, const QAngle &angAngles, CBaseEntity *pentOwner )
{
// Create a new entity with CGrappleHook private data
CGrappleHook *pHook = (CGrappleHook *)CreateEntityByName( "grapple_hook" );
UTIL_SetOrigin( pHook, vecOrigin );
pHook->SetAbsAngles( angAngles );
pHook->Spawn();
CWeaponGrapple *pOwner = (CWeaponGrapple *)pentOwner;
pHook->m_hOwner = pOwner;
pHook->SetOwnerEntity( pOwner->GetOwner() );
pHook->m_hPlayer = (CBasePlayer *)pOwner->GetOwner();
return pHook;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CGrappleHook::~CGrappleHook( void )
{
if ( m_hBolt )
{
UTIL_Remove( m_hBolt );
m_hBolt = NULL;
}
// Revert Jay's gai flag
if ( m_hPlayer )
m_hPlayer->SetPhysicsFlag( PFLAG_VPHYSICS_MOTIONCONTROLLER, false );
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CGrappleHook::CreateVPhysics( void )
{
// Create the object in the physics system
VPhysicsInitNormal( SOLID_BBOX, FSOLID_NOT_STANDABLE, false );
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
unsigned int CGrappleHook::PhysicsSolidMaskForEntity() const
{
return ( BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_HITBOX ) & ~CONTENTS_GRATE;
}
//-----------------------------------------------------------------------------
// Purpose: Spawn
//-----------------------------------------------------------------------------
void CGrappleHook::Spawn( void )
{
Precache( );
SetModel( HOOK_MODEL );
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
UTIL_SetSize( this, -Vector(1,1,1), Vector(1,1,1) );
SetSolid( SOLID_BBOX );
SetGravity( 0.05f );
// The rock is invisible, the crossbow bolt is the visual representation
AddEffects( EF_NODRAW );
// Make sure we're updated if we're underwater
UpdateWaterState();
SetTouch( &CGrappleHook::HookTouch );
SetThink( &CGrappleHook::FlyThink );
SetNextThink( gpGlobals->curtime + 0.1f );
m_pSpring = NULL;
m_fSpringLength = 0.0f;
m_bPlayerWasStanding = false;
}
void CGrappleHook::Precache( void )
{
PrecacheModel( HOOK_MODEL );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pOther -
//-----------------------------------------------------------------------------
void CGrappleHook::HookTouch( CBaseEntity *pOther )
{
if ( !pOther->IsSolid() || pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) )
return;
if ( (pOther != m_hOwner) && (pOther->m_takedamage != DAMAGE_NO) )
{
m_hOwner->NotifyHookDied();
SetTouch( NULL );
SetThink( NULL );
UTIL_Remove( this );
}
else
{
trace_t	tr;
tr = BaseClass::GetTouchTrace();
// See if we struck the world
if ( pOther->GetMoveType() == MOVETYPE_NONE && !( tr.surface.flags & SURF_SKY ) )
{
EmitSound( "Weapon_AR2.Reload_Push" );
// if what we hit is static architecture, can stay around for a while.
Vector vecDir = GetAbsVelocity();
//FIXME: We actually want to stick (with hierarchy) to what we've hit
SetMoveType( MOVETYPE_NONE );
Vector vForward;
AngleVectors( GetAbsAngles(), &vForward );
VectorNormalize ( vForward );
CEffectData data;
data.m_vOrigin = tr.endpos;
data.m_vNormal = vForward;
data.m_nEntIndex = 0;
// DispatchEffect( "Impact", data );
// AddEffects( EF_NODRAW );
SetTouch( NULL );
VPhysicsDestroyObject();
VPhysicsInitNormal( SOLID_VPHYSICS, FSOLID_NOT_STANDABLE, false );
AddSolidFlags( FSOLID_NOT_SOLID );
// SetMoveType( MOVETYPE_NONE );
if ( !m_hPlayer )
{
Assert( 0 );
return;
}
// Set Jay's gai flag
m_hPlayer->SetPhysicsFlag( PFLAG_VPHYSICS_MOTIONCONTROLLER, true );
//IPhysicsObject *pPhysObject = m_hPlayer->VPhysicsGetObject();
IPhysicsObject *pRootPhysObject = VPhysicsGetObject();
Assert( pRootPhysObject );
Assert( pPhysObject );
pRootPhysObject->EnableMotion( false );
// Root has huge mass, tip has little
pRootPhysObject->SetMass( VPHYSICS_MAX_MASS );
// pPhysObject->SetMass( 100 );
// float damping = 3;
// pPhysObject->SetDamping( &damping, &damping );
Vector origin = m_hPlayer->GetAbsOrigin();
Vector rootOrigin = GetAbsOrigin();
m_fSpringLength = (origin - rootOrigin).Length();
m_bPlayerWasStanding = ( ( m_hPlayer->GetFlags() & FL_DUCKING ) == 0 );
SetThink( &CGrappleHook::HookedThink );
SetNextThink( gpGlobals->curtime + 0.1f );
}
else
{
// Put a mark unless we've hit the sky
/*if ( ( tr.surface.flags & SURF_SKY ) == false )
{
UTIL_ImpactTrace( &tr, DMG_BULLET );
}*/
SetTouch( NULL );
SetThink( NULL );
m_hOwner->NotifyHookDied();
UTIL_Remove( this );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGrappleHook::HookedThink( void )
{
//set next globalthink
SetNextThink( gpGlobals->curtime + 0.05f ); //0.1f
//All of this push the player far from the hook
Vector tempVec1 = m_hPlayer->GetAbsOrigin() - GetAbsOrigin();
VectorNormalize(tempVec1);
int temp_multiplier = -1;
m_hPlayer->SetGravity(0.0f);
m_hPlayer->SetGroundEntity(NULL);
if (m_hOwner->m_bHook){
//temp_multiplier = 1;
m_hPlayer->SetAbsVelocity(tempVec1*temp_multiplier*15); //50
}
else
{
m_hPlayer->SetAbsVelocity(tempVec1*temp_multiplier*300); //400
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGrappleHook::FlyThink( void )
{
QAngle angNewAngles;
VectorAngles( GetAbsVelocity(), angNewAngles );
SetAbsAngles( angNewAngles );
SetNextThink( gpGlobals->curtime + 0.1f );
}
#endif
IMPLEMENT_NETWORKCLASS_ALIASED( WeaponGrapple, DT_WeaponGrapple )
#ifdef CLIENT_DLL
void RecvProxy_HookDied( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
CWeaponGrapple *pGrapple = ((CWeaponGrapple*)pStruct);
RecvProxy_IntToEHandle( pData, pStruct, pOut );
CBaseEntity *pNewHook = pGrapple->GetHook();
if ( pNewHook == NULL )
{
if ( pGrapple->GetOwner() && pGrapple->GetOwner()->GetActiveWeapon() == pGrapple )
{
pGrapple->NotifyHookDied();
}
}
}
#endif
BEGIN_NETWORK_TABLE( CWeaponGrapple, DT_WeaponGrapple )
#ifdef CLIENT_DLL
RecvPropBool( RECVINFO( m_bInZoom ) ),
RecvPropBool( RECVINFO( m_bMustReload ) ),
RecvPropEHandle( RECVINFO( m_hHook ), RecvProxy_HookDied ),
RecvPropInt ( RECVINFO (m_nBulletType)),
#else
SendPropBool( SENDINFO( m_bInZoom ) ),
SendPropBool( SENDINFO( m_bMustReload ) ),
SendPropEHandle( SENDINFO( m_hHook ) ),
SendPropInt ( SENDINFO (m_nBulletType)),
#endif
END_NETWORK_TABLE()
#ifdef CLIENT_DLL
BEGIN_PREDICTION_DATA( CWeaponGrapple )
DEFINE_PRED_FIELD( m_bInZoom, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_bMustReload, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
END_PREDICTION_DATA()
#endif
LINK_ENTITY_TO_CLASS( weapon_grapple, CWeaponGrapple );
PRECACHE_WEAPON_REGISTER( weapon_grapple );
#ifndef CLIENT_DLL
acttable_t	CWeaponGrapple::m_acttable[] =
{
{ ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_PHYSGUN, false },
{ ACT_HL2MP_RUN, ACT_HL2MP_RUN_PHYSGUN, false },
{ ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_PHYSGUN, false },
{ ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_PHYSGUN, false },
{ ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_PHYSGUN, false },
{ ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_PHYSGUN, false },
{ ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_PHYSGUN, false },
// { ACT_IDLE, ACT_IDLE, false },
// { ACT_RUN, ACT_RUN, false },
// { ACT_WALK_CROUCH, ACT_WALK_CROUCH, false },
// { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD, false },
// { ACT_JUMP, ACT_JUMP, false },
};
IMPLEMENT_ACTTABLE(CWeaponGrapple);
#endif
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CWeaponGrapple::CWeaponGrapple( void )
{
m_bReloadsSingly = true;
m_bFiresUnderwater = true;
m_bInZoom = false;
m_bMustReload = false;
m_nBulletType = -1;
#ifndef CLIENT_DLL
m_pLightGlow= NULL;
pBeam = NULL;
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Precache
//-----------------------------------------------------------------------------
void CWeaponGrapple::Precache( void )
{
#ifndef CLIENT_DLL
UTIL_PrecacheOther( "grapple_hook" );
#endif
PrecacheModel( "sprites/physbeam.vmt" );
PrecacheModel( "sprites/physcannon_bluecore2b.vmt" );
BaseClass::Precache();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::PrimaryAttack( void )
{
// Can't have an active hook out
if ( m_hHook != NULL )
return;
#ifndef CLIENT_DLL
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( !pPlayer )
{
return;
}
//Disabled on MP it makes think the weapon that it needs to reload
/*if ( m_iClip1 <= 0 )
{
if ( !m_bFireOnEmpty )
{
Reload();
}
else
{
WeaponSound( EMPTY );
m_flNextPrimaryAttack = 0.15;
}
return;
}*/
//m_iPrimaryAttacks++;
gamestats->Event_WeaponFired( pPlayer, true, GetClassname() );
//Obligatory for MP so the sound can be played
CDisablePredictionFiltering disabler;
WeaponSound( SINGLE );
pPlayer->DoMuzzleFlash();
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
pPlayer->SetAnimation( PLAYER_ATTACK1 );
m_flNextPrimaryAttack = gpGlobals->curtime + 0.75;
m_flNextSecondaryAttack = gpGlobals->curtime + 0.75;
//Disabled so we can shoot all the time that we want
//m_iClip1--;
Vector vecSrc = pPlayer->Weapon_ShootPosition();
Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_SCALE_DEFAULT );
//We will not shoot bullets anymore
//pPlayer->FireBullets( 1, vecSrc, vecAiming, vec3_origin, MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 0 );
pPlayer->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 );
CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 600, 0.2, GetOwner() );
if ( !m_iClip1 && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
{
// HEV suit - indicate out of ammo condition
pPlayer->SetSuitUpdate( "!HEV_AMO0", FALSE, 0 );
}
trace_t tr;
Vector vecShootOrigin, vecShootDir, vecDir, vecEnd;
//Gets the direction where the player is aiming
AngleVectors (pPlayer->EyeAngles(), &vecDir);
//Gets the position of the player
vecShootOrigin = pPlayer->Weapon_ShootPosition();
//Gets the position where the hook will hit
vecEnd = vecShootOrigin + (vecDir * MAX_TRACE_LENGTH);
//Traces a line between the two vectors
UTIL_TraceLine( vecShootOrigin, vecEnd, MASK_SHOT, pPlayer, COLLISION_GROUP_NONE, &tr);
//Draws the beam
DrawBeam( vecShootOrigin, tr.endpos, 15.5 );
//Creates an energy impact effect if we don't hit the sky or other places
if ( (tr.surface.flags & SURF_SKY) == false )
{
/*CPVSFilter filter( tr.endpos );
te->GaussExplosion( filter, 0.0f, tr.endpos, tr.plane.normal, 0 );
m_nBulletType = GetAmmoDef()->Index("GaussEnergy");
UTIL_ImpactTrace( &tr, m_nBulletType );*/
//Makes a sprite at the end of the beam
m_pLightGlow = CSprite::SpriteCreate( "sprites/physcannon_bluecore2b.vmt", GetAbsOrigin(), TRUE);
//Sets FX render and color
m_pLightGlow->SetTransparency( 9, 255, 255, 255, 200, kRenderFxNoDissipation );
//Sets the position
m_pLightGlow->SetAbsOrigin(tr.endpos);
//Bright
m_pLightGlow->SetBrightness( 255 );
//Scale
m_pLightGlow->SetScale( 0.65 );
}
#endif
FireHook();
SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration( ACT_VM_PRIMARYATTACK ) );
}
//-----------------------------------------------------------------------------
// Purpose: Toggles the grapple hook modes
//-----------------------------------------------------------------------------
void CWeaponGrapple::SecondaryAttack( void )
{
ToggleHook();
WeaponSound(EMPTY);
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponGrapple::Reload( void )
{
if ( ( m_bMustReload ) && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) )
{
//Redraw the weapon
SendWeaponAnim( ACT_VM_IDLE ); //ACT_VM_RELOAD
//Update our times
m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
//Mark this as done
m_bMustReload = false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Toggles between pull and rappel mode
//-----------------------------------------------------------------------------
bool CWeaponGrapple::ToggleHook( void )
{
#ifndef CLIENT_DLL
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( m_bHook )
{
m_bHook = false;
ClientPrint(pPlayer,HUD_PRINTCENTER, "Pull mode");
return m_bHook;
}
else
{
m_bHook = true;
ClientPrint(pPlayer,HUD_PRINTCENTER, "Rappel mode");
return m_bHook;
}
#endif
return m_bHook;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::ItemBusyFrame( void )
{
// Allow zoom toggling even when we're reloading
//CheckZoomToggle();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::ItemPostFrame( void )
{
//Enforces being able to use PrimaryAttack and Secondary Attack
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
if ( ( pOwner->m_nButtons & IN_ATTACK ) )
{
if ( m_flNextPrimaryAttack < gpGlobals->curtime )
{
PrimaryAttack();
}
}
else if ( m_bMustReload ) //&& HasWeaponIdleTimeElapsed() )
{
Reload();
}
if ( ( pOwner->m_afButtonPressed & IN_ATTACK2 ) )
{
if ( m_flNextPrimaryAttack < gpGlobals->curtime )
{
SecondaryAttack();
}
}
else if ( m_bMustReload ) //&& HasWeaponIdleTimeElapsed() )
{
Reload();
}
//Allow a refire as fast as the player can click
if ( ( ( pOwner->m_nButtons & IN_ATTACK ) == false ) )
{
m_flNextPrimaryAttack = gpGlobals->curtime - 0.1f;
}
#ifndef CLIENT_DLL
if ( m_hHook )
{
if ( !(pOwner->m_nButtons & IN_ATTACK) && !(pOwner->m_nButtons & IN_ATTACK2))
{
m_hHook->SetTouch( NULL );
m_hHook->SetThink( NULL );
UTIL_Remove( m_hHook );
m_hHook = NULL;
NotifyHookDied();
m_bMustReload = true;
}
}
#endif
// BaseClass::ItemPostFrame();
}
//-----------------------------------------------------------------------------
// Purpose: Fires the hook
//-----------------------------------------------------------------------------
void CWeaponGrapple::FireHook( void )
{
if ( m_bMustReload )
return;
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
if ( pOwner == NULL )
return;
#ifndef CLIENT_DLL
Vector vecAiming = pOwner->GetAutoaimVector( 0 );
Vector vecSrc = pOwner->Weapon_ShootPosition();
QAngle angAiming;
VectorAngles( vecAiming, angAiming );
CGrappleHook *pHook = CGrappleHook::HookCreate( vecSrc, angAiming, this );
if ( pOwner->GetWaterLevel() == 3 )
{
pHook->SetAbsVelocity( vecAiming * BOLT_WATER_VELOCITY );
}
else
{
pHook->SetAbsVelocity( vecAiming * BOLT_AIR_VELOCITY );
}
m_hHook = pHook;
#endif
pOwner->ViewPunch( QAngle( -2, 0, 0 ) );
//WeaponSound( SINGLE );
//WeaponSound( SPECIAL2 );
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + 0.75;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponGrapple::Deploy( void )
{
if ( m_bMustReload )
{
return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_CROSSBOW_DRAW_UNLOADED, (char*)GetAnimPrefix() );
}
return BaseClass::Deploy();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pSwitchingTo -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponGrapple::Holster( CBaseCombatWeapon *pSwitchingTo )
{
#ifndef CLIENT_DLL
if ( m_hHook )
{
m_hHook->SetTouch( NULL );
m_hHook->SetThink( NULL );
UTIL_Remove( m_hHook );
m_hHook = NULL;
NotifyHookDied();
m_bMustReload = true;
}
#endif
return BaseClass::Holster( pSwitchingTo );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::Drop( const Vector &vecVelocity )
{
#ifndef CLIENT_DLL
if ( m_hHook )
{
m_hHook->SetTouch( NULL );
m_hHook->SetThink( NULL );
UTIL_Remove( m_hHook );
m_hHook = NULL;
NotifyHookDied();
m_bMustReload = true;
}
#endif
BaseClass::Drop( vecVelocity );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CWeaponGrapple::HasAnyAmmo( void )
{
if ( m_hHook != NULL )
return true;
return BaseClass::HasAnyAmmo();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CWeaponGrapple::CanHolster( void )
{
//Can't have an active hook out
if ( m_hHook != NULL )
return false;
return BaseClass::CanHolster();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponGrapple::NotifyHookDied( void )
{
m_hHook = NULL;
#ifndef CLIENT_DLL
if ( pBeam )
{
UTIL_Remove( pBeam ); //Kill beam
pBeam = NULL;
UTIL_Remove( m_pLightGlow ); //Kill sprite
m_pLightGlow = NULL;
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Draws a beam
// Input : &startPos - where the beam should begin
// &endPos - where the beam should end
// width - what the diameter of the beam should be (units?)
//-----------------------------------------------------------------------------
void CWeaponGrapple::DrawBeam( const Vector &startPos, const Vector &endPos, float width )
{
#ifndef CLIENT_DLL
//Tracer down the middle (NOT NEEDED, IT WILL FIRE A TRACER)
//UTIL_Tracer( startPos, endPos, 0, TRACER_DONT_USE_ATTACHMENT, 6500, false, "GaussTracer" );
trace_t tr;
//Draw the main beam shaft
pBeam = CBeam::BeamCreate( "sprites/physbeam.vmt", 15.5 );
// It starts at startPos
pBeam->SetStartPos( startPos );
// This sets up some things that the beam uses to figure out where
// it should start and end
pBeam->PointEntInit( endPos, this );
// This makes it so that the beam appears to come from the muzzle of the pistol
pBeam->SetEndAttachment( LookupAttachment("Muzzle") );
pBeam->SetWidth( width );
// pBeam->SetEndWidth( 0.05f );
// Higher brightness means less transparent
pBeam->SetBrightness( 255 );
//pBeam->SetColor( 255, 185+random->RandomInt( -16, 16 ), 40 );
pBeam->RelinkBeam();
//Sets scrollrate of the beam sprite
float scrollOffset = gpGlobals->curtime + 5.5;
pBeam->SetScrollRate(scrollOffset);
// The beam should only exist for a very short time
//pBeam->LiveForTime( 0.1f );
UpdateWaterState();
SetTouch( &CGrappleHook::HookTouch );
SetThink( &CGrappleHook::FlyThink );
SetNextThink( gpGlobals->curtime + 0.1f );
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &tr - used to figure out where to do the effect
// nDamageType - ???
//-----------------------------------------------------------------------------
void CWeaponGrapple::DoImpactEffect( trace_t &tr, int nDamageType )
{
#ifndef CLIENT_DLL
if ( (tr.surface.flags & SURF_SKY) == false )
{
CPVSFilter filter( tr.endpos );
te->GaussExplosion( filter, 0.0f, tr.endpos, tr.plane.normal, 0 );
m_nBulletType = GetAmmoDef()->Index("GaussEnergy");
UTIL_ImpactTrace( &tr, m_nBulletType );
}
#endif
}

server/hl2/weapon_grapple.h

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements the grapple hook weapon.
//
// Primary attack: fires a beam that hooks on a surface.
// Secondary attack: switches between pull and rapple modes
//
//
//=============================================================================//
#ifndef WEAPON_GRAPPLE_H
#define WEAPON_GRAPPLE_H
#ifdef _WIN32
#pragma once
#endif
#include "weapon_hl2mpbasehlmpcombatweapon.h"
//#include "basehlcombatweapon.h"
#include "sprite.h"
#include "props.h"
#ifndef CLIENT_DLL
#include "rope.h"
#include "props.h"
#endif
#include "rope_shared.h"
#include "te_effect_dispatch.h"
#include "beam_shared.h"
#ifndef CLIENT_DLL
class CWeaponGrapple;
//-----------------------------------------------------------------------------
// Grapple Hook
//-----------------------------------------------------------------------------
class CGrappleHook : public CBaseCombatCharacter
{
DECLARE_CLASS( CGrappleHook, CBaseCombatCharacter );
public:
CGrappleHook() { };
~CGrappleHook();
Class_T Classify( void ) { return CLASS_NONE; }
public:
void Spawn( void );
void Precache( void );
void FlyThink( void );
void HookedThink( void );
void HookTouch( CBaseEntity *pOther );
bool CreateVPhysics( void );
unsigned int PhysicsSolidMaskForEntity() const;
static CGrappleHook *HookCreate( const Vector &vecOrigin, const QAngle &angAngles, CBaseEntity *pentOwner = NULL );
protected:
DECLARE_DATADESC();
private:
CHandle<CWeaponGrapple> m_hOwner;
CHandle<CBasePlayer> m_hPlayer;
CHandle<CDynamicProp> m_hBolt;
IPhysicsSpring *m_pSpring;
float m_fSpringLength;
bool m_bPlayerWasStanding;
};
#endif
//-----------------------------------------------------------------------------
// CWeaponGrapple
//-----------------------------------------------------------------------------
#ifdef CLIENT_DLL
#define CWeaponGrapple C_WeaponGrapple
#endif
class CWeaponGrapple : public CBaseHL2MPCombatWeapon
//class CWeaponGrapple : public CBaseHLCombatWeapon
{
DECLARE_CLASS( CWeaponGrapple, CBaseHL2MPCombatWeapon );
// DECLARE_CLASS( CWeaponGrapple, CBaseHLCombatWeapon );
public:
CWeaponGrapple( void );
virtual void Precache( void );
virtual void PrimaryAttack( void );
virtual void SecondaryAttack( void );
virtual bool Deploy( void );
bool CanHolster( void );
virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
void Drop( const Vector &vecVelocity );
virtual bool Reload( void );
virtual void ItemPostFrame( void );
virtual void ItemBusyFrame( void );
void NotifyHookDied( void );
bool HasAnyAmmo( void );
CBaseEntity *GetHook( void ) { return m_hHook; }
bool ToggleHook( void );
void DrawBeam( const Vector &startPos, const Vector &endPos, float width );
void	DoImpactEffect( trace_t &tr, int nDamageType );
bool m_bHook;
DECLARE_NETWORKCLASS();
DECLARE_PREDICTABLE();
private:
void FireHook( void );
#ifndef CLIENT_DLL
DECLARE_ACTTABLE();
#endif
private:
#ifndef CLIENT_DLL
CHandle<CBeam> pBeam;
CHandle<CSprite> m_pLightGlow;
#endif
CNetworkVar( bool, m_bInZoom );
CNetworkVar( bool, m_bMustReload );
CNetworkHandle( CBaseEntity, m_hHook );
CWeaponGrapple( const CWeaponGrapple & );
CNetworkVar( int, m_nBulletType );
};
#endif // WEAPON_GRAPPLE_H

Weapon Script (weapon_grapple.txt)

// Grapple Hook
WeaponData
{
// Weapon data is loaded by both the Game and Client DLLs.
"printname" "GRAPPLE HOOK"
"viewmodel" "models/weapons/v_physcannon.mdl"
"playermodel" "models/weapons/w_Physics.mdl"
"anim_prefix" "gauss"
"bucket" "5"
"bucket_position" "2"
"bucket_360" "0"
"bucket_position_360" "0"
"clip_size" "1"
"clip2_size" "-1"
"primary_ammo" "None"
"secondary_ammo" "None"
"default_clip" "-1"
"default_clip2" "-1"
"autoswitchto" "0"
"autoswitchfrom" "0"
"weight" "0"
"rumble" "1"
"item_flags" "0"
// Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)
SoundData
{
"reload" "Weapon_Pistol.Reload"
"reload_npc" "Weapon_Pistol.NPC_Reload"
"empty" "Weapon_MegaPhysCannon.DryFire"
"single_shot" "Weapon_Mortar.Single"
"single_shot_npc" "Weapon_Mortar.Single"
"special1" "Weapon_PhysCannon.HoldSound"
"special2" "Weapon_Pistol.Special2"
"burst" "Weapon_Pistol.Burst"
}
// Weapon Sprite data is loaded by the Client DLL.
TextureData
{
"weapon"
{
"font" "WeaponIcons"
"character" "m"
}
"weapon_s"
{
"font" "WeaponIconsSelected"
"character" "m"
}
"crosshair"
{
"font" "Crosshairs"
"character" "Q"
}
"autoaim"
{
"file" "sprites/crosshairs"
"x" "48"
"y" "72"
"width" "24"
"height" "24"
}
}
}

grapplehook.fgd (entity definition on a fgd)

//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======
//
// Grapple Hook entity weapon, copy and paste into your mod fgd file.
//
//=============================================================================
@PointClass base(Weapon) studio("models/weapons/w_physics.mdl") = weapon_grapple: "Grapple Hook" []

GitHub link (In case you prefer to download the files, as well as the test map)

https://github.com/MaestroFenix/NewGrappleHook