summaryrefslogtreecommitdiff
path: root/game/client/tf/c_tf_projectile_arrow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/tf/c_tf_projectile_arrow.cpp')
-rw-r--r--game/client/tf/c_tf_projectile_arrow.cpp375
1 files changed, 375 insertions, 0 deletions
diff --git a/game/client/tf/c_tf_projectile_arrow.cpp b/game/client/tf/c_tf_projectile_arrow.cpp
new file mode 100644
index 0000000..67c8685
--- /dev/null
+++ b/game/client/tf/c_tf_projectile_arrow.cpp
@@ -0,0 +1,375 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "c_tf_projectile_arrow.h"
+#include "particles_new.h"
+#include "SpriteTrail.h"
+#include "c_tf_player.h"
+#include "collisionutils.h"
+#include "util_shared.h"
+#include "c_rope.h"
+
+//-----------------------------------------------------------------------------
+IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_Arrow, DT_TFProjectile_Arrow )
+
+BEGIN_NETWORK_TABLE( C_TFProjectile_Arrow, DT_TFProjectile_Arrow )
+ RecvPropBool( RECVINFO( m_bArrowAlight ) ),
+ RecvPropBool( RECVINFO( m_bCritical ) ),
+ RecvPropInt( RECVINFO( m_iProjectileType ) ),
+END_NETWORK_TABLE()
+
+//-----------------------------------------------------------------------------
+IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_HealingBolt, DT_TFProjectile_HealingBolt )
+
+BEGIN_NETWORK_TABLE( C_TFProjectile_HealingBolt, DT_TFProjectile_HealingBolt )
+END_NETWORK_TABLE()
+
+IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_GrapplingHook, DT_TFProjectile_GrapplingHook )
+
+BEGIN_NETWORK_TABLE( C_TFProjectile_GrapplingHook, DT_TFProjectile_GrapplingHook )
+END_NETWORK_TABLE()
+
+#define NEAR_MISS_THRESHOLD 120
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+C_TFProjectile_Arrow::C_TFProjectile_Arrow( void )
+{
+ m_fAttachTime = 0.f;
+ m_nextNearMissCheck = 0.f;
+ m_bNearMiss = false;
+ m_bArrowAlight = false;
+ m_bCritical = true;
+ m_pCritEffect = NULL;
+ m_iCachedDeflect = false;
+ m_flLifeTime = 40.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+C_TFProjectile_Arrow::~C_TFProjectile_Arrow( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_TFProjectile_Arrow::OnDataChanged( DataUpdateType_t updateType )
+{
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+
+#ifdef STAGING_ONLY
+ if ( m_iProjectileType == TF_PROJECTILE_SNIPERBULLET )
+ {
+ switch ( GetTeamNumber() )
+ {
+ case TF_TEAM_BLUE:
+ ParticleProp()->Create( "bullet_distortion_trail", PATTACH_ABSORIGIN_FOLLOW );
+ break;
+ case TF_TEAM_RED:
+ ParticleProp()->Create( "bullet_distortion_trail", PATTACH_ABSORIGIN_FOLLOW );
+ break;
+ }
+ }
+ else if ( m_bArrowAlight )
+#else
+ if ( m_bArrowAlight )
+#endif // STAGING_ONLY
+ {
+ ParticleProp()->Create( "flying_flaming_arrow", PATTACH_POINT_FOLLOW, "muzzle" );
+ }
+ }
+ if ( m_bCritical )
+ {
+ if ( updateType == DATA_UPDATE_CREATED || m_iCachedDeflect != GetDeflected() )
+ {
+ CreateCritTrail();
+ }
+ }
+ m_iCachedDeflect = GetDeflected();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_TFProjectile_Arrow::NotifyBoneAttached( C_BaseAnimating* attachTarget )
+{
+ BaseClass::NotifyBoneAttached( attachTarget );
+
+ m_fAttachTime = gpGlobals->curtime;
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_TFProjectile_Arrow::ClientThink( void )
+{
+ // Perform a near-miss check.
+ if ( !m_bNearMiss && (gpGlobals->curtime > m_nextNearMissCheck) )
+ {
+ CheckNearMiss();
+ m_nextNearMissCheck = gpGlobals->curtime + 0.05f;
+ }
+
+ // Remove crit effect if we hit a wall.
+ if ( GetMoveType() == MOVETYPE_NONE && m_pCritEffect )
+ {
+ ParticleProp()->StopEmission( m_pCritEffect );
+ m_pCritEffect = NULL;
+ }
+
+ BaseClass::ClientThink();
+
+ // DO THIS LAST: Destroy us automatically after a period of time.
+ if ( m_pAttachedTo )
+ {
+ if ( gpGlobals->curtime - m_fAttachTime > m_flLifeTime )
+ {
+ Release();
+ return;
+ }
+ else if ( m_pAttachedTo->IsEffectActive( EF_NODRAW ) && !IsEffectActive( EF_NODRAW ) )
+ {
+ AddEffects( EF_NODRAW );
+ UpdateVisibility();
+ }
+ else if ( !m_pAttachedTo->IsEffectActive( EF_NODRAW ) && IsEffectActive( EF_NODRAW ) && (m_pAttachedTo != C_BasePlayer::GetLocalPlayer()) )
+ {
+ RemoveEffects( EF_NODRAW );
+ UpdateVisibility();
+ }
+ }
+
+ if ( IsDormant() && !IsEffectActive( EF_NODRAW ) )
+ {
+ AddEffects( EF_NODRAW );
+ UpdateVisibility();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_TFProjectile_Arrow::CheckNearMiss( void )
+{
+ // Check against the local player. If we're near him play a near miss sound.
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( !pLocalPlayer || !pLocalPlayer->IsAlive() )
+ return;
+
+ // If we are attached to something or stationary we don't want to do near miss checks.
+ if ( m_pAttachedTo || (GetMoveType() == MOVETYPE_NONE) )
+ {
+ m_bNearMiss = true;
+ return;
+ }
+
+ // Can't hear near miss sounds from friendly arrows.
+ if ( pLocalPlayer->GetTeamNumber() == GetTeamNumber() )
+ return;
+
+ Vector vecPlayerPos = pLocalPlayer->GetAbsOrigin();
+ Vector vecArrowPos = GetAbsOrigin(), forward;
+ AngleVectors( GetAbsAngles(), &forward );
+ Vector vecArrowDest = GetAbsOrigin() + forward * 200.f;
+
+ // If the arrow is moving away from the player just stop checking.
+ float dist1 = vecArrowPos.DistToSqr( vecPlayerPos );
+ float dist2 = vecArrowDest.DistToSqr( vecPlayerPos );
+ if ( dist2 > dist1 )
+ {
+ m_bNearMiss = true;
+ return;
+ }
+
+ // Check to see if the arrow is passing near the player.
+ Vector vecClosestPoint;
+ float dist;
+ CalcClosestPointOnLineSegment( vecPlayerPos, vecArrowPos, vecArrowDest, vecClosestPoint, &dist );
+ dist = vecPlayerPos.DistTo( vecClosestPoint );
+ if ( dist > NEAR_MISS_THRESHOLD )
+ return;
+
+ // The arrow is passing close to the local player.
+ m_bNearMiss = true;
+ SetNextClientThink( CLIENT_THINK_NEVER );
+
+ // If the arrow is about to hit something, don't play the sound and stop this check.
+ trace_t tr;
+ UTIL_TraceLine( vecArrowPos, vecArrowPos + forward * 400.f, CONTENTS_HITBOX|CONTENTS_MONSTER|CONTENTS_SOLID, this, COLLISION_GROUP_NONE, &tr );
+ if ( tr.DidHit() )
+ return;
+
+ // We're good for a near miss!
+ float soundlen = 0;
+ EmitSound_t params;
+ params.m_flSoundTime = 0;
+ params.m_pSoundName = "Weapon_Arrow.Nearmiss";
+ params.m_pflSoundDuration = &soundlen;
+ CSingleUserRecipientFilter localFilter( pLocalPlayer );
+ EmitSound( localFilter, pLocalPlayer->entindex(), params );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_TFProjectile_Arrow::CreateCritTrail( void )
+{
+ if ( IsDormant() )
+ return;
+
+ if ( m_pCritEffect )
+ {
+ ParticleProp()->StopEmission( m_pCritEffect );
+ m_pCritEffect = NULL;
+ }
+
+ if ( m_bCritical )
+ {
+ switch( GetTeamNumber() )
+ {
+ case TF_TEAM_BLUE:
+ m_pCritEffect = ParticleProp()->Create( "critical_rocket_blue", PATTACH_ABSORIGIN_FOLLOW );
+ break;
+ case TF_TEAM_RED:
+ m_pCritEffect = ParticleProp()->Create( "critical_rocket_red", PATTACH_ABSORIGIN_FOLLOW );
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+void C_TFProjectile_HealingBolt::OnDataChanged( DataUpdateType_t updateType )
+{
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ switch( GetTeamNumber() )
+ {
+ case TF_TEAM_BLUE:
+ ParticleProp()->Create( "healshot_trail_blue", PATTACH_ABSORIGIN_FOLLOW );
+ break;
+ case TF_TEAM_RED:
+ ParticleProp()->Create( "healshot_trail_red", PATTACH_ABSORIGIN_FOLLOW );
+ break;
+ }
+ }
+
+ BaseClass::OnDataChanged( updateType );
+}
+
+
+void C_TFProjectile_GrapplingHook::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ int nTeam = GetTeamNumber();
+ C_TFPlayer *pTFPlayer = ToTFPlayer( GetOwnerEntity() );
+ if ( pTFPlayer && pTFPlayer->IsPlayerClass( TF_CLASS_SPY ) && pTFPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && pTFPlayer->GetTeamNumber() != GetLocalPlayerTeam() )
+ {
+ nTeam = pTFPlayer->m_Shared.GetDisguiseTeam();
+ }
+
+ const char *pszMaterialName = "cable/cable";
+ switch ( nTeam )
+ {
+ case TF_TEAM_BLUE:
+ pszMaterialName = "cable/cable_blue";
+ break;
+ case TF_TEAM_RED:
+ pszMaterialName = "cable/cable_red";
+ break;
+ }
+
+ C_BaseEntity *pStartEnt = GetOwnerEntity();
+ int iAttachment = 0;
+
+ if ( pTFPlayer )
+ {
+ CTFWeaponBase *pWeapon = assert_cast< CTFWeaponBase* >( pTFPlayer->GetActiveWeapon() );
+ if ( pWeapon )
+ {
+ pStartEnt = pWeapon;
+ int iMuzzle = pWeapon->LookupAttachment( "muzzle" );
+ if ( iMuzzle != -1 )
+ {
+ iAttachment = iMuzzle;
+ }
+ }
+ }
+
+ int iHookAttachment = LookupAttachment( "rope_locator" );
+ if ( iHookAttachment == -1 )
+ iHookAttachment = 0;
+
+ m_hRope = C_RopeKeyframe::Create( pStartEnt, this, iAttachment, iHookAttachment, 2, pszMaterialName );
+
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ }
+}
+
+
+void C_TFProjectile_GrapplingHook::UpdateOnRemove()
+{
+ RemoveRope();
+
+ BaseClass::UpdateOnRemove();
+}
+
+
+void C_TFProjectile_GrapplingHook::ClientThink()
+{
+ UpdateRope();
+}
+
+
+void C_TFProjectile_GrapplingHook::UpdateRope()
+{
+ C_TFPlayer *pTFPlayer = ToTFPlayer( GetOwnerEntity() );
+ if ( !pTFPlayer || !pTFPlayer->IsAlive() )
+ {
+ RemoveRope();
+ return;
+ }
+
+ Vector vecStart = pTFPlayer->WorldSpaceCenter();
+ if ( pTFPlayer->GetActiveWeapon() )
+ {
+ int iAttachment = pTFPlayer->GetActiveWeapon()->LookupAttachment( "muzzle" );
+ if ( iAttachment != -1 )
+ {
+ GetAttachment( iAttachment, vecStart );
+ }
+ }
+
+ float flDist = vecStart.DistTo( WorldSpaceCenter() );
+
+ if ( m_hRope )
+ {
+ float flHangDist = pTFPlayer->GetGrapplingHookTarget() ? 0.1f * flDist : 1.5f * flDist;
+ assert_cast< C_RopeKeyframe* >( m_hRope.Get() )->SetupHangDistance( flHangDist );
+ }
+}
+
+
+void C_TFProjectile_GrapplingHook::RemoveRope()
+{
+ if ( m_hRope )
+ {
+ m_hRope->Release();
+ m_hRope = NULL;
+ }
+}