You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
858 lines
26 KiB
858 lines
26 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// TF Grappling Hook |
|
// |
|
//============================================================================= |
|
#include "cbase.h" |
|
#include "in_buttons.h" |
|
#include "tf_weapon_grapplinghook.h" |
|
|
|
// Client specific. |
|
#ifdef CLIENT_DLL |
|
#include "c_tf_player.h" |
|
#include "gc_clientsystem.h" |
|
#include "prediction.h" |
|
#include "soundenvelope.h" |
|
// Server specific. |
|
#else |
|
#include "tf_player.h" |
|
#include "entity_rune.h" |
|
#include "effect_dispatch_data.h" |
|
#include "tf_fx.h" |
|
#include "func_respawnroom.h" |
|
#endif |
|
|
|
//============================================================================= |
|
// |
|
// Grappling hook tables. |
|
// |
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFGrapplingHook, DT_GrapplingHook ) |
|
|
|
BEGIN_NETWORK_TABLE( CTFGrapplingHook, DT_GrapplingHook ) |
|
#ifdef GAME_DLL |
|
SendPropEHandle( SENDINFO( m_hProjectile ) ), |
|
#else // GAME_DLL |
|
RecvPropEHandle( RECVINFO( m_hProjectile ) ), |
|
#endif // CLIENT_DLL |
|
END_NETWORK_TABLE() |
|
|
|
BEGIN_PREDICTION_DATA( CTFGrapplingHook ) |
|
END_PREDICTION_DATA() |
|
|
|
LINK_ENTITY_TO_CLASS( tf_weapon_grapplinghook, CTFGrapplingHook ); |
|
PRECACHE_WEAPON_REGISTER( tf_weapon_grapplinghook ); |
|
|
|
// Server specific. |
|
#ifndef CLIENT_DLL |
|
BEGIN_DATADESC( CTFGrapplingHook ) |
|
END_DATADESC() |
|
#endif // CLIENT_DLL |
|
|
|
// This is basically a copy of s_acttableMeleeAllclass table except primary fire to use grappling hook specific |
|
acttable_t s_grapplinghook_normal_acttable[] = |
|
{ |
|
{ ACT_MP_STAND_IDLE, ACT_MP_STAND_MELEE_ALLCLASS, false }, |
|
{ ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_MELEE_ALLCLASS, false }, |
|
{ ACT_MP_RUN, ACT_MP_RUN_MELEE_ALLCLASS, false }, |
|
{ ACT_MP_WALK, ACT_MP_WALK_MELEE_ALLCLASS, false }, |
|
{ ACT_MP_AIRWALK, ACT_GRAPPLE_PULL_IDLE, false }, |
|
{ ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_MELEE_ALLCLASS, false }, |
|
{ ACT_MP_JUMP, ACT_MP_JUMP_MELEE_ALLCLASS, false }, |
|
{ ACT_MP_JUMP_START, ACT_MP_JUMP_START_MELEE_ALLCLASS, false }, |
|
{ ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_MELEE_ALLCLASS, false }, |
|
{ ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_MELEE_ALLCLASS, false }, |
|
{ ACT_MP_SWIM, ACT_MP_SWIM_MELEE_ALLCLASS, false }, |
|
{ ACT_MP_DOUBLEJUMP_CROUCH, ACT_MP_DOUBLEJUMP_CROUCH_MELEE, false }, |
|
|
|
{ ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_GRAPPLE_FIRE_START, false }, |
|
{ ACT_MP_ATTACK_CROUCH_PRIMARYFIRE, ACT_GRAPPLE_FIRE_START, false }, |
|
{ ACT_MP_ATTACK_SWIM_PRIMARYFIRE, ACT_GRAPPLE_FIRE_START, false }, |
|
{ ACT_MP_ATTACK_AIRWALK_PRIMARYFIRE, ACT_GRAPPLE_FIRE_START, false }, |
|
|
|
{ ACT_MP_ATTACK_STAND_SECONDARYFIRE, ACT_MP_ATTACK_STAND_MELEE_SECONDARY, false }, |
|
{ ACT_MP_ATTACK_CROUCH_SECONDARYFIRE, ACT_MP_ATTACK_CROUCH_MELEE_SECONDARY,false }, |
|
{ ACT_MP_ATTACK_SWIM_SECONDARYFIRE, ACT_MP_ATTACK_SWIM_MELEE_ALLCLASS, false }, |
|
{ ACT_MP_ATTACK_AIRWALK_SECONDARYFIRE, ACT_MP_ATTACK_AIRWALK_MELEE_ALLCLASS, false }, |
|
|
|
{ ACT_MP_GESTURE_FLINCH, ACT_MP_GESTURE_FLINCH_MELEE, false }, |
|
|
|
{ ACT_MP_GRENADE1_DRAW, ACT_MP_MELEE_GRENADE1_DRAW, false }, |
|
{ ACT_MP_GRENADE1_IDLE, ACT_MP_MELEE_GRENADE1_IDLE, false }, |
|
{ ACT_MP_GRENADE1_ATTACK, ACT_MP_MELEE_GRENADE1_ATTACK, false }, |
|
{ ACT_MP_GRENADE2_DRAW, ACT_MP_MELEE_GRENADE2_DRAW, false }, |
|
{ ACT_MP_GRENADE2_IDLE, ACT_MP_MELEE_GRENADE2_IDLE, false }, |
|
{ ACT_MP_GRENADE2_ATTACK, ACT_MP_MELEE_GRENADE2_ATTACK, false }, |
|
|
|
{ ACT_MP_GESTURE_VC_HANDMOUTH, ACT_MP_GESTURE_VC_HANDMOUTH_MELEE, false }, |
|
{ ACT_MP_GESTURE_VC_FINGERPOINT, ACT_MP_GESTURE_VC_FINGERPOINT_MELEE, false }, |
|
{ ACT_MP_GESTURE_VC_FISTPUMP, ACT_MP_GESTURE_VC_FISTPUMP_MELEE, false }, |
|
{ ACT_MP_GESTURE_VC_THUMBSUP, ACT_MP_GESTURE_VC_THUMBSUP_MELEE, false }, |
|
{ ACT_MP_GESTURE_VC_NODYES, ACT_MP_GESTURE_VC_NODYES_MELEE, false }, |
|
{ ACT_MP_GESTURE_VC_NODNO, ACT_MP_GESTURE_VC_NODNO_MELEE, false }, |
|
}; |
|
|
|
// This is basically a copy of s_acttableSecondary table except primary fire to use grappling hook specific |
|
acttable_t s_grapplinghook_engineer_acttable[] = |
|
{ |
|
{ ACT_MP_STAND_IDLE, ACT_MP_STAND_SECONDARY, false }, |
|
{ ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_SECONDARY, false }, |
|
{ ACT_MP_RUN, ACT_MP_RUN_SECONDARY, false }, |
|
{ ACT_MP_WALK, ACT_MP_WALK_SECONDARY, false }, |
|
{ ACT_MP_AIRWALK, ACT_MP_AIRWALK_SECONDARY, false }, |
|
{ ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_SECONDARY, false }, |
|
{ ACT_MP_JUMP, ACT_MP_JUMP_SECONDARY, false }, |
|
{ ACT_MP_JUMP_START, ACT_MP_JUMP_START_SECONDARY, false }, |
|
{ ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_SECONDARY, false }, |
|
{ ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_SECONDARY, false }, |
|
{ ACT_MP_SWIM, ACT_MP_SWIM_SECONDARY, false }, |
|
{ ACT_MP_DOUBLEJUMP_CROUCH, ACT_MP_DOUBLEJUMP_CROUCH_SECONDARY, false }, |
|
|
|
{ ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_GRAPPLE_FIRE_START, false }, |
|
{ ACT_MP_ATTACK_CROUCH_PRIMARYFIRE, ACT_GRAPPLE_FIRE_START, false }, |
|
{ ACT_MP_ATTACK_SWIM_PRIMARYFIRE, ACT_GRAPPLE_FIRE_START, false }, |
|
{ ACT_MP_ATTACK_AIRWALK_PRIMARYFIRE, ACT_GRAPPLE_FIRE_START, false }, |
|
|
|
{ ACT_MP_RELOAD_STAND, ACT_MP_RELOAD_STAND_SECONDARY, false }, |
|
{ ACT_MP_RELOAD_STAND_LOOP, ACT_MP_RELOAD_STAND_SECONDARY_LOOP, false }, |
|
{ ACT_MP_RELOAD_STAND_END, ACT_MP_RELOAD_STAND_SECONDARY_END, false }, |
|
{ ACT_MP_RELOAD_CROUCH, ACT_MP_RELOAD_CROUCH_SECONDARY, false }, |
|
{ ACT_MP_RELOAD_CROUCH_LOOP,ACT_MP_RELOAD_CROUCH_SECONDARY_LOOP,false }, |
|
{ ACT_MP_RELOAD_CROUCH_END, ACT_MP_RELOAD_CROUCH_SECONDARY_END, false }, |
|
{ ACT_MP_RELOAD_SWIM, ACT_MP_RELOAD_SWIM_SECONDARY, false }, |
|
{ ACT_MP_RELOAD_SWIM_LOOP, ACT_MP_RELOAD_SWIM_SECONDARY_LOOP, false }, |
|
{ ACT_MP_RELOAD_SWIM_END, ACT_MP_RELOAD_SWIM_SECONDARY_END, false }, |
|
{ ACT_MP_RELOAD_AIRWALK, ACT_MP_RELOAD_AIRWALK_SECONDARY, false }, |
|
{ ACT_MP_RELOAD_AIRWALK_LOOP, ACT_MP_RELOAD_AIRWALK_SECONDARY_LOOP, false }, |
|
{ ACT_MP_RELOAD_AIRWALK_END,ACT_MP_RELOAD_AIRWALK_SECONDARY_END,false }, |
|
|
|
{ ACT_MP_GESTURE_FLINCH, ACT_MP_GESTURE_FLINCH_SECONDARY, false }, |
|
|
|
{ ACT_MP_GRENADE1_DRAW, ACT_MP_SECONDARY_GRENADE1_DRAW, false }, |
|
{ ACT_MP_GRENADE1_IDLE, ACT_MP_SECONDARY_GRENADE1_IDLE, false }, |
|
{ ACT_MP_GRENADE1_ATTACK, ACT_MP_SECONDARY_GRENADE1_ATTACK, false }, |
|
{ ACT_MP_GRENADE2_DRAW, ACT_MP_SECONDARY_GRENADE2_DRAW, false }, |
|
{ ACT_MP_GRENADE2_IDLE, ACT_MP_SECONDARY_GRENADE2_IDLE, false }, |
|
{ ACT_MP_GRENADE2_ATTACK, ACT_MP_SECONDARY_GRENADE2_ATTACK, false }, |
|
|
|
{ ACT_MP_ATTACK_STAND_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false }, |
|
{ ACT_MP_ATTACK_CROUCH_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false }, |
|
{ ACT_MP_ATTACK_SWIM_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false }, |
|
{ ACT_MP_ATTACK_AIRWALK_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false }, |
|
|
|
{ ACT_MP_GESTURE_VC_HANDMOUTH, ACT_MP_GESTURE_VC_HANDMOUTH_SECONDARY, false }, |
|
{ ACT_MP_GESTURE_VC_FINGERPOINT, ACT_MP_GESTURE_VC_FINGERPOINT_SECONDARY, false }, |
|
{ ACT_MP_GESTURE_VC_FISTPUMP, ACT_MP_GESTURE_VC_FISTPUMP_SECONDARY, false }, |
|
{ ACT_MP_GESTURE_VC_THUMBSUP, ACT_MP_GESTURE_VC_THUMBSUP_SECONDARY, false }, |
|
{ ACT_MP_GESTURE_VC_NODYES, ACT_MP_GESTURE_VC_NODYES_SECONDARY, false }, |
|
{ ACT_MP_GESTURE_VC_NODNO, ACT_MP_GESTURE_VC_NODNO_SECONDARY, false }, |
|
}; |
|
|
|
acttable_t *CTFGrapplingHook::ActivityList( int &iActivityCount ) |
|
{ |
|
CTFPlayer *pOwner = GetTFPlayerOwner(); |
|
if ( pOwner ) |
|
{ |
|
if ( pOwner->IsPlayerClass( TF_CLASS_ENGINEER ) ) |
|
{ |
|
iActivityCount = ARRAYSIZE( s_grapplinghook_engineer_acttable ); |
|
return s_grapplinghook_engineer_acttable; |
|
} |
|
else |
|
{ |
|
iActivityCount = ARRAYSIZE( s_grapplinghook_normal_acttable ); |
|
return s_grapplinghook_normal_acttable; |
|
} |
|
} |
|
|
|
return BaseClass::ActivityList( iActivityCount ); |
|
} |
|
|
|
|
|
poseparamtable_t s_grapplinghook_normal_poseparamtable[] = |
|
{ |
|
{ "R_hand_grip", 14 }, |
|
{ "R_arm", 2 }, |
|
}; |
|
|
|
poseparamtable_t s_grapplinghook_engineer_poseparamtable[] = |
|
{ |
|
{ "r_handposes_engineer", 1 }, |
|
}; |
|
|
|
poseparamtable_t *CTFGrapplingHook::PoseParamList( int &iPoseParamCount ) |
|
{ |
|
CTFPlayer *pOwner = GetTFPlayerOwner(); |
|
if ( pOwner ) |
|
{ |
|
if ( pOwner->IsPlayerClass( TF_CLASS_ENGINEER ) ) |
|
{ |
|
iPoseParamCount = ARRAYSIZE( s_grapplinghook_engineer_poseparamtable ); |
|
return s_grapplinghook_engineer_poseparamtable; |
|
} |
|
else |
|
{ |
|
iPoseParamCount = ARRAYSIZE( s_grapplinghook_normal_poseparamtable ); |
|
return s_grapplinghook_normal_poseparamtable; |
|
} |
|
} |
|
|
|
return BaseClass::PoseParamList( iPoseParamCount ); |
|
} |
|
|
|
|
|
ConVar tf_grapplinghook_projectile_speed( "tf_grapplinghook_projectile_speed", "1500", FCVAR_REPLICATED | FCVAR_CHEAT, "How fast does the grappliing hook projectile travel" ); |
|
ConVar tf_grapplinghook_max_distance( "tf_grapplinghook_max_distance", "2000", FCVAR_REPLICATED | FCVAR_CHEAT, "Valid distance for grappling hook to travel" ); |
|
ConVar tf_grapplinghook_fire_delay( "tf_grapplinghook_fire_delay", "0.5", FCVAR_REPLICATED | FCVAR_CHEAT ); |
|
|
|
float m_flNextSupernovaDenyWarning = 0.f; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFGrapplingHook::CTFGrapplingHook() |
|
{ |
|
#ifdef GAME_DLL |
|
m_bReleasedAfterLatched = false; |
|
#endif // GAME_DLL |
|
|
|
#ifdef CLIENT_DLL |
|
m_pHookSound = NULL; |
|
m_bLatched = false; |
|
#endif // CLIENT_DLL |
|
} |
|
|
|
|
|
void CTFGrapplingHook::Precache() |
|
{ |
|
BaseClass::Precache(); |
|
|
|
#ifdef GAME_DLL |
|
PrecacheScriptSound( "WeaponGrapplingHook.Shoot" ); |
|
#endif // GAME_DLL |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CBaseEntity *CTFGrapplingHook::FireProjectile( CTFPlayer *pPlayer ) |
|
{ |
|
#ifdef GAME_DLL |
|
Assert( m_hProjectile == NULL ); |
|
m_hProjectile = BaseClass::FireProjectile( pPlayer ); |
|
|
|
return m_hProjectile; |
|
#else |
|
return BaseClass::FireProjectile( pPlayer ); |
|
#endif // GAME_DLL |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGrapplingHook::ItemPostFrame( void ) |
|
{ |
|
CTFPlayer *pOwner = GetTFPlayerOwner(); |
|
if (!pOwner) |
|
return; |
|
|
|
#ifdef CLIENT_DLL |
|
static bool bFired = false; |
|
#endif // CLIENT_DLL |
|
|
|
CBaseEntity *pHookTarget = pOwner->GetGrapplingHookTarget(); |
|
bool bJump = ( pOwner->m_nButtons & IN_JUMP ) > 0; |
|
bool bForceReleaseHook = pHookTarget != NULL && bJump; |
|
|
|
if ( !bForceReleaseHook && ( pOwner->m_nButtons & IN_ATTACK || pOwner->IsUsingActionSlot() ) ) |
|
{ |
|
#ifdef CLIENT_DLL |
|
if ( !bFired ) |
|
{ |
|
PrimaryAttack(); |
|
bFired = true; |
|
} |
|
#else |
|
PrimaryAttack(); |
|
#endif // CLIENT_DLL |
|
|
|
if ( pOwner->GetGrapplingHookTarget() ) |
|
{ |
|
SendWeaponAnim( ACT_GRAPPLE_PULL_START ); |
|
} |
|
else if ( m_hProjectile ) |
|
{ |
|
SendWeaponAnim( ACT_VM_PRIMARYATTACK ); |
|
} |
|
else |
|
{ |
|
SendWeaponAnim( ACT_GRAPPLE_IDLE ); |
|
} |
|
} |
|
else |
|
{ |
|
#ifdef CLIENT_DLL |
|
bFired = false; |
|
#endif // CLIENT_DLL |
|
|
|
OnHookReleased( bForceReleaseHook ); |
|
} |
|
|
|
if ( pOwner->GetViewModel(0) ) |
|
{ |
|
pOwner->GetViewModel(0)->SetPlaybackRate( 1.f ); |
|
} |
|
if ( pOwner->GetViewModel(1) ) |
|
{ |
|
pOwner->GetViewModel(1)->SetPlaybackRate( 1.f ); |
|
} |
|
|
|
BaseClass::ItemPostFrame(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFGrapplingHook::CanAttack( void ) |
|
{ |
|
CTFPlayer *pPlayer = GetTFPlayerOwner(); |
|
|
|
if ( pPlayer->m_Shared.IsFeignDeathReady() ) |
|
return false; |
|
|
|
return BaseClass::CanAttack(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGrapplingHook::PrimaryAttack( void ) |
|
{ |
|
CTFPlayer *pOwner = GetTFPlayerOwner(); |
|
|
|
#ifdef GAME_DLL |
|
// make sure to unlatch from the current target and remove the old projectile before we fire a new one |
|
if ( m_bReleasedAfterLatched ) |
|
{ |
|
RemoveHookProjectile( true ); |
|
} |
|
#endif // GAME_DLL |
|
|
|
if ( m_hProjectile ) |
|
return; |
|
|
|
if ( m_flNextPrimaryAttack > gpGlobals->curtime ) |
|
return; |
|
|
|
if ( pOwner && pOwner->m_Shared.IsControlStunned() ) |
|
return; |
|
|
|
Vector vecSrc; |
|
QAngle angForward; |
|
Vector vecOffset( 23.5f, -8.0f, -3.0f ); // copied from CTFWeaponBaseGun::FireArrow |
|
GetProjectileFireSetup( pOwner, vecOffset, &vecSrc, &angForward, false ); |
|
Vector vecForward; |
|
AngleVectors( angForward, &vecForward ); |
|
|
|
// check if aiming at skybox |
|
trace_t tr; |
|
UTIL_TraceLine( vecSrc, vecSrc + tf_grapplinghook_max_distance.GetFloat() * vecForward, MASK_SOLID, pOwner, COLLISION_GROUP_DEBRIS, &tr ); |
|
if ( !tr.DidHit() || ( tr.fraction < 1.0 && tr.surface.flags & SURF_SKY ) ) |
|
{ |
|
#ifdef CLIENT_DLL |
|
// play fail sound on client here |
|
if ( pOwner && prediction->IsFirstTimePredicted() ) |
|
{ |
|
pOwner->EmitSound( "Player.DenyWeaponSelection" ); |
|
} |
|
#endif // CLIENT_DLL |
|
return; |
|
} |
|
|
|
BaseClass::PrimaryAttack(); |
|
|
|
m_flNextPrimaryAttack = gpGlobals->curtime + tf_grapplinghook_fire_delay.GetFloat(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFGrapplingHook::Deploy( void ) |
|
{ |
|
#ifdef GAME_DLL |
|
RemoveHookProjectile( true ); |
|
m_bReleasedAfterLatched = IsLatchedToTargetPlayer(); |
|
#endif // GAME_DLL |
|
return BaseClass::Deploy(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFGrapplingHook::Holster( CBaseCombatWeapon *pSwitchingTo ) |
|
{ |
|
#ifdef GAME_DLL |
|
RemoveHookProjectile(); |
|
m_bReleasedAfterLatched = IsLatchedToTargetPlayer(); |
|
#endif // GAME_DLL |
|
|
|
return BaseClass::Holster( pSwitchingTo ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGrapplingHook::GetProjectileFireSetup( CTFPlayer *pPlayer, Vector vecOffset, Vector *vecSrc, QAngle *angForward, bool bHitTeammates /*= true*/, float flEndDist /*= 2000.f*/ ) |
|
{ |
|
BaseClass::GetProjectileFireSetup( pPlayer, vecOffset, vecSrc, angForward, bHitTeammates, flEndDist ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
float CTFGrapplingHook::GetProjectileSpeed() |
|
{ |
|
CTFPlayer *pOwner = GetTFPlayerOwner(); |
|
|
|
if ( pOwner && pOwner->m_Shared.GetCarryingRuneType() == RUNE_AGILITY ) |
|
{ |
|
switch ( pOwner->GetPlayerClass()->GetClassIndex() ) |
|
{ |
|
case TF_CLASS_SOLDIER: |
|
case TF_CLASS_HEAVYWEAPONS: |
|
return 2600.f; |
|
default: |
|
return 3000.f; |
|
} |
|
} |
|
|
|
return tf_grapplinghook_projectile_speed.GetFloat(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFGrapplingHook::SendWeaponAnim( int actBase ) |
|
{ |
|
CTFPlayer *pPlayer = GetTFPlayerOwner(); |
|
if ( !pPlayer ) |
|
return BaseClass::SendWeaponAnim( actBase ); |
|
|
|
if ( actBase == ACT_VM_DRAW ) |
|
{ |
|
actBase = ACT_GRAPPLE_DRAW; |
|
} |
|
else if ( pPlayer->GetGrapplingHookTarget() ) |
|
{ |
|
if ( GetActivity() != ACT_GRAPPLE_PULL_START && GetActivity() != ACT_GRAPPLE_PULL_IDLE ) |
|
{ |
|
bool bResult = BaseClass::SendWeaponAnim( ACT_GRAPPLE_PULL_START ); |
|
//DevMsg("pull start %f\n", gpGlobals->curtime ); |
|
|
|
m_startPullingTimer.Start( SequenceDuration() ); |
|
|
|
return bResult; |
|
} |
|
else |
|
{ |
|
if ( GetActivity() == ACT_GRAPPLE_PULL_IDLE ) |
|
return true; |
|
|
|
if ( GetActivity() == ACT_GRAPPLE_PULL_START && m_startPullingTimer.HasStarted() && !m_startPullingTimer.IsElapsed() ) |
|
return true; |
|
|
|
actBase = ACT_GRAPPLE_PULL_IDLE; |
|
//DevMsg("pull idle %f\n", gpGlobals->curtime ); |
|
|
|
m_startPullingTimer.Invalidate(); |
|
} |
|
} |
|
else if ( actBase == ACT_VM_PRIMARYATTACK ) |
|
{ |
|
if ( GetActivity() != ACT_GRAPPLE_FIRE_START && GetActivity() != ACT_GRAPPLE_FIRE_IDLE ) |
|
{ |
|
bool bResult = BaseClass::SendWeaponAnim( ACT_GRAPPLE_FIRE_START ); |
|
//DevMsg("fire start %f\n", gpGlobals->curtime ); |
|
|
|
m_startFiringTimer.Start( SequenceDuration() ); |
|
|
|
return bResult; |
|
} |
|
else |
|
{ |
|
if ( GetActivity() == ACT_GRAPPLE_FIRE_IDLE ) |
|
return true; |
|
|
|
if ( GetActivity() == ACT_GRAPPLE_FIRE_START && m_startFiringTimer.HasStarted() && !m_startFiringTimer.IsElapsed() ) |
|
return true; |
|
|
|
actBase = ACT_GRAPPLE_FIRE_IDLE; |
|
//DevMsg("fire idle %f\n", gpGlobals->curtime ); |
|
|
|
m_startFiringTimer.Invalidate(); |
|
} |
|
} |
|
else |
|
{ |
|
if ( GetActivity() == ACT_GRAPPLE_PULL_IDLE ) |
|
{ |
|
actBase = ACT_GRAPPLE_PULL_END; |
|
//DevMsg("pull end %f\n", gpGlobals->curtime ); |
|
} |
|
else |
|
{ |
|
if ( GetActivity() == ACT_GRAPPLE_IDLE ) |
|
return true; |
|
|
|
actBase = ACT_GRAPPLE_IDLE; |
|
//DevMsg("grapple idle %f\n", gpGlobals->curtime ); |
|
} |
|
} |
|
|
|
return BaseClass::SendWeaponAnim( actBase ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGrapplingHook::PlayWeaponShootSound( void ) |
|
{ |
|
#ifdef GAME_DLL |
|
EmitSound( "WeaponGrapplingHook.Shoot" ); |
|
#endif // GAME_DLL |
|
} |
|
|
|
|
|
|
|
#ifdef GAME_DLL |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGrapplingHook::ActivateRune() |
|
{ |
|
CTFPlayer *pOwner = GetTFPlayerOwner(); |
|
if ( pOwner && pOwner->m_Shared.IsRuneCharged() ) |
|
{ |
|
RuneTypes_t type = pOwner->m_Shared.GetCarryingRuneType(); |
|
if ( type == RUNE_SUPERNOVA ) |
|
{ |
|
// don't allow stealthed player to activate the power |
|
if ( pOwner->m_Shared.IsStealthed() ) |
|
return; |
|
|
|
int nEnemyTeam = GetEnemyTeam( pOwner->GetTeamNumber() ); |
|
|
|
// apply super nova effect to all enemies |
|
bool bHitAnyTarget = false; |
|
CUtlVector< CTFPlayer* > vecPlayers; |
|
CUtlVector< CTFPlayer* > vecVictims; |
|
CollectPlayers( &vecPlayers, nEnemyTeam, COLLECT_ONLY_LIVING_PLAYERS ); |
|
const float flEffectRadiusSqr = Sqr( 1500.f ); |
|
const float flMinPushForce = 200.f; |
|
const float flMaxPushForce = 500.f; |
|
const float flMinStunDuration = 2.f; |
|
const float flMaxStunDuration = 4.f; |
|
for ( int i = 0; i < vecPlayers.Count(); ++i ) |
|
{ |
|
CTFPlayer *pOther = vecPlayers[i]; |
|
Vector toPlayer = pOther->WorldSpaceCenter() - pOwner->WorldSpaceCenter(); |
|
float flDistSqr = toPlayer.LengthSqr(); |
|
|
|
// Collect valid enemies |
|
if ( flDistSqr <= flEffectRadiusSqr && pOwner->IsLineOfSightClear( pOther, CBaseCombatCharacter::IGNORE_ACTORS ) && !PointInRespawnRoom( pOther, pOther->WorldSpaceCenter() ) ) |
|
{ |
|
vecVictims.AddToTail( pOther ); |
|
} |
|
} |
|
|
|
// if there is more than one victim, the stun duration increases |
|
float flStunDuration = MIN( flMinStunDuration + ( ( vecVictims.Count() - 1 ) * 0.5 ), flMaxStunDuration ); |
|
|
|
for ( int i = 0; i < vecVictims.Count(); ++i ) |
|
{ |
|
// force enemy to drop rune, stun, and push them |
|
CTFPlayer *pOther = vecVictims[i]; |
|
const char *pszEffect = pOwner->GetTeamNumber() == TF_TEAM_RED ? "powerup_supernova_strike_red" : "powerup_supernova_strike_blue"; |
|
|
|
CPVSFilter filter( WorldSpaceCenter() ); |
|
Vector vStart = pOwner->WorldSpaceCenter(); |
|
Vector vEnd = pOther->GetAbsOrigin() + Vector( 0, 0, 56 ); |
|
te_tf_particle_effects_control_point_t controlPoint = { PATTACH_ABSORIGIN, vEnd }; |
|
TE_TFParticleEffectComplex( filter, 0.f, pszEffect, vStart, QAngle( 0.f, 0.f, 0.f ), NULL, &controlPoint, pOther, PATTACH_CUSTOMORIGIN ); |
|
|
|
|
|
pOther->DropRune( false, pOwner->GetTeamNumber() ); |
|
pOther->DropFlag(); |
|
|
|
pOther->m_Shared.StunPlayer( flStunDuration, 1.f, TF_STUN_MOVEMENT | TF_STUN_CONTROLS, pOwner ); |
|
|
|
// send the player flying |
|
// make sure we push players up and away |
|
Vector toPlayer = pOther->WorldSpaceCenter() - pOwner->WorldSpaceCenter(); |
|
toPlayer.z = 0.0f; |
|
toPlayer.NormalizeInPlace(); |
|
toPlayer.z = 1.0f; |
|
|
|
// scale push force based on distance from the supernova origin |
|
float flDistSqr = toPlayer.LengthSqr(); |
|
float flPushForce = RemapValClamped( flDistSqr, 0.f, flEffectRadiusSqr, flMaxPushForce, flMinPushForce ); |
|
Vector vPush = flPushForce * toPlayer; |
|
pOther->ApplyAbsVelocityImpulse( vPush ); |
|
|
|
bHitAnyTarget = true; |
|
} |
|
|
|
// don't deploy with no target |
|
if ( bHitAnyTarget ) |
|
{ |
|
// play effect |
|
const char *pszEffect = pOwner->GetTeamNumber() == TF_TEAM_RED ? "powerup_supernova_explode_red" : "powerup_supernova_explode_blue"; |
|
CEffectData data; |
|
data.m_nHitBox = GetParticleSystemIndex( pszEffect ); |
|
data.m_vOrigin = pOwner->GetAbsOrigin(); |
|
data.m_vAngles = vec3_angle; |
|
|
|
CPASFilter filter( data.m_vOrigin ); |
|
filter.SetIgnorePredictionCull( true ); |
|
|
|
te->DispatchEffect( filter, 0.0, data.m_vOrigin, "ParticleEffect", data ); |
|
pOwner->EmitSound( "Powerup.PickUpSupernovaActivate" ); |
|
|
|
// remove the power and reposition instantly |
|
pOwner->m_Shared.SetCarryingRuneType( RUNE_NONE ); |
|
CTFRune::RepositionRune( type, TEAM_ANY ); |
|
} |
|
else |
|
{ |
|
if ( gpGlobals->curtime > m_flNextSupernovaDenyWarning ) |
|
{ |
|
m_flNextSupernovaDenyWarning = gpGlobals->curtime + 0.5f; |
|
|
|
CSingleUserRecipientFilter singleFilter( pOwner ); |
|
EmitSound( singleFilter, pOwner->entindex(), "Player.UseDeny" ); |
|
ClientPrint( pOwner, HUD_PRINTCENTER, "#TF_Powerup_Supernova_Deny" ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGrapplingHook::RemoveHookProjectile( bool bForce /*= false*/ ) |
|
{ |
|
if ( !bForce && IsLatchedToTargetPlayer() ) |
|
{ |
|
// don't remove the projectile until we unlatched from the target (by hooking again) |
|
return; |
|
} |
|
|
|
if ( m_hProjectile ) |
|
{ |
|
UTIL_Remove( m_hProjectile ); |
|
m_hProjectile = NULL; |
|
} |
|
} |
|
|
|
bool CTFGrapplingHook::IsLatchedToTargetPlayer() const |
|
{ |
|
CTFPlayer *pOwner = GetTFPlayerOwner(); |
|
return pOwner && pOwner->GetGrapplingHookTarget() && pOwner->GetGrapplingHookTarget()->IsPlayer(); |
|
} |
|
|
|
#endif // GAME_DLL |
|
|
|
void CTFGrapplingHook::OnHookReleased( bool bForce ) |
|
{ |
|
#ifdef GAME_DLL |
|
RemoveHookProjectile( bForce ); |
|
m_bReleasedAfterLatched = IsLatchedToTargetPlayer(); |
|
#endif // GAME_DLL |
|
|
|
if ( GetActivity() != ACT_GRAPPLE_DRAW && GetActivity() != ACT_GRAPPLE_IDLE && GetActivity() != ACT_GRAPPLE_PULL_END ) |
|
SendWeaponAnim( ACT_GRAPPLE_PULL_END ); |
|
|
|
if ( bForce ) |
|
m_flNextPrimaryAttack = gpGlobals->curtime + tf_grapplinghook_fire_delay.GetFloat(); |
|
} |
|
|
|
#ifdef CLIENT_DLL |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGrapplingHook::UpdateOnRemove() |
|
{ |
|
StopHookSound(); |
|
|
|
BaseClass::UpdateOnRemove(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGrapplingHook::OnDataChanged( DataUpdateType_t type ) |
|
{ |
|
BaseClass::OnDataChanged( type ); |
|
|
|
UpdateHookSound(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGrapplingHook::StartHookSound() |
|
{ |
|
StopHookSound(); |
|
|
|
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); |
|
CLocalPlayerFilter filter; |
|
m_pHookSound = controller.SoundCreate( filter, entindex(), "WeaponGrapplingHook.ReelStart" ); |
|
controller.Play( m_pHookSound, 1.0, 100 ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGrapplingHook::StopHookSound() |
|
{ |
|
if ( m_pHookSound ) |
|
{ |
|
CSoundEnvelopeController::GetController().SoundDestroy( m_pHookSound ); |
|
m_pHookSound = NULL; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGrapplingHook::UpdateHookSound() |
|
{ |
|
CTFPlayer *pOwner = GetTFPlayerOwner(); |
|
if ( pOwner ) |
|
{ |
|
bool bLatched = pOwner->GetGrapplingHookTarget() != NULL && m_hProjectile != NULL; |
|
if ( m_bLatched != bLatched ) |
|
{ |
|
if ( !m_bLatched ) |
|
{ |
|
StartHookSound(); |
|
} |
|
else |
|
{ |
|
StopHookSound(); |
|
|
|
CLocalPlayerFilter filter; |
|
EmitSound( filter, entindex(), "WeaponGrapplingHook.ReelStop" ); |
|
} |
|
|
|
m_bLatched = bLatched; |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// CEquipGrapplingHookNotification |
|
//----------------------------------------------------------------------------- |
|
void CEquipGrapplingHookNotification::Accept() |
|
{ |
|
m_bHasTriggered = true; |
|
|
|
CPlayerInventory *pLocalInv = TFInventoryManager()->GetLocalInventory(); |
|
if ( !pLocalInv ) |
|
{ |
|
MarkForDeletion(); |
|
return; |
|
} |
|
|
|
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); |
|
if ( !pLocalPlayer ) |
|
{ |
|
MarkForDeletion(); |
|
return; |
|
} |
|
|
|
// try to equip non-stock-grapplinghook first |
|
/*static CSchemaItemDefHandle pItemDef_GrapplingHook( "TF_WEAPON_GRAPPLINGHOOK" ); |
|
|
|
Assert( pItemDef_GrapplingHook ); |
|
|
|
CEconItemView *pGrapplingHook = NULL; |
|
|
|
if ( pItemDef_GrapplingHook ) |
|
{ |
|
for ( int i = 0 ; i < pLocalInv->GetItemCount() ; ++i ) |
|
{ |
|
CEconItemView *pItem = pLocalInv->GetItem( i ); |
|
Assert( pItem ); |
|
if ( pItem->GetItemDefinition() == pItemDef_GrapplingHook ) |
|
{ |
|
pGrapplingHook = pItem; |
|
break; |
|
} |
|
} |
|
}*/ |
|
|
|
// Default item becomes a grappling hook in this mode |
|
itemid_t iItemId = INVALID_ITEM_ID; |
|
/*if ( pGrapplingHook ) |
|
{ |
|
iItemId = pGrapplingHook->GetItemID(); |
|
}*/ |
|
|
|
if ( iItemId == INVALID_ITEM_ID ) |
|
{ |
|
iItemId = 0; |
|
|
|
static CSchemaItemDefHandle pItemDef_Grapple( "TF_WEAPON_GRAPPLINGHOOK" ); |
|
CEconItemView *pDefaultGrapple = TFInventoryManager()->GetBaseItemForClass( pLocalPlayer->GetPlayerClass()->GetClassIndex(), LOADOUT_POSITION_ACTION ); |
|
if ( pDefaultGrapple ) |
|
{ |
|
if ( pDefaultGrapple->GetItemDefinition() == pItemDef_Grapple ) |
|
{ |
|
iItemId = pDefaultGrapple->GetItemID(); |
|
} |
|
} |
|
} |
|
|
|
TFInventoryManager()->EquipItemInLoadout( pLocalPlayer->GetPlayerClass()->GetClassIndex(), LOADOUT_POSITION_ACTION, iItemId ); |
|
|
|
// Tell the GC to tell server that we should respawn if we're in a respawn room |
|
GCSDK::CGCMsg< GCSDK::MsgGCEmpty_t > msg( k_EMsgGCRespawnPostLoadoutChange ); |
|
GCClientSystem()->BSendMessage( msg ); |
|
|
|
MarkForDeletion(); |
|
} |
|
|
|
//=========================================================================================== |
|
void CEquipGrapplingHookNotification::UpdateTick() |
|
{ |
|
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); |
|
if ( pLocalPlayer ) |
|
{ |
|
CTFGrapplingHook *pGrapplingHook = dynamic_cast<CTFGrapplingHook*>( pLocalPlayer->Weapon_OwnsThisID( TF_WEAPON_GRAPPLINGHOOK ) ); |
|
if ( pGrapplingHook ) |
|
{ |
|
MarkForDeletion(); |
|
} |
|
} |
|
} |
|
|
|
#endif // CLIENT_DLL
|
|
|