Pt-br/Grapple Hook

Foi assim que fizemos o gancho encontrado no Mod Obsidian Conflict. O código foi escrito por Hyperjag3 e Skidz. Você pode encontrar uma versão melhorada no YouTube criada pelo usuário MaestroFenix ​​e pode pegar o código do GitHub para testá-lo em seu próprio mod.
Se alguém quiser desmontar isso e escrever alguma documentação sobre como funciona, seria bom. Eu simplesmente não tenho tempo para fazer isso, infelizmente.
A arma foi projetada para multijogador, mas deve funcionar bem em um mod para um jogador, uma vez que você troque as referências a hl2mp. Ele usa o modelo de besta.
NOTA: Se você incluir este código em seu mod de alguma forma, dê créditos à equipe do Obsidian Conflict no arquivo leia-me do seu mod! 
Este código foi carregado em 2009, e além de deixar a parte de fazer a versão cliente do código, contém alguns bugs como mover o jogador em todas as direções ou não puxar sempre. No entanto, uma nova versão foi feita:
Substituído o método spring para vetores. Dá maior controle ao jogador em vez de apenas voar em todas as direções.
Adicionado um modo de rapel, para que agora os jogadores possam descer/frear sua velocidade de queda com segurança.
O gancho agora pode disparar o "gancho" mais rápido (isso poderia ser feito apenas com um viewmodel que possui uma recarga rápida, mas foi corrigido no código).
Substituí o parafuso e a corda por uma viga, para deixá-la mais bonita.
O “gancho” agora pode ser visto tanto em primeira quanto em terceira pessoa. Antes, pela forma como foi feito, parecia bom em terceira pessoa, mas foi movido no viewmodel.
Usa a Arma Gravitacional como modelo.
Além de dar créditos à Obsidian Conflict Team, você também deve dar créditos ao Maestro Fénix, que é o criador desta versão melhorada.
Você pode ver um vídeo de demonstração aqui. clique em mim!.
== 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" []
=== Link do github (Encaso você queira baixar os arquivos, ou usar o mapa de teste)===
https://github.com/MaestroFenix/NewGrappleHook