Revision as of 14:44, 28 February 2017

This is how we made the grappling hook found in the Obsidian Conflict Mod. The code was written by Hyperjag3 and Skidz.

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

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

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

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

Replaced the spring method for vectors. It gives higher control to the player instead of just flying on all the directions.

Added a rappel mode, so now players can descend/brake their falling speed safely.

The grapple hook can fire now the "hook" faster (this could be done just with a viewmodel that has a fast reload, but it has been fixed at the code).

Replaced the bolt and rope for a beam, to make it more pretty.

The "hook" now can be seen right both in first and thirdperson. Before, due the way it was made, it looked fine at thirdperson, but it was moved at the viewmodel.

Uses the Gravity Gun as model.

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

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_DLLLINK_ENTITY_TO_CLASS(grapple_hook,CGrappleHook);BEGIN_DATADESC(CGrappleHook)// Function PointersDEFINE_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(constVector&vecOrigin,constQAngle&angAngles,CBaseEntity*pentOwner){// Create a new entity with CGrappleHook private dataCGrappleHook*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();returnpHook;}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------CGrappleHook::~CGrappleHook(void){if(m_hBolt){UTIL_Remove(m_hBolt);m_hBolt=NULL;}// Revert Jay's gai flagif(m_hPlayer)m_hPlayer->SetPhysicsFlag(PFLAG_VPHYSICS_MOTIONCONTROLLER,false);}//-----------------------------------------------------------------------------// Purpose:// Output : Returns true on success, false on failure.//-----------------------------------------------------------------------------boolCGrappleHook::CreateVPhysics(void){// Create the object in the physics systemVPhysicsInitNormal(SOLID_BBOX,FSOLID_NOT_STANDABLE,false);returntrue;}//-----------------------------------------------------------------------------//-----------------------------------------------------------------------------unsignedintCGrappleHook::PhysicsSolidMaskForEntity()const{return(BaseClass::PhysicsSolidMaskForEntity()|CONTENTS_HITBOX)&~CONTENTS_GRATE;}//-----------------------------------------------------------------------------// Purpose: Spawn//-----------------------------------------------------------------------------voidCGrappleHook::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 representationAddEffects(EF_NODRAW);// Make sure we're updated if we're underwaterUpdateWaterState();SetTouch(&CGrappleHook::HookTouch);SetThink(&CGrappleHook::FlyThink);SetNextThink(gpGlobals->curtime+0.1f);m_pSpring=NULL;m_fSpringLength=0.0f;m_bPlayerWasStanding=false;}voidCGrappleHook::Precache(void){PrecacheModel(HOOK_MODEL);}//-----------------------------------------------------------------------------// Purpose:// Input : *pOther -//-----------------------------------------------------------------------------voidCGrappleHook::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_ttr;tr=BaseClass::GetTouchTrace();// See if we struck the worldif(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.VectorvecDir=GetAbsVelocity();//FIXME: We actually want to stick (with hierarchy) to what we've hitSetMoveType(MOVETYPE_NONE);VectorvForward;AngleVectors(GetAbsAngles(),&vForward);VectorNormalize(vForward);CEffectDatadata;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 flagm_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 littlepRootPhysObject->SetMass(VPHYSICS_MAX_MASS);// pPhysObject->SetMass( 100 );// float damping = 3;// pPhysObject->SetDamping( &damping, &damping );Vectororigin=m_hPlayer->GetAbsOrigin();VectorrootOrigin=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 skyif((tr.surface.flags&SURF_SKY)==false){UTIL_ImpactTrace(&tr,DMG_BULLET);}SetTouch(NULL);SetThink(NULL);m_hOwner->NotifyHookDied();UTIL_Remove(this);}}}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------voidCGrappleHook::HookedThink(void){//set next globalthinkSetNextThink(gpGlobals->curtime+0.05f);//0.1f//All of this push the player far from the hookVectortempVec1=m_hPlayer->GetAbsOrigin()-GetAbsOrigin();VectorNormalize(tempVec1);inttemp_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://-----------------------------------------------------------------------------voidCGrappleHook::FlyThink(void){QAngleangNewAngles;VectorAngles(GetAbsVelocity(),angNewAngles);SetAbsAngles(angNewAngles);SetNextThink(gpGlobals->curtime+0.1f);}#endifIMPLEMENT_NETWORKCLASS_ALIASED(WeaponGrapple,DT_WeaponGrapple)#ifdef CLIENT_DLLvoidRecvProxy_HookDied(constCRecvProxyData*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();}}}#endifBEGIN_NETWORK_TABLE(CWeaponGrapple,DT_WeaponGrapple)#ifdef CLIENT_DLLRecvPropBool(RECVINFO(m_bInZoom)),RecvPropBool(RECVINFO(m_bMustReload)),RecvPropEHandle(RECVINFO(m_hHook),RecvProxy_HookDied),RecvPropInt(RECVINFO(m_nBulletType)),#elseSendPropBool(SENDINFO(m_bInZoom)),SendPropBool(SENDINFO(m_bMustReload)),SendPropEHandle(SENDINFO(m_hHook)),SendPropInt(SENDINFO(m_nBulletType)),#endifEND_NETWORK_TABLE()#ifdef CLIENT_DLLBEGIN_PREDICTION_DATA(CWeaponGrapple)DEFINE_PRED_FIELD(m_bInZoom,FIELD_BOOLEAN,FTYPEDESC_INSENDTABLE),DEFINE_PRED_FIELD(m_bMustReload,FIELD_BOOLEAN,FTYPEDESC_INSENDTABLE),END_PREDICTION_DATA()#endifLINK_ENTITY_TO_CLASS(weapon_grapple,CWeaponGrapple);PRECACHE_WEAPON_REGISTER(weapon_grapple);#ifndef CLIENT_DLLacttable_tCWeaponGrapple::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_DLLm_pLightGlow=NULL;pBeam=NULL;#endif}//-----------------------------------------------------------------------------// Purpose: Precache//-----------------------------------------------------------------------------voidCWeaponGrapple::Precache(void){#ifndef CLIENT_DLLUTIL_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://-----------------------------------------------------------------------------voidCWeaponGrapple::PrimaryAttack(void){// Can't have an active hook outif(m_hHook!=NULL)return;#ifndef CLIENT_DLLCBasePlayer*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--;VectorvecSrc=pPlayer->Weapon_ShootPosition();VectorvecAiming=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 conditionpPlayer->SetSuitUpdate("!HEV_AMO0",FALSE,0);}trace_ttr;VectorvecShootOrigin,vecShootDir,vecDir,vecEnd;//Gets the direction where the player is aimingAngleVectors(pPlayer->EyeAngles(),&vecDir);//Gets the position of the playervecShootOrigin=pPlayer->Weapon_ShootPosition();//Gets the position where the hook will hitvecEnd=vecShootOrigin+(vecDir*MAX_TRACE_LENGTH);//Traces a line between the two vectorsUTIL_TraceLine(vecShootOrigin,vecEnd,MASK_SHOT,pPlayer,COLLISION_GROUP_NONE,&tr);//Draws the beamDrawBeam(vecShootOrigin,tr.endpos,15.5);//Creates an energy impact effect if we don't hit the sky or other placesif((tr.surface.flags&SURF_SKY)==false){CPVSFilterfilter(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 beamm_pLightGlow=CSprite::SpriteCreate("sprites/physcannon_bluecore2b.vmt",GetAbsOrigin(),TRUE);//Sets FX render and colorm_pLightGlow->SetTransparency(9,255,255,255,200,kRenderFxNoDissipation);//Sets the positionm_pLightGlow->SetAbsOrigin(tr.endpos);//Brightm_pLightGlow->SetBrightness(255);//Scalem_pLightGlow->SetScale(0.65);}#endifFireHook();SetWeaponIdleTime(gpGlobals->curtime+SequenceDuration(ACT_VM_PRIMARYATTACK));}//-----------------------------------------------------------------------------// Purpose: Toggles the grapple hook modes//-----------------------------------------------------------------------------voidCWeaponGrapple::SecondaryAttack(void){ToggleHook();WeaponSound(EMPTY);}//-----------------------------------------------------------------------------// Purpose:// Output : Returns true on success, false on failure.//-----------------------------------------------------------------------------boolCWeaponGrapple::Reload(void){if((m_bMustReload)&&(m_flNextPrimaryAttack<=gpGlobals->curtime)){//Redraw the weaponSendWeaponAnim(ACT_VM_IDLE);//ACT_VM_RELOAD//Update our timesm_flNextPrimaryAttack=gpGlobals->curtime+SequenceDuration();//Mark this as donem_bMustReload=false;}returntrue;}//-----------------------------------------------------------------------------// Purpose: Toggles between pull and rappel mode//-----------------------------------------------------------------------------boolCWeaponGrapple::ToggleHook(void){#ifndef CLIENT_DLLCBasePlayer*pPlayer=ToBasePlayer(GetOwner());if(m_bHook){m_bHook=false;ClientPrint(pPlayer,HUD_PRINTCENTER,"Pull mode");returnm_bHook;}else{m_bHook=true;ClientPrint(pPlayer,HUD_PRINTCENTER,"Rappel mode");returnm_bHook;}#endifreturnm_bHook;}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------voidCWeaponGrapple::ItemBusyFrame(void){// Allow zoom toggling even when we're reloading//CheckZoomToggle();}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------voidCWeaponGrapple::ItemPostFrame(void){//Enforces being able to use PrimaryAttack and Secondary AttackCBasePlayer*pOwner=ToBasePlayer(GetOwner());if((pOwner->m_nButtons&IN_ATTACK)){if(m_flNextPrimaryAttack<gpGlobals->curtime){PrimaryAttack();}}elseif(m_bMustReload)//&& HasWeaponIdleTimeElapsed() ){Reload();}if((pOwner->m_afButtonPressed&IN_ATTACK2)){if(m_flNextPrimaryAttack<gpGlobals->curtime){SecondaryAttack();}}elseif(m_bMustReload)//&& HasWeaponIdleTimeElapsed() ){Reload();}//Allow a refire as fast as the player can clickif(((pOwner->m_nButtons&IN_ATTACK)==false)){m_flNextPrimaryAttack=gpGlobals->curtime-0.1f;}#ifndef CLIENT_DLLif(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//-----------------------------------------------------------------------------voidCWeaponGrapple::FireHook(void){if(m_bMustReload)return;CBasePlayer*pOwner=ToBasePlayer(GetOwner());if(pOwner==NULL)return;#ifndef CLIENT_DLLVectorvecAiming=pOwner->GetAutoaimVector(0);VectorvecSrc=pOwner->Weapon_ShootPosition();QAngleangAiming;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;#endifpOwner->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.//-----------------------------------------------------------------------------boolCWeaponGrapple::Deploy(void){if(m_bMustReload){returnDefaultDeploy((char*)GetViewModel(),(char*)GetWorldModel(),ACT_CROSSBOW_DRAW_UNLOADED,(char*)GetAnimPrefix());}returnBaseClass::Deploy();}//-----------------------------------------------------------------------------// Purpose:// Input : *pSwitchingTo -// Output : Returns true on success, false on failure.//-----------------------------------------------------------------------------boolCWeaponGrapple::Holster(CBaseCombatWeapon*pSwitchingTo){#ifndef CLIENT_DLLif(m_hHook){m_hHook->SetTouch(NULL);m_hHook->SetThink(NULL);UTIL_Remove(m_hHook);m_hHook=NULL;NotifyHookDied();m_bMustReload=true;}#endifreturnBaseClass::Holster(pSwitchingTo);}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------voidCWeaponGrapple::Drop(constVector&vecVelocity){#ifndef CLIENT_DLLif(m_hHook){m_hHook->SetTouch(NULL);m_hHook->SetThink(NULL);UTIL_Remove(m_hHook);m_hHook=NULL;NotifyHookDied();m_bMustReload=true;}#endifBaseClass::Drop(vecVelocity);}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------boolCWeaponGrapple::HasAnyAmmo(void){if(m_hHook!=NULL)returntrue;returnBaseClass::HasAnyAmmo();}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------boolCWeaponGrapple::CanHolster(void){//Can't have an active hook outif(m_hHook!=NULL)returnfalse;returnBaseClass::CanHolster();}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------voidCWeaponGrapple::NotifyHookDied(void){m_hHook=NULL;#ifndef CLIENT_DLLif(pBeam){UTIL_Remove(pBeam);//Kill beampBeam=NULL;UTIL_Remove(m_pLightGlow);//Kill spritem_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?)//-----------------------------------------------------------------------------voidCWeaponGrapple::DrawBeam(constVector&startPos,constVector&endPos,floatwidth){#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_ttr;//Draw the main beam shaftpBeam=CBeam::BeamCreate("sprites/physbeam.vmt",15.5);// It starts at startPospBeam->SetStartPos(startPos);// This sets up some things that the beam uses to figure out where// it should start and endpBeam->PointEntInit(endPos,this);// This makes it so that the beam appears to come from the muzzle of the pistolpBeam->SetEndAttachment(LookupAttachment("Muzzle"));pBeam->SetWidth(width);// pBeam->SetEndWidth( 0.05f );// Higher brightness means less transparentpBeam->SetBrightness(255);//pBeam->SetColor( 255, 185+random->RandomInt( -16, 16 ), 40 );pBeam->RelinkBeam();//Sets scrollrate of the beam spritefloatscrollOffset=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 - ???//-----------------------------------------------------------------------------voidCWeaponGrapple::DoImpactEffect(trace_t&tr,intnDamageType){#ifndef CLIENT_DLLif((tr.surface.flags&SURF_SKY)==false){CPVSFilterfilter(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.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_DLLLINK_ENTITY_TO_CLASS(grapple_hook,CGrappleHook);BEGIN_DATADESC(CGrappleHook)// Function PointersDEFINE_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(constVector&vecOrigin,constQAngle&angAngles,CBaseEntity*pentOwner){// Create a new entity with CGrappleHook private dataCGrappleHook*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();returnpHook;}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------CGrappleHook::~CGrappleHook(void){if(m_hBolt){UTIL_Remove(m_hBolt);m_hBolt=NULL;}// Revert Jay's gai flagif(m_hPlayer)m_hPlayer->SetPhysicsFlag(PFLAG_VPHYSICS_MOTIONCONTROLLER,false);}//-----------------------------------------------------------------------------// Purpose:// Output : Returns true on success, false on failure.//-----------------------------------------------------------------------------boolCGrappleHook::CreateVPhysics(void){// Create the object in the physics systemVPhysicsInitNormal(SOLID_BBOX,FSOLID_NOT_STANDABLE,false);returntrue;}//-----------------------------------------------------------------------------//-----------------------------------------------------------------------------unsignedintCGrappleHook::PhysicsSolidMaskForEntity()const{return(BaseClass::PhysicsSolidMaskForEntity()|CONTENTS_HITBOX)&~CONTENTS_GRATE;}//-----------------------------------------------------------------------------// Purpose: Spawn//-----------------------------------------------------------------------------voidCGrappleHook::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 representationAddEffects(EF_NODRAW);// Make sure we're updated if we're underwaterUpdateWaterState();SetTouch(&CGrappleHook::HookTouch);SetThink(&CGrappleHook::FlyThink);SetNextThink(gpGlobals->curtime+0.1f);m_pSpring=NULL;m_fSpringLength=0.0f;m_bPlayerWasStanding=false;}voidCGrappleHook::Precache(void){PrecacheModel(HOOK_MODEL);}//-----------------------------------------------------------------------------// Purpose:// Input : *pOther -//-----------------------------------------------------------------------------voidCGrappleHook::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_ttr;tr=BaseClass::GetTouchTrace();// See if we struck the worldif(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.VectorvecDir=GetAbsVelocity();//FIXME: We actually want to stick (with hierarchy) to what we've hitSetMoveType(MOVETYPE_NONE);VectorvForward;AngleVectors(GetAbsAngles(),&vForward);VectorNormalize(vForward);CEffectDatadata;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 flagm_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 littlepRootPhysObject->SetMass(VPHYSICS_MAX_MASS);// pPhysObject->SetMass( 100 );// float damping = 3;// pPhysObject->SetDamping( &damping, &damping );Vectororigin=m_hPlayer->GetAbsOrigin();VectorrootOrigin=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 skyif((tr.surface.flags&SURF_SKY)==false){UTIL_ImpactTrace(&tr,DMG_BULLET);}SetTouch(NULL);SetThink(NULL);m_hOwner->NotifyHookDied();UTIL_Remove(this);}}}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------voidCGrappleHook::HookedThink(void){//set next globalthinkSetNextThink(gpGlobals->curtime+0.05f);//0.1f//All of this push the player far from the hookVectortempVec1=m_hPlayer->GetAbsOrigin()-GetAbsOrigin();VectorNormalize(tempVec1);inttemp_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://-----------------------------------------------------------------------------voidCGrappleHook::FlyThink(void){QAngleangNewAngles;VectorAngles(GetAbsVelocity(),angNewAngles);SetAbsAngles(angNewAngles);SetNextThink(gpGlobals->curtime+0.1f);}#endifIMPLEMENT_NETWORKCLASS_ALIASED(WeaponGrapple,DT_WeaponGrapple)#ifdef CLIENT_DLLvoidRecvProxy_HookDied(constCRecvProxyData*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();}}}#endifBEGIN_NETWORK_TABLE(CWeaponGrapple,DT_WeaponGrapple)#ifdef CLIENT_DLLRecvPropBool(RECVINFO(m_bInZoom)),RecvPropBool(RECVINFO(m_bMustReload)),RecvPropEHandle(RECVINFO(m_hHook),RecvProxy_HookDied),RecvPropInt(RECVINFO(m_nBulletType)),#elseSendPropBool(SENDINFO(m_bInZoom)),SendPropBool(SENDINFO(m_bMustReload)),SendPropEHandle(SENDINFO(m_hHook)),SendPropInt(SENDINFO(m_nBulletType)),#endifEND_NETWORK_TABLE()#ifdef CLIENT_DLLBEGIN_PREDICTION_DATA(CWeaponGrapple)DEFINE_PRED_FIELD(m_bInZoom,FIELD_BOOLEAN,FTYPEDESC_INSENDTABLE),DEFINE_PRED_FIELD(m_bMustReload,FIELD_BOOLEAN,FTYPEDESC_INSENDTABLE),END_PREDICTION_DATA()#endifLINK_ENTITY_TO_CLASS(weapon_grapple,CWeaponGrapple);PRECACHE_WEAPON_REGISTER(weapon_grapple);#ifndef CLIENT_DLLacttable_tCWeaponGrapple::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_DLLm_pLightGlow=NULL;pBeam=NULL;#endif}//-----------------------------------------------------------------------------// Purpose: Precache//-----------------------------------------------------------------------------voidCWeaponGrapple::Precache(void){#ifndef CLIENT_DLLUTIL_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://-----------------------------------------------------------------------------voidCWeaponGrapple::PrimaryAttack(void){// Can't have an active hook outif(m_hHook!=NULL)return;#ifndef CLIENT_DLLCBasePlayer*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--;VectorvecSrc=pPlayer->Weapon_ShootPosition();VectorvecAiming=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 conditionpPlayer->SetSuitUpdate("!HEV_AMO0",FALSE,0);}trace_ttr;VectorvecShootOrigin,vecShootDir,vecDir,vecEnd;//Gets the direction where the player is aimingAngleVectors(pPlayer->EyeAngles(),&vecDir);//Gets the position of the playervecShootOrigin=pPlayer->Weapon_ShootPosition();//Gets the position where the hook will hitvecEnd=vecShootOrigin+(vecDir*MAX_TRACE_LENGTH);//Traces a line between the two vectorsUTIL_TraceLine(vecShootOrigin,vecEnd,MASK_SHOT,pPlayer,COLLISION_GROUP_NONE,&tr);//Draws the beamDrawBeam(vecShootOrigin,tr.endpos,15.5);//Creates an energy impact effect if we don't hit the sky or other placesif((tr.surface.flags&SURF_SKY)==false){CPVSFilterfilter(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 beamm_pLightGlow=CSprite::SpriteCreate("sprites/physcannon_bluecore2b.vmt",GetAbsOrigin(),TRUE);//Sets FX render and colorm_pLightGlow->SetTransparency(9,255,255,255,200,kRenderFxNoDissipation);//Sets the positionm_pLightGlow->SetAbsOrigin(tr.endpos);//Brightm_pLightGlow->SetBrightness(255);//Scalem_pLightGlow->SetScale(0.65);}#endifFireHook();SetWeaponIdleTime(gpGlobals->curtime+SequenceDuration(ACT_VM_PRIMARYATTACK));}//-----------------------------------------------------------------------------// Purpose: Toggles the grapple hook modes//-----------------------------------------------------------------------------voidCWeaponGrapple::SecondaryAttack(void){ToggleHook();WeaponSound(EMPTY);}//-----------------------------------------------------------------------------// Purpose:// Output : Returns true on success, false on failure.//-----------------------------------------------------------------------------boolCWeaponGrapple::Reload(void){if((m_bMustReload)&&(m_flNextPrimaryAttack<=gpGlobals->curtime)){//Redraw the weaponSendWeaponAnim(ACT_VM_IDLE);//ACT_VM_RELOAD//Update our timesm_flNextPrimaryAttack=gpGlobals->curtime+SequenceDuration();//Mark this as donem_bMustReload=false;}returntrue;}//-----------------------------------------------------------------------------// Purpose: Toggles between pull and rappel mode//-----------------------------------------------------------------------------boolCWeaponGrapple::ToggleHook(void){#ifndef CLIENT_DLLCBasePlayer*pPlayer=ToBasePlayer(GetOwner());if(m_bHook){m_bHook=false;ClientPrint(pPlayer,HUD_PRINTCENTER,"Pull mode");returnm_bHook;}else{m_bHook=true;ClientPrint(pPlayer,HUD_PRINTCENTER,"Rappel mode");returnm_bHook;}#endifreturnm_bHook;}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------voidCWeaponGrapple::ItemBusyFrame(void){// Allow zoom toggling even when we're reloading//CheckZoomToggle();}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------voidCWeaponGrapple::ItemPostFrame(void){//Enforces being able to use PrimaryAttack and Secondary AttackCBasePlayer*pOwner=ToBasePlayer(GetOwner());if((pOwner->m_nButtons&IN_ATTACK)){if(m_flNextPrimaryAttack<gpGlobals->curtime){PrimaryAttack();}}elseif(m_bMustReload)//&& HasWeaponIdleTimeElapsed() ){Reload();}if((pOwner->m_afButtonPressed&IN_ATTACK2)){if(m_flNextPrimaryAttack<gpGlobals->curtime){SecondaryAttack();}}elseif(m_bMustReload)//&& HasWeaponIdleTimeElapsed() ){Reload();}//Allow a refire as fast as the player can clickif(((pOwner->m_nButtons&IN_ATTACK)==false)){m_flNextPrimaryAttack=gpGlobals->curtime-0.1f;}#ifndef CLIENT_DLLif(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//-----------------------------------------------------------------------------voidCWeaponGrapple::FireHook(void){if(m_bMustReload)return;CBasePlayer*pOwner=ToBasePlayer(GetOwner());if(pOwner==NULL)return;#ifndef CLIENT_DLLVectorvecAiming=pOwner->GetAutoaimVector(0);VectorvecSrc=pOwner->Weapon_ShootPosition();QAngleangAiming;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;#endifpOwner->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.//-----------------------------------------------------------------------------boolCWeaponGrapple::Deploy(void){if(m_bMustReload){returnDefaultDeploy((char*)GetViewModel(),(char*)GetWorldModel(),ACT_CROSSBOW_DRAW_UNLOADED,(char*)GetAnimPrefix());}returnBaseClass::Deploy();}//-----------------------------------------------------------------------------// Purpose:// Input : *pSwitchingTo -// Output : Returns true on success, false on failure.//-----------------------------------------------------------------------------boolCWeaponGrapple::Holster(CBaseCombatWeapon*pSwitchingTo){#ifndef CLIENT_DLLif(m_hHook){m_hHook->SetTouch(NULL);m_hHook->SetThink(NULL);UTIL_Remove(m_hHook);m_hHook=NULL;NotifyHookDied();m_bMustReload=true;}#endifreturnBaseClass::Holster(pSwitchingTo);}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------voidCWeaponGrapple::Drop(constVector&vecVelocity){#ifndef CLIENT_DLLif(m_hHook){m_hHook->SetTouch(NULL);m_hHook->SetThink(NULL);UTIL_Remove(m_hHook);m_hHook=NULL;NotifyHookDied();m_bMustReload=true;}#endifBaseClass::Drop(vecVelocity);}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------boolCWeaponGrapple::HasAnyAmmo(void){if(m_hHook!=NULL)returntrue;returnBaseClass::HasAnyAmmo();}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------boolCWeaponGrapple::CanHolster(void){//Can't have an active hook outif(m_hHook!=NULL)returnfalse;returnBaseClass::CanHolster();}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------voidCWeaponGrapple::NotifyHookDied(void){m_hHook=NULL;#ifndef CLIENT_DLLif(pBeam){UTIL_Remove(pBeam);//Kill beampBeam=NULL;UTIL_Remove(m_pLightGlow);//Kill spritem_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?)//-----------------------------------------------------------------------------voidCWeaponGrapple::DrawBeam(constVector&startPos,constVector&endPos,floatwidth){#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_ttr;//Draw the main beam shaftpBeam=CBeam::BeamCreate("sprites/physbeam.vmt",15.5);// It starts at startPospBeam->SetStartPos(startPos);// This sets up some things that the beam uses to figure out where// it should start and endpBeam->PointEntInit(endPos,this);// This makes it so that the beam appears to come from the muzzle of the pistolpBeam->SetEndAttachment(LookupAttachment("Muzzle"));pBeam->SetWidth(width);// pBeam->SetEndWidth( 0.05f );// Higher brightness means less transparentpBeam->SetBrightness(255);//pBeam->SetColor( 255, 185+random->RandomInt( -16, 16 ), 40 );pBeam->RelinkBeam();//Sets scrollrate of the beam spritefloatscrollOffset=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 - ???//-----------------------------------------------------------------------------voidCWeaponGrapple::DoImpactEffect(trace_t&tr,intnDamageType){#ifndef CLIENT_DLLif((tr.surface.flags&SURF_SKY)==false){CPVSFilterfilter(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.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_DLLLINK_ENTITY_TO_CLASS(grapple_hook,CGrappleHook);BEGIN_DATADESC(CGrappleHook)// Function PointersDEFINE_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(constVector&vecOrigin,constQAngle&angAngles,CBaseEntity*pentOwner){// Create a new entity with CGrappleHook private dataCGrappleHook*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();returnpHook;}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------CGrappleHook::~CGrappleHook(void){if(m_hBolt){UTIL_Remove(m_hBolt);m_hBolt=NULL;}// Revert Jay's gai flagif(m_hPlayer)m_hPlayer->SetPhysicsFlag(PFLAG_VPHYSICS_MOTIONCONTROLLER,false);}//-----------------------------------------------------------------------------// Purpose:// Output : Returns true on success, false on failure.//-----------------------------------------------------------------------------boolCGrappleHook::CreateVPhysics(void){// Create the object in the physics systemVPhysicsInitNormal(SOLID_BBOX,FSOLID_NOT_STANDABLE,false);returntrue;}//-----------------------------------------------------------------------------//-----------------------------------------------------------------------------unsignedintCGrappleHook::PhysicsSolidMaskForEntity()const{return(BaseClass::PhysicsSolidMaskForEntity()|CONTENTS_HITBOX)&~CONTENTS_GRATE;}//-----------------------------------------------------------------------------// Purpose: Spawn//-----------------------------------------------------------------------------voidCGrappleHook::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 representationAddEffects(EF_NODRAW);// Make sure we're updated if we're underwaterUpdateWaterState();SetTouch(&CGrappleHook::HookTouch);SetThink(&CGrappleHook::FlyThink);SetNextThink(gpGlobals->curtime+0.1f);m_pSpring=NULL;m_fSpringLength=0.0f;m_bPlayerWasStanding=false;}voidCGrappleHook::Precache(void){PrecacheModel(HOOK_MODEL);}//-----------------------------------------------------------------------------// Purpose:// Input : *pOther -//-----------------------------------------------------------------------------voidCGrappleHook::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_ttr;tr=BaseClass::GetTouchTrace();// See if we struck the worldif(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.VectorvecDir=GetAbsVelocity();//FIXME: We actually want to stick (with hierarchy) to what we've hitSetMoveType(MOVETYPE_NONE);VectorvForward;AngleVectors(GetAbsAngles(),&vForward);VectorNormalize(vForward);CEffectDatadata;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 flagm_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 littlepRootPhysObject->SetMass(VPHYSICS_MAX_MASS);// pPhysObject->SetMass( 100 );// float damping = 3;// pPhysObject->SetDamping( &damping, &damping );Vectororigin=m_hPlayer->GetAbsOrigin();VectorrootOrigin=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://-----------------------------------------------------------------------------voidCGrappleHook::HookedThink(void){//set next globalthinkSetNextThink(gpGlobals->curtime+0.05f);//0.1f//All of this push the player far from the hookVectortempVec1=m_hPlayer->GetAbsOrigin()-GetAbsOrigin();VectorNormalize(tempVec1);inttemp_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://-----------------------------------------------------------------------------voidCGrappleHook::FlyThink(void){QAngleangNewAngles;VectorAngles(GetAbsVelocity(),angNewAngles);SetAbsAngles(angNewAngles);SetNextThink(gpGlobals->curtime+0.1f);}#endifIMPLEMENT_NETWORKCLASS_ALIASED(WeaponGrapple,DT_WeaponGrapple)#ifdef CLIENT_DLLvoidRecvProxy_HookDied(constCRecvProxyData*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();}}}#endifBEGIN_NETWORK_TABLE(CWeaponGrapple,DT_WeaponGrapple)#ifdef CLIENT_DLLRecvPropBool(RECVINFO(m_bInZoom)),RecvPropBool(RECVINFO(m_bMustReload)),RecvPropEHandle(RECVINFO(m_hHook),RecvProxy_HookDied),RecvPropInt(RECVINFO(m_nBulletType)),#elseSendPropBool(SENDINFO(m_bInZoom)),SendPropBool(SENDINFO(m_bMustReload)),SendPropEHandle(SENDINFO(m_hHook)),SendPropInt(SENDINFO(m_nBulletType)),#endifEND_NETWORK_TABLE()#ifdef CLIENT_DLLBEGIN_PREDICTION_DATA(CWeaponGrapple)DEFINE_PRED_FIELD(m_bInZoom,FIELD_BOOLEAN,FTYPEDESC_INSENDTABLE),DEFINE_PRED_FIELD(m_bMustReload,FIELD_BOOLEAN,FTYPEDESC_INSENDTABLE),END_PREDICTION_DATA()#endifLINK_ENTITY_TO_CLASS(weapon_grapple,CWeaponGrapple);PRECACHE_WEAPON_REGISTER(weapon_grapple);#ifndef CLIENT_DLLacttable_tCWeaponGrapple::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_DLLm_pLightGlow=NULL;pBeam=NULL;#endif}//-----------------------------------------------------------------------------// Purpose: Precache//-----------------------------------------------------------------------------voidCWeaponGrapple::Precache(void){#ifndef CLIENT_DLLUTIL_PrecacheOther("grapple_hook");#endifPrecacheModel("sprites/physbeam.vmt");PrecacheModel("sprites/physcannon_bluecore2b.vmt");BaseClass::Precache();}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------voidCWeaponGrapple::PrimaryAttack(void){// Can't have an active hook outif(m_hHook!=NULL)return;#ifndef CLIENT_DLLCBasePlayer*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 playedCDisablePredictionFilteringdisabler;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--;VectorvecSrc=pPlayer->Weapon_ShootPosition();VectorvecAiming=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 conditionpPlayer->SetSuitUpdate("!HEV_AMO0",FALSE,0);}trace_ttr;VectorvecShootOrigin,vecShootDir,vecDir,vecEnd;//Gets the direction where the player is aimingAngleVectors(pPlayer->EyeAngles(),&vecDir);//Gets the position of the playervecShootOrigin=pPlayer->Weapon_ShootPosition();//Gets the position where the hook will hitvecEnd=vecShootOrigin+(vecDir*MAX_TRACE_LENGTH);//Traces a line between the two vectorsUTIL_TraceLine(vecShootOrigin,vecEnd,MASK_SHOT,pPlayer,COLLISION_GROUP_NONE,&tr);//Draws the beamDrawBeam(vecShootOrigin,tr.endpos,15.5);//Creates an energy impact effect if we don't hit the sky or other placesif((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 beamm_pLightGlow=CSprite::SpriteCreate("sprites/physcannon_bluecore2b.vmt",GetAbsOrigin(),TRUE);//Sets FX render and colorm_pLightGlow->SetTransparency(9,255,255,255,200,kRenderFxNoDissipation);//Sets the positionm_pLightGlow->SetAbsOrigin(tr.endpos);//Brightm_pLightGlow->SetBrightness(255);//Scalem_pLightGlow->SetScale(0.65);}#endifFireHook();SetWeaponIdleTime(gpGlobals->curtime+SequenceDuration(ACT_VM_PRIMARYATTACK));}//-----------------------------------------------------------------------------// Purpose: Toggles the grapple hook modes//-----------------------------------------------------------------------------voidCWeaponGrapple::SecondaryAttack(void){ToggleHook();WeaponSound(EMPTY);}//-----------------------------------------------------------------------------// Purpose:// Output : Returns true on success, false on failure.//-----------------------------------------------------------------------------boolCWeaponGrapple::Reload(void){if((m_bMustReload)&&(m_flNextPrimaryAttack<=gpGlobals->curtime)){//Redraw the weaponSendWeaponAnim(ACT_VM_IDLE);//ACT_VM_RELOAD//Update our timesm_flNextPrimaryAttack=gpGlobals->curtime+SequenceDuration();//Mark this as donem_bMustReload=false;}returntrue;}//-----------------------------------------------------------------------------// Purpose: Toggles between pull and rappel mode//-----------------------------------------------------------------------------boolCWeaponGrapple::ToggleHook(void){#ifndef CLIENT_DLLCBasePlayer*pPlayer=ToBasePlayer(GetOwner());if(m_bHook){m_bHook=false;ClientPrint(pPlayer,HUD_PRINTCENTER,"Pull mode");returnm_bHook;}else{m_bHook=true;ClientPrint(pPlayer,HUD_PRINTCENTER,"Rappel mode");returnm_bHook;}#endifreturnm_bHook;}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------voidCWeaponGrapple::ItemBusyFrame(void){// Allow zoom toggling even when we're reloading//CheckZoomToggle();}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------voidCWeaponGrapple::ItemPostFrame(void){//Enforces being able to use PrimaryAttack and Secondary AttackCBasePlayer*pOwner=ToBasePlayer(GetOwner());if((pOwner->m_nButtons&IN_ATTACK)){if(m_flNextPrimaryAttack<gpGlobals->curtime){PrimaryAttack();}}elseif(m_bMustReload)//&& HasWeaponIdleTimeElapsed() ){Reload();}if((pOwner->m_afButtonPressed&IN_ATTACK2)){if(m_flNextPrimaryAttack<gpGlobals->curtime){SecondaryAttack();}}elseif(m_bMustReload)//&& HasWeaponIdleTimeElapsed() ){Reload();}//Allow a refire as fast as the player can clickif(((pOwner->m_nButtons&IN_ATTACK)==false)){m_flNextPrimaryAttack=gpGlobals->curtime-0.1f;}#ifndef CLIENT_DLLif(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//-----------------------------------------------------------------------------voidCWeaponGrapple::FireHook(void){if(m_bMustReload)return;CBasePlayer*pOwner=ToBasePlayer(GetOwner());if(pOwner==NULL)return;#ifndef CLIENT_DLLVectorvecAiming=pOwner->GetAutoaimVector(0);VectorvecSrc=pOwner->Weapon_ShootPosition();QAngleangAiming;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;#endifpOwner->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.//-----------------------------------------------------------------------------boolCWeaponGrapple::Deploy(void){if(m_bMustReload){returnDefaultDeploy((char*)GetViewModel(),(char*)GetWorldModel(),ACT_CROSSBOW_DRAW_UNLOADED,(char*)GetAnimPrefix());}returnBaseClass::Deploy();}//-----------------------------------------------------------------------------// Purpose:// Input : *pSwitchingTo -// Output : Returns true on success, false on failure.//-----------------------------------------------------------------------------boolCWeaponGrapple::Holster(CBaseCombatWeapon*pSwitchingTo){#ifndef CLIENT_DLLif(m_hHook){m_hHook->SetTouch(NULL);m_hHook->SetThink(NULL);UTIL_Remove(m_hHook);m_hHook=NULL;NotifyHookDied();m_bMustReload=true;}#endifreturnBaseClass::Holster(pSwitchingTo);}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------voidCWeaponGrapple::Drop(constVector&vecVelocity){#ifndef CLIENT_DLLif(m_hHook){m_hHook->SetTouch(NULL);m_hHook->SetThink(NULL);UTIL_Remove(m_hHook);m_hHook=NULL;NotifyHookDied();m_bMustReload=true;}#endifBaseClass::Drop(vecVelocity);}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------boolCWeaponGrapple::HasAnyAmmo(void){if(m_hHook!=NULL)returntrue;returnBaseClass::HasAnyAmmo();}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------boolCWeaponGrapple::CanHolster(void){//Can't have an active hook outif(m_hHook!=NULL)returnfalse;returnBaseClass::CanHolster();}//-----------------------------------------------------------------------------// Purpose://-----------------------------------------------------------------------------voidCWeaponGrapple::NotifyHookDied(void){m_hHook=NULL;#ifndef CLIENT_DLLif(pBeam){UTIL_Remove(pBeam);//Kill beampBeam=NULL;UTIL_Remove(m_pLightGlow);//Kill spritem_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?)//-----------------------------------------------------------------------------voidCWeaponGrapple::DrawBeam(constVector&startPos,constVector&endPos,floatwidth){#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_ttr;//Draw the main beam shaftpBeam=CBeam::BeamCreate("sprites/physbeam.vmt",15.5);// It starts at startPospBeam->SetStartPos(startPos);// This sets up some things that the beam uses to figure out where// it should start and endpBeam->PointEntInit(endPos,this);// This makes it so that the beam appears to come from the muzzle of the pistolpBeam->SetEndAttachment(LookupAttachment("Muzzle"));pBeam->SetWidth(width);// pBeam->SetEndWidth( 0.05f );// Higher brightness means less transparentpBeam->SetBrightness(255);//pBeam->SetColor( 255, 185+random->RandomInt( -16, 16 ), 40 );pBeam->RelinkBeam();//Sets scrollrate of the beam spritefloatscrollOffset=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 - ???//-----------------------------------------------------------------------------voidCWeaponGrapple::DoImpactEffect(trace_t&tr,intnDamageType){#ifndef CLIENT_DLLif((tr.surface.flags&SURF_SKY)==false){CPVSFilterfilter(tr.endpos);te->GaussExplosion(filter,0.0f,tr.endpos,tr.plane.normal,0);m_nBulletType=GetAmmoDef()->Index("GaussEnergy");UTIL_ImpactTrace(&tr,m_nBulletType);}#endif}

Weapon Script (weapon_grapple.txt)

// Grapple HookWeaponData{// 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"}}}