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.
691 lines
20 KiB
691 lines
20 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Weapon Knife. |
|
// |
|
//============================================================================= |
|
#include "cbase.h" |
|
#include "tf_gamerules.h" |
|
#include "tf_weapon_knife.h" |
|
#include "decals.h" |
|
#include "debugoverlay_shared.h" |
|
|
|
// Client specific. |
|
#ifdef CLIENT_DLL |
|
#include "c_tf_player.h" |
|
#include "c_tf_gamestats.h" |
|
|
|
// Server specific. |
|
#else |
|
#include "tf_player.h" |
|
#include "tf_gamestats.h" |
|
#include "ilagcompensationmanager.h" |
|
#endif |
|
|
|
//============================================================================= |
|
// |
|
// Weapon Knife tables. |
|
// |
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFKnife, DT_TFWeaponKnife ) |
|
|
|
BEGIN_NETWORK_TABLE( CTFKnife, DT_TFWeaponKnife ) |
|
#if defined( CLIENT_DLL ) |
|
RecvPropBool( RECVINFO( m_bReadyToBackstab ) ), |
|
RecvPropBool( RECVINFO( m_bKnifeExists ) ), |
|
RecvPropFloat( RECVINFO( m_flKnifeRegenerateDuration ) ), |
|
RecvPropFloat( RECVINFO( m_flKnifeMeltTimestamp ) ), |
|
#else |
|
SendPropBool( SENDINFO( m_bReadyToBackstab ) ), |
|
SendPropBool( SENDINFO( m_bKnifeExists ) ), |
|
SendPropFloat( SENDINFO( m_flKnifeRegenerateDuration ), 0, SPROP_NOSCALE ), |
|
SendPropFloat( SENDINFO( m_flKnifeMeltTimestamp ), 0, SPROP_NOSCALE ), |
|
#endif |
|
END_NETWORK_TABLE() |
|
|
|
BEGIN_PREDICTION_DATA( CTFKnife ) |
|
#ifdef CLIENT_DLL |
|
DEFINE_PRED_FIELD( m_bReadyToBackstab, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), |
|
#endif |
|
END_PREDICTION_DATA() |
|
|
|
LINK_ENTITY_TO_CLASS( tf_weapon_knife, CTFKnife ); |
|
PRECACHE_WEAPON_REGISTER( tf_weapon_knife ); |
|
|
|
|
|
//============================================================================= |
|
// |
|
// Weapon Knife functions. |
|
// |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFKnife::CTFKnife() |
|
{ |
|
m_bReadyToBackstab = false; |
|
m_flBlockedTime = 0.f; |
|
m_bAllowHolsterBecauseForced = false; |
|
|
|
ResetVars(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFKnife::ResetVars( void ) |
|
{ |
|
m_bKnifeExists = true; |
|
m_flKnifeRegenerateDuration = 1.0f; |
|
m_flKnifeMeltTimestamp = 0.0f; |
|
m_bWasTaunting = false; |
|
m_bAllowHolsterBecauseForced = false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: We are regenerating (ie: resupply cabinet) |
|
//----------------------------------------------------------------------------- |
|
void CTFKnife::WeaponRegenerate( void ) |
|
{ |
|
BaseClass::WeaponRegenerate(); |
|
|
|
ResetVars(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFKnife::WeaponReset( void ) |
|
{ |
|
BaseClass::WeaponReset(); |
|
|
|
ResetVars(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
bool CTFKnife::DoSwingTrace( trace_t &trace ) |
|
{ |
|
return BaseClass::DoSwingTrace( trace ); |
|
} |
|
|
|
|
|
#ifdef GAME_DLL |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFKnife::ApplyOnInjuredAttributes( CTFPlayer *pVictim, CTFPlayer *pAttacker, const CTakeDamageInfo &info ) |
|
{ |
|
BaseClass::ApplyOnInjuredAttributes( pVictim, pAttacker, info ); |
|
|
|
int iMeltsInFire = 0; |
|
CALL_ATTRIB_HOOK_INT( iMeltsInFire, melts_in_fire ); |
|
if ( iMeltsInFire > 0 && info.GetDamageType() & DMG_BURN ) |
|
{ |
|
if ( m_bKnifeExists ) |
|
{ |
|
// melt it! |
|
m_bKnifeExists = false; |
|
m_flKnifeRegenerateDuration = iMeltsInFire; |
|
m_flKnifeMeltTimestamp = gpGlobals->curtime; |
|
|
|
CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); |
|
if ( pPlayer ) |
|
{ |
|
pPlayer->EmitSound( "Icicle.Melt" ); |
|
|
|
// force switch to sapper |
|
// Set flag to allow holstering during this forced switch (holstering might otherwise have been inhibited by being blocked). This addresses |
|
// a corner-case where a Spy stabbing a Razorback-equipped sniper with the Spycicle and then immediately burned doesn't switch away and could backstab immediately |
|
// even though their knife should have melted. |
|
CBaseCombatWeapon *mySapper = pPlayer->Weapon_GetWeaponByType( TF_WPN_TYPE_BUILDING ); |
|
m_bAllowHolsterBecauseForced = true; |
|
if ( !mySapper ) |
|
{ |
|
// this should never happen |
|
pPlayer->SwitchToNextBestWeapon( this ); |
|
} |
|
else |
|
{ |
|
pPlayer->Weapon_Switch( mySapper ); |
|
} |
|
m_bAllowHolsterBecauseForced = false; |
|
} |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool CTFKnife::DecreaseRegenerationTime( float value, bool bForce ) |
|
{ |
|
// didn't do anything |
|
if ( m_bKnifeExists ) |
|
return false; |
|
|
|
float flTime = value * 0.005f * m_flKnifeRegenerateDuration; |
|
m_flKnifeMeltTimestamp -= flTime; |
|
return true; |
|
} |
|
#endif |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Set stealth attack bool |
|
//----------------------------------------------------------------------------- |
|
void CTFKnife::PrimaryAttack( void ) |
|
{ |
|
CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); |
|
|
|
if ( !CanAttack() ) |
|
return; |
|
|
|
// Set the weapon usage mode - primary, secondary. |
|
m_iWeaponMode = TF_WEAPON_PRIMARY_MODE; |
|
|
|
m_hBackstabVictim = NULL; |
|
int iBackstabVictimHealth = 0; |
|
|
|
#if !defined (CLIENT_DLL) |
|
// Move other players back to history positions based on local player's lag |
|
lagcompensation->StartLagCompensation( pPlayer, pPlayer->GetCurrentCommand() ); |
|
#endif |
|
|
|
trace_t trace; |
|
if ( DoSwingTrace( trace ) == true ) |
|
{ |
|
// we will hit something with the attack |
|
if( trace.m_pEnt && trace.m_pEnt->IsPlayer() ) |
|
{ |
|
CTFPlayer *pTarget = ToTFPlayer( trace.m_pEnt ); |
|
|
|
if ( pTarget && pTarget->GetTeamNumber() != pPlayer->GetTeamNumber() ) |
|
{ |
|
// Deal extra damage to players when stabbing them from behind |
|
if ( CanPerformBackstabAgainstTarget( pTarget ) ) |
|
{ |
|
// store the victim to compare when we do the damage |
|
m_hBackstabVictim.Set( pTarget ); |
|
iBackstabVictimHealth = Max( m_hBackstabVictim->GetHealth(), 75 ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
#ifndef CLIENT_DLL |
|
pPlayer->RemoveInvisibility(); |
|
lagcompensation->FinishLagCompensation( pPlayer ); |
|
#endif |
|
|
|
// Swing the weapon. |
|
Swing( pPlayer ); |
|
Smack(); |
|
m_flSmackTime = -1.0f; |
|
|
|
m_bReadyToBackstab = false; // Hand is down. |
|
|
|
#if !defined( CLIENT_DLL ) |
|
pPlayer->SpeakWeaponFire(); |
|
CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() ); |
|
#endif |
|
#ifdef CLIENT_DLL |
|
C_CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() ); |
|
#endif |
|
|
|
bool bSuccessfulBackstab = IsBackstab() && !m_hBackstabVictim->IsAlive(); |
|
|
|
ETFFlagType ignoreTypes[] = { TF_FLAGTYPE_PLAYER_DESTRUCTION }; |
|
if ( ShouldDisguiseOnBackstab() && bSuccessfulBackstab && !pPlayer->HasTheFlag( ignoreTypes, ARRAYSIZE( ignoreTypes ) ) ) |
|
{ |
|
// Different rules in MvM when stabbing bots |
|
bool bMvM = TFGameRules() && TFGameRules()->IsMannVsMachineMode() && m_hBackstabVictim->IsBot(); |
|
if ( bMvM ) |
|
{ |
|
// Remove the disguise first, otherwise this attribute is overpowered |
|
pPlayer->RemoveDisguise(); |
|
} |
|
|
|
// We should very quickly disguise as our victim. |
|
const float flDelay = bMvM ? 1.5f : 0.2f; |
|
SetContextThink( &CTFKnife::DisguiseOnKill, gpGlobals->curtime + flDelay, "DisguiseOnKill" ); |
|
} |
|
else |
|
{ |
|
pPlayer->RemoveDisguise(); |
|
} |
|
|
|
|
|
#ifdef GAME_DLL |
|
int iSanguisuge = 0; |
|
CALL_ATTRIB_HOOK_INT( iSanguisuge, sanguisuge ); |
|
if ( bSuccessfulBackstab && iSanguisuge > 0 ) |
|
{ |
|
// Our health cap is 3x our default maximum health cap. This is so high to make up for |
|
// the fact that our default is lowered by equipping the weapon. |
|
int iBaseMaxHealth = pPlayer->GetMaxHealth() * 3, |
|
iNewHealth = MIN( pPlayer->GetHealth() + iBackstabVictimHealth, iBaseMaxHealth ), |
|
iDeltaHealth = iNewHealth - pPlayer->GetHealth(); |
|
|
|
if ( iDeltaHealth > 0 ) |
|
{ |
|
pPlayer->TakeHealth( iDeltaHealth, DMG_IGNORE_MAXHEALTH ); |
|
pPlayer->m_Shared.HealthKitPickupEffects( iDeltaHealth ); |
|
} |
|
} |
|
#endif // GAME_DLL |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFKnife::DisguiseOnKill() |
|
{ |
|
#ifdef GAME_DLL |
|
if ( !m_hBackstabVictim.Get() ) |
|
return; |
|
|
|
int nTeam = m_hBackstabVictim->GetTeamNumber(); |
|
int nClass = m_hBackstabVictim->GetPlayerClass()->GetClassIndex(); |
|
CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); |
|
if ( pPlayer ) |
|
{ |
|
pPlayer->m_Shared.Disguise( nTeam, nClass, m_hBackstabVictim.Get(), true ); |
|
} |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFKnife::ShouldDisguiseOnBackstab() |
|
{ |
|
int iDisguiseAsVictim = 0; |
|
CALL_ATTRIB_HOOK_INT( iDisguiseAsVictim, set_disguise_on_backstab ); |
|
if ( iDisguiseAsVictim == 1 ) |
|
return true; |
|
else |
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Do backstab damage |
|
//----------------------------------------------------------------------------- |
|
float CTFKnife::GetMeleeDamage( CBaseEntity *pTarget, int* piDamageType, int* piCustomDamage ) |
|
{ |
|
float flBaseDamage = BaseClass::GetMeleeDamage( pTarget, piDamageType, piCustomDamage ); |
|
CTFPlayer *pTFOwner = ToTFPlayer( GetPlayerOwner() ); |
|
if ( !pTFOwner ) |
|
return false; |
|
|
|
if ( pTarget->IsPlayer() ) |
|
{ |
|
if ( IsBackstab() ) |
|
{ |
|
CTFPlayer *pTFTarget = ToTFPlayer( pTarget ); |
|
// Special rules in modes where player power grows significantly |
|
if ( !pTFOwner->IsBot() && pTFTarget && pTFTarget->IsMiniBoss() ) |
|
{ |
|
// MvM: Cap damage against bots and check for a damage upgrade |
|
float flBonusDmg = 1.f; |
|
CALL_ATTRIB_HOOK_FLOAT( flBonusDmg, mult_dmg ); |
|
flBaseDamage = 250.f * flBonusDmg; |
|
|
|
// Minibosses: Adjust damage when backstabbing based on level of armor piercing |
|
// Base amount is 25% of normal damage. Each level adds 25% to a cap of 125%. |
|
float flArmorPiercing = 25.f; |
|
CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pTFOwner, flArmorPiercing, armor_piercing ); |
|
flBaseDamage *= clamp( flArmorPiercing / 100.0f, 0.25f, 1.25f ); |
|
} |
|
else // Regular game mode, or the attacker is a bot |
|
{ |
|
// Do twice the target's health so that random modification will still kill him. |
|
flBaseDamage = pTarget->GetHealth() * 2; |
|
} |
|
|
|
// Declare a backstab. |
|
*piCustomDamage = TF_DMG_CUSTOM_BACKSTAB; |
|
} |
|
else if (pTFOwner->m_Shared.IsCritBoosted()) |
|
{ |
|
m_bCurrentAttackIsCrit = true; |
|
} |
|
else |
|
{ |
|
m_bCurrentAttackIsCrit = false; // don't do a crit if we failed the above checks. |
|
} |
|
} |
|
|
|
return flBaseDamage; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Are we in a backstab position? |
|
//----------------------------------------------------------------------------- |
|
bool CTFKnife::CanPerformBackstabAgainstTarget( CTFPlayer *pTarget ) |
|
{ |
|
if ( !pTarget ) |
|
return false; |
|
|
|
// Immune? |
|
int iNoBackstab = 0; |
|
CALL_ATTRIB_HOOK_INT_ON_OTHER( pTarget, iNoBackstab, cannot_be_backstabbed ); |
|
if ( iNoBackstab ) |
|
return false; |
|
|
|
// Behind and facing target's back? |
|
if ( IsBehindAndFacingTarget( pTarget ) ) |
|
return true; |
|
|
|
// Is target (bot) disabled via a sapper? |
|
if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && pTarget->GetTeamNumber() == TF_TEAM_PVE_INVADERS ) |
|
{ |
|
if ( pTarget->m_Shared.InCond( TF_COND_MVM_BOT_STUN_RADIOWAVE ) ) |
|
return true; |
|
|
|
if ( pTarget->m_Shared.InCond( TF_COND_SAPPED ) && !pTarget->IsMiniBoss() ) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Determine if we are reasonably facing our target. |
|
//----------------------------------------------------------------------------- |
|
bool CTFKnife::IsBehindAndFacingTarget( CTFPlayer *pTarget ) |
|
{ |
|
CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() ); |
|
if ( !pOwner ) |
|
return false; |
|
|
|
// Get a vector from owner origin to target origin |
|
Vector vecToTarget; |
|
vecToTarget = pTarget->WorldSpaceCenter() - pOwner->WorldSpaceCenter(); |
|
vecToTarget.z = 0.0f; |
|
vecToTarget.NormalizeInPlace(); |
|
|
|
// Get owner forward view vector |
|
Vector vecOwnerForward; |
|
AngleVectors( pOwner->EyeAngles(), &vecOwnerForward, NULL, NULL ); |
|
vecOwnerForward.z = 0.0f; |
|
vecOwnerForward.NormalizeInPlace(); |
|
|
|
// Get target forward view vector |
|
Vector vecTargetForward; |
|
AngleVectors( pTarget->EyeAngles(), &vecTargetForward, NULL, NULL ); |
|
vecTargetForward.z = 0.0f; |
|
vecTargetForward.NormalizeInPlace(); |
|
|
|
// Make sure owner is behind, facing and aiming at target's back |
|
float flPosVsTargetViewDot = DotProduct( vecToTarget, vecTargetForward ); // Behind? |
|
float flPosVsOwnerViewDot = DotProduct( vecToTarget, vecOwnerForward ); // Facing? |
|
float flViewAnglesDot = DotProduct( vecTargetForward, vecOwnerForward ); // Facestab? |
|
|
|
// Debug |
|
// NDebugOverlay::HorzArrow( pTarget->WorldSpaceCenter(), pTarget->WorldSpaceCenter() + 50.0f * vecTargetForward, 5.0f, 0, 255, 0, 255, true, NDEBUG_PERSIST_TILL_NEXT_SERVER ); |
|
// NDebugOverlay::HorzArrow( pOwner->WorldSpaceCenter(), pOwner->WorldSpaceCenter() + 50.0f * vecOwnerForward, 5.0f, 0, 255, 0, 255, true, NDEBUG_PERSIST_TILL_NEXT_SERVER ); |
|
// DevMsg( "PosDot: %3.2f FacingDot: %3.2f AnglesDot: %3.2f\n", flPosVsTargetViewDot, flPosVsOwnerViewDot, flViewAnglesDot ); |
|
|
|
return ( flPosVsTargetViewDot > 0.f && flPosVsOwnerViewDot > 0.5 && flViewAnglesDot > -0.3f ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFKnife::CalcIsAttackCriticalHelper( void ) |
|
{ |
|
// Always crit from behind, never from front |
|
return IsBackstab(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFKnife::CalcIsAttackCriticalHelperNoCrits( void ) |
|
{ |
|
// Always crit from behind, never from front |
|
return IsBackstab(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Allow melee weapons to send different anim events |
|
// Input : - |
|
//----------------------------------------------------------------------------- |
|
void CTFKnife::SendPlayerAnimEvent( CTFPlayer *pPlayer ) |
|
{ |
|
if ( IsBackstab() ) |
|
{ |
|
pPlayer->DoAnimationEvent( PLAYERANIMEVENT_CUSTOM_GESTURE, ACT_MP_ATTACK_STAND_SECONDARYFIRE ); |
|
} |
|
else |
|
{ |
|
pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFKnife::CanDeploy( void ) |
|
{ |
|
m_bKnifeExists = ( gpGlobals->curtime - m_flKnifeMeltTimestamp > m_flKnifeRegenerateDuration ); |
|
|
|
if ( m_bKnifeExists == false ) |
|
{ |
|
// melted icicle has not yet regenerated |
|
return false; |
|
} |
|
|
|
return BaseClass::CanDeploy(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFKnife::Deploy( void ) |
|
{ |
|
bool bDeployed = BaseClass::Deploy(); |
|
|
|
m_bReadyToBackstab = false; |
|
|
|
m_bKnifeExists = true; |
|
|
|
return bDeployed; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFKnife::ItemPreFrame( void ) |
|
{ |
|
BaseClass::ItemPreFrame(); |
|
ProcessDisguiseImpulse(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFKnife::ItemPostFrame( void ) |
|
{ |
|
CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); |
|
if ( pPlayer ) |
|
{ |
|
if ( pPlayer->m_Shared.InCond( TF_COND_TAUNTING ) ) |
|
{ |
|
m_bWasTaunting = true; |
|
} |
|
else if ( m_bWasTaunting ) |
|
{ |
|
// we were taunting and now we're not |
|
m_bWasTaunting = false; |
|
|
|
// force switch away from knife if we can't deploy it right now |
|
if ( !CanDeploy() ) |
|
{ |
|
// force switch to sapper |
|
CBaseCombatWeapon *mySapper = pPlayer->Weapon_GetWeaponByType( TF_WPN_TYPE_BUILDING ); |
|
if ( !mySapper ) |
|
{ |
|
// this should never happen |
|
pPlayer->SwitchToNextBestWeapon( this ); |
|
} |
|
else |
|
{ |
|
pPlayer->Weapon_Switch( mySapper ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
BackstabVMThink(); |
|
BaseClass::ItemPostFrame(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFKnife::ItemBusyFrame( void ) |
|
{ |
|
BaseClass::ItemBusyFrame(); |
|
ProcessDisguiseImpulse(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFKnife::ItemHolsterFrame( void ) |
|
{ |
|
BaseClass::ItemHolsterFrame(); |
|
ProcessDisguiseImpulse(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Special case handling of 'Your Eternal Reward' input polling |
|
//----------------------------------------------------------------------------- |
|
void CTFKnife::ProcessDisguiseImpulse( void ) |
|
{ |
|
CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); |
|
if ( !pPlayer ) |
|
return; |
|
|
|
// Only care about this with the right weapon |
|
if ( GetKnifeType() != KNIFE_DISGUISE_ONKILL ) |
|
return; |
|
|
|
// If we're not already disguised, ignore |
|
if ( !pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) ) |
|
return; |
|
|
|
pPlayer->m_Shared.ProcessDisguiseImpulse( pPlayer ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFKnife::BackstabVMThink( void ) |
|
{ |
|
CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); |
|
if ( !pPlayer ) |
|
return; |
|
|
|
if ( pPlayer->GetActiveWeapon() != this ) |
|
return; |
|
|
|
// Don't do this if we are doing something other than idling. |
|
// int iIdealActivity = GetIdealActivity(); |
|
// if ( (iIdealActivity != ACT_VM_IDLE) && (iIdealActivity != ACT_BACKSTAB_VM_IDLE) ) |
|
// return; |
|
int iActivity = GetActivity(); |
|
if ( (iActivity != ACT_VM_IDLE) && (iActivity != ACT_BACKSTAB_VM_IDLE) && (iActivity != ACT_MELEE_VM_IDLE) && |
|
(iActivity != ACT_ITEM1_VM_IDLE) && (iActivity != ACT_ITEM1_BACKSTAB_VM_IDLE) && |
|
(iActivity != ACT_ITEM2_VM_IDLE) && (iActivity != ACT_ITEM2_BACKSTAB_VM_IDLE) ) |
|
return; |
|
|
|
|
|
// Are we in backstab range and not cloaked? |
|
trace_t trace; |
|
if ( DoSwingTrace( trace ) == true && CanAttack() ) |
|
{ |
|
// We will hit something if we attack. |
|
if( trace.m_pEnt && trace.m_pEnt->IsPlayer() ) |
|
{ |
|
CTFPlayer *pTarget = ToTFPlayer( trace.m_pEnt ); |
|
|
|
if ( pTarget && pTarget->GetTeamNumber() != pPlayer->GetTeamNumber() ) |
|
{ |
|
if ( CanPerformBackstabAgainstTarget( pTarget ) ) |
|
{ |
|
if ( !m_bReadyToBackstab ) |
|
{ |
|
SendWeaponAnim( ACT_BACKSTAB_VM_UP ); |
|
|
|
m_bReadyToBackstab = true; |
|
} |
|
} |
|
else if ( m_bReadyToBackstab ) |
|
{ |
|
|
|
SendWeaponAnim( ACT_BACKSTAB_VM_DOWN ); |
|
|
|
m_bReadyToBackstab = false; |
|
} |
|
} |
|
} |
|
} |
|
else if ( m_bReadyToBackstab ) |
|
{ |
|
SendWeaponAnim( ACT_BACKSTAB_VM_DOWN ); |
|
m_bReadyToBackstab = false; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Play animation appropriate to ball status. |
|
//----------------------------------------------------------------------------- |
|
bool CTFKnife::SendWeaponAnim( int iActivity ) |
|
{ |
|
CTFPlayer *pPlayer = GetTFPlayerOwner(); |
|
if ( !pPlayer ) |
|
return BaseClass::SendWeaponAnim( iActivity ); |
|
|
|
if ( m_bReadyToBackstab ) |
|
{ |
|
switch ( iActivity ) |
|
{ |
|
case ACT_VM_IDLE: |
|
case ACT_ITEM1_VM_IDLE: |
|
case ACT_ITEM2_VM_IDLE: |
|
iActivity = ACT_BACKSTAB_VM_IDLE; |
|
break; |
|
} |
|
} |
|
|
|
return BaseClass::SendWeaponAnim( iActivity ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: The spy's backstab was blocked by a player item. |
|
//----------------------------------------------------------------------------- |
|
void CTFKnife::BackstabBlocked( void ) |
|
{ |
|
CTFPlayer *pPlayer = GetTFPlayerOwner(); |
|
if ( !pPlayer ) |
|
return; |
|
|
|
// Delay the spy's next attack for a time. |
|
pPlayer->SetNextAttack( gpGlobals->curtime + 2.f ); |
|
m_flBlockedTime = gpGlobals->curtime; |
|
|
|
SendWeaponAnim( ACT_MELEE_VM_STUN ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Spy can't change weapons if knife is hot. |
|
//----------------------------------------------------------------------------- |
|
bool CTFKnife::CanHolster( void ) const |
|
{ |
|
if ( !m_bAllowHolsterBecauseForced && gpGlobals->curtime - m_flBlockedTime < 2.f ) |
|
return false; |
|
else |
|
return BaseClass::CanHolster(); |
|
} |