Grapple Hook

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
Category:Free source code
Category:Weapons programming