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.
375 lines
10 KiB
375 lines
10 KiB
//========= 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; |
|
} |
|
}
|
|
|