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.
617 lines
17 KiB
617 lines
17 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//============================================================================= |
|
|
|
#include "cbase.h" |
|
#include "tf_weapon_sword.h" |
|
|
|
// Client specific. |
|
#ifdef CLIENT_DLL |
|
#include "c_tf_player.h" |
|
#include "econ_entity.h" |
|
// Server specific. |
|
#else |
|
#include "tf_player.h" |
|
#endif |
|
|
|
//============================================================================= |
|
// |
|
// Weapon Sword tables. |
|
// |
|
|
|
// CTFSword |
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFSword, DT_TFWeaponSword ) |
|
|
|
BEGIN_NETWORK_TABLE( CTFSword, DT_TFWeaponSword ) |
|
END_NETWORK_TABLE() |
|
|
|
BEGIN_PREDICTION_DATA( CTFSword ) |
|
END_PREDICTION_DATA() |
|
|
|
LINK_ENTITY_TO_CLASS( tf_weapon_sword, CTFSword ); |
|
PRECACHE_WEAPON_REGISTER( tf_weapon_sword ); |
|
|
|
// CTFKatana |
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFKatana, DT_TFWeaponKatana ) |
|
|
|
BEGIN_NETWORK_TABLE( CTFKatana, DT_TFWeaponKatana ) |
|
#ifdef CLIENT_DLL |
|
RecvPropBool( RECVINFO( m_bIsBloody ) ), |
|
#else |
|
SendPropBool( SENDINFO( m_bIsBloody ) ), |
|
#endif |
|
END_NETWORK_TABLE() |
|
|
|
BEGIN_PREDICTION_DATA( CTFKatana ) |
|
END_PREDICTION_DATA() |
|
|
|
LINK_ENTITY_TO_CLASS( tf_weapon_katana, CTFKatana ); |
|
PRECACHE_WEAPON_REGISTER( tf_weapon_katana ); |
|
|
|
//============================================================================= |
|
// |
|
// Decapitation melee weapon base implementation. |
|
// |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFDecapitationMeleeWeaponBase::CTFDecapitationMeleeWeaponBase() |
|
: m_bHolstering( false ) |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFDecapitationMeleeWeaponBase::Precache() |
|
{ |
|
BaseClass::Precache(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
float CTFDecapitationMeleeWeaponBase::GetMeleeDamage( CBaseEntity *pTarget, int* piDamageType, int* piCustomDamage ) |
|
{ |
|
float flBaseDamage = BaseClass::GetMeleeDamage( pTarget, piDamageType, piCustomDamage ); |
|
|
|
*piCustomDamage = TF_DMG_CUSTOM_DECAPITATION; |
|
|
|
return flBaseDamage; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
Activity CTFDecapitationMeleeWeaponBase::TranslateViewmodelHandActivityInternal( Activity actBase ) |
|
{ |
|
CTFPlayer *pPlayer = GetTFPlayerOwner(); |
|
if ( !pPlayer ) |
|
return BaseClass::TranslateViewmodelHandActivityInternal( actBase ); |
|
|
|
CEconItemView *pEconItemView = GetAttributeContainer()->GetItem(); |
|
if ( pEconItemView ) |
|
{ |
|
if ( pEconItemView->GetAnimationSlot() == TF_WPN_TYPE_MELEE_ALLCLASS ) |
|
return BaseClass::TranslateViewmodelHandActivityInternal( actBase ); |
|
} |
|
|
|
// Alright, so we have some decapitation weapons (katana) that can be used |
|
// by both the soldier and the demoman, but the classes play totally different |
|
// animations using the same weapon. |
|
// |
|
// This logic is also responsible for playing the correct animations on the |
|
// demo when he's using non-shared weapons like the Eyelanders. |
|
if ( pPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_DEMOMAN ) |
|
{ |
|
switch ( actBase ) |
|
{ |
|
case ACT_VM_DRAW: |
|
actBase = ACT_VM_DRAW_SPECIAL; |
|
break; |
|
case ACT_VM_HOLSTER: |
|
actBase = ACT_VM_HOLSTER_SPECIAL; |
|
break; |
|
case ACT_VM_IDLE: |
|
actBase = ACT_VM_IDLE_SPECIAL; |
|
break; |
|
case ACT_VM_PULLBACK: |
|
actBase = ACT_VM_PULLBACK_SPECIAL; |
|
break; |
|
case ACT_VM_PRIMARYATTACK: |
|
actBase = ACT_VM_PRIMARYATTACK_SPECIAL; |
|
break; |
|
case ACT_VM_SECONDARYATTACK: |
|
actBase = ACT_VM_PRIMARYATTACK_SPECIAL; |
|
break; |
|
case ACT_VM_HITCENTER: |
|
actBase = ACT_VM_HITCENTER_SPECIAL; |
|
break; |
|
case ACT_VM_SWINGHARD: |
|
actBase = ACT_VM_SWINGHARD_SPECIAL; |
|
break; |
|
case ACT_VM_IDLE_TO_LOWERED: |
|
actBase = ACT_VM_IDLE_TO_LOWERED_SPECIAL; |
|
break; |
|
case ACT_VM_IDLE_LOWERED: |
|
actBase = ACT_VM_IDLE_LOWERED_SPECIAL; |
|
break; |
|
case ACT_VM_LOWERED_TO_IDLE: |
|
actBase = ACT_VM_LOWERED_TO_IDLE_SPECIAL; |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
return BaseClass::TranslateViewmodelHandActivityInternal( actBase ); |
|
} |
|
/* |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFDecapitationMeleeWeaponBase::SendWeaponAnim( int iActivity ) |
|
{ |
|
|
|
}*/ |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFDecapitationMeleeWeaponBase::CanDecapitate( void ) |
|
{ |
|
CEconItemView *pScriptItem = GetAttributeContainer()->GetItem(); |
|
if ( !pScriptItem ) |
|
return false; |
|
|
|
CEconItemDefinition *pStaticData = pScriptItem->GetStaticData(); |
|
if ( !pStaticData ) |
|
return false; |
|
|
|
int iDecapitateType = 0; |
|
CALL_ATTRIB_HOOK_INT( iDecapitateType, decapitate_type ); |
|
return iDecapitateType != 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFDecapitationMeleeWeaponBase::SetupGameEventListeners( void ) |
|
{ |
|
ListenForGameEvent( "player_death" ); |
|
} |
|
|
|
#ifdef CLIENT_DLL |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFDecapitationMeleeWeaponBase::UpdateAttachmentModels( void ) |
|
{ |
|
BaseClass::UpdateAttachmentModels(); |
|
|
|
CTFPlayer *pTFPlayer = GetTFPlayerOwner(); |
|
if ( !pTFPlayer ) |
|
return; |
|
|
|
if ( !pTFPlayer->IsLocalPlayer() ) |
|
return; |
|
|
|
if ( !pTFPlayer->GetViewModel() ) |
|
return; |
|
|
|
if ( !pTFPlayer->m_Shared.IsShieldEquipped() ) |
|
return; |
|
|
|
CTFWearableDemoShield* pMyShield = dynamic_cast<CTFWearableDemoShield*>( m_hShield.Get() ); |
|
|
|
if ( pMyShield ) |
|
{ |
|
pMyShield->UpdateAttachmentModels(); |
|
} |
|
else |
|
{ |
|
// Find a shield wearable... |
|
for ( int i=0; i<pTFPlayer->GetNumWearables(); ++i ) |
|
{ |
|
CEconWearable* pItem = pTFPlayer->GetWearable(i); |
|
if ( !pItem ) |
|
continue; |
|
|
|
pMyShield = dynamic_cast<CTFWearableDemoShield*>( pItem ); |
|
if ( !pMyShield ) |
|
continue; |
|
|
|
m_hShield.Set( pMyShield ); |
|
} |
|
|
|
if ( pMyShield ) |
|
{ |
|
pMyShield->UpdateAttachmentModels(); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CTFDecapitationMeleeWeaponBase::DrawOverriddenViewmodel( C_BaseViewModel *pViewmodel, int flags ) |
|
{ |
|
int iRes = BaseClass::DrawOverriddenViewmodel( pViewmodel, flags ); |
|
|
|
CTFWearableDemoShield* pMyShield = dynamic_cast<CTFWearableDemoShield*>( m_hShield.Get() ); |
|
|
|
if ( pMyShield ) |
|
{ |
|
pMyShield->DrawOverriddenViewmodel( pViewmodel, flags ); |
|
} |
|
|
|
return iRes; |
|
} |
|
#endif // CLIENT_DLL |
|
|
|
//============================================================================= |
|
// |
|
// Weapon Sword functions. |
|
// |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFSword::WeaponReset( void ) |
|
{ |
|
BaseClass::WeaponReset(); |
|
|
|
#ifdef CLIENT_DLL |
|
m_flNextIdleWavRoll = 0; |
|
m_iPrevWavDecap = 0; |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CTFSword::GetSwingRange( void ) |
|
{ |
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); |
|
if ( pOwner && pOwner->m_Shared.InCond( TF_COND_SHIELD_CHARGE ) ) |
|
{ |
|
return 128; |
|
} |
|
else |
|
{ |
|
//int iRange = 0; |
|
//CALL_ATTRIB_HOOK_INT( iRange, is_a_sword ) |
|
//return 72; |
|
return 72; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: A speed mod that is applied even if the weapon isn't in hand. |
|
//----------------------------------------------------------------------------- |
|
float CTFSword::GetSwordSpeedMod( void ) |
|
{ |
|
if ( m_bHolstering ) |
|
return 1.f; |
|
|
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); |
|
if ( !pOwner ) |
|
return 0; |
|
|
|
if ( !CanDecapitate() ) |
|
return 1.f; |
|
|
|
int iDecaps = MIN( MAX_DECAPITATIONS, pOwner->m_Shared.GetDecapitations() ); |
|
return 1.f + (iDecaps * 0.08f); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CTFSword::GetSwordHealthMod( void ) |
|
{ |
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); |
|
if ( !pOwner ) |
|
return 0; |
|
|
|
if ( !CanDecapitate() ) |
|
return 0.f; |
|
|
|
int iDecaps = MIN( MAX_DECAPITATIONS, pOwner->m_Shared.GetDecapitations() ); |
|
return (iDecaps * 15); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFSword::OnDecapitation( CTFPlayer *pDeadPlayer ) |
|
{ |
|
BaseClass::OnDecapitation( pDeadPlayer ); |
|
|
|
#ifdef GAME_DLL |
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); |
|
Assert( pOwner ); |
|
|
|
// The pyro's scythe is actually a "sword" as far as the gameplay code is concerned, |
|
// but we don't want the demo's head-collecting silliness to apply. |
|
if ( pOwner->GetPlayerClass()->GetClassIndex() == TF_CLASS_DEMOMAN ) |
|
{ |
|
// We have chopped someone's bloody 'ead off! |
|
int iDecap = pOwner->m_Shared.GetDecapitations(); |
|
|
|
// transfer decapitations |
|
if ( pDeadPlayer ) |
|
{ |
|
iDecap += pDeadPlayer->m_Shared.GetDecapitations(); |
|
} |
|
pOwner->m_Shared.SetDecapitations( ++iDecap ); |
|
pOwner->TeamFortress_SetSpeed(); |
|
if ( pOwner->m_Shared.GetBestOverhealDecayMult() == -1.f ) |
|
{ |
|
pOwner->m_Shared.SetBestOverhealDecayMult( 0.25f ); |
|
} |
|
if ( pOwner->GetHealth() < pOwner->m_Shared.GetMaxBuffedHealth() ) |
|
{ |
|
pOwner->TakeHealth( 15, DMG_IGNORE_MAXHEALTH ); |
|
} |
|
if ( !pOwner->m_Shared.InCond( TF_COND_DEMO_BUFF ) ) |
|
{ |
|
pOwner->m_Shared.AddCond( TF_COND_DEMO_BUFF ); |
|
} |
|
} |
|
#endif // GAME_DLL |
|
} |
|
|
|
#ifdef GAME_DLL |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFDecapitationMeleeWeaponBase::Holster( CBaseCombatWeapon *pSwitchingTo ) |
|
{ |
|
m_bHolstering = true; |
|
bool res = BaseClass::Holster( pSwitchingTo ); |
|
m_bHolstering = false; |
|
if ( res ) |
|
{ |
|
StopListeningForAllEvents(); |
|
} |
|
return res; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFDecapitationMeleeWeaponBase::FireGameEvent( IGameEvent *event ) |
|
{ |
|
const char *pszEventName = event->GetName(); |
|
if ( Q_strcmp( pszEventName, "player_death" ) != 0 ) |
|
return; |
|
|
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); |
|
if ( !pOwner ) |
|
return; |
|
|
|
int iAttackerID = event->GetInt( "attacker" ); |
|
if ( iAttackerID != pOwner->GetUserID() ) |
|
return; |
|
|
|
int iWeaponID = event->GetInt( "weaponid" ); |
|
int iCustomKill = event->GetInt( "customkill" ); |
|
if ( iWeaponID != TF_WEAPON_SWORD && iCustomKill != TF_DMG_CUSTOM_TAUNTATK_BARBARIAN_SWING ) |
|
return; |
|
|
|
// Off with their heads! |
|
if ( !CanDecapitate() ) |
|
return; |
|
|
|
OnDecapitation( ToTFPlayer( UTIL_PlayerByUserId( event->GetInt( "userid" ) ) ) ); |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFSword::Deploy( void ) |
|
{ |
|
bool res = BaseClass::Deploy(); |
|
|
|
if ( !CanDecapitate() ) |
|
{ |
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); |
|
if ( !pOwner ) |
|
return res; |
|
|
|
pOwner->m_Shared.SetDecapitations( 0 ); |
|
|
|
return res; |
|
} |
|
|
|
#ifdef GAME_DLL |
|
if ( res ) |
|
{ |
|
SetupGameEventListeners(); |
|
} |
|
#else |
|
// When we go active, there's a chance we immediately thirst for heads. |
|
if ( RandomInt( 1,4 ) == 1 ) |
|
{ |
|
m_flNextIdleWavRoll = gpGlobals->curtime + 3.0; |
|
} |
|
else |
|
{ |
|
m_flNextIdleWavRoll = gpGlobals->curtime + RandomFloat( 5.0, 30.0 ); |
|
} |
|
#endif |
|
|
|
return res; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CTFSword::GetCount( void ) |
|
{ |
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); |
|
if ( !pOwner ) |
|
return 0; |
|
|
|
if ( !CanDecapitate() ) |
|
return 0; |
|
|
|
return pOwner->m_Shared.GetDecapitations(); |
|
} |
|
|
|
#ifdef CLIENT_DLL |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFSword::WeaponIdle( void ) |
|
{ |
|
BaseClass::WeaponIdle(); |
|
|
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); |
|
if ( !pOwner ) |
|
return; |
|
|
|
if ( !CanDecapitate() ) |
|
return; |
|
|
|
// If we've decapped someone recently, we roll shortly afterwards |
|
int iDecaps = pOwner->m_Shared.GetDecapitations(); |
|
if ( m_iPrevWavDecap < iDecaps ) |
|
{ |
|
m_flNextIdleWavRoll = MIN( m_flNextIdleWavRoll, gpGlobals->curtime + RandomFloat(3,5) ); |
|
} |
|
m_iPrevWavDecap = iDecaps; |
|
|
|
// Randomly play sounds to the local player. The more decaps we have, the more chance it's a good wav. |
|
if ( m_flNextIdleWavRoll < gpGlobals->curtime ) |
|
{ |
|
float flChance = RemapValClamped( iDecaps, 0, 10, 0.25, 0.9 ); |
|
if ( RandomFloat() <= flChance ) |
|
{ |
|
// Chance to get the more powerful wav: |
|
float flChanceForGoodWav = RemapValClamped( iDecaps, 0, 10, 0.1, 0.75 ); |
|
if ( RandomFloat() <= flChanceForGoodWav ) |
|
{ |
|
WeaponSound( SPECIAL2 ); |
|
} |
|
else |
|
{ |
|
WeaponSound( SPECIAL1 ); |
|
} |
|
} |
|
|
|
m_flNextIdleWavRoll = gpGlobals->curtime + RandomFloat( 30.0, 60.0 ); |
|
} |
|
} |
|
|
|
#endif |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFKatana::CTFKatana() |
|
{ |
|
m_bIsBloody = false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFKatana::Deploy( void ) |
|
{ |
|
bool res = BaseClass::Deploy(); |
|
|
|
m_bIsBloody = false; |
|
|
|
if ( CanDecapitate() && res ) |
|
{ |
|
#ifdef GAME_DLL |
|
SetupGameEventListeners(); |
|
#endif |
|
} |
|
|
|
return res; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
float CTFKatana::GetMeleeDamage( CBaseEntity *pTarget, int* piDamageType, int* piCustomDamage ) |
|
{ |
|
// Start with our base damage. We use this to generate our custom damage flags, |
|
// if any. We may trash the damage amount. |
|
float fDamage = BaseClass::GetMeleeDamage( pTarget, piDamageType, piCustomDamage ); |
|
|
|
// The katana is a weapon of honor!!!! (Hitting someone wielding a katana with |
|
// your katana results in a massive damage boost, a one-hit kill.) |
|
if ( IsHonorBound() ) |
|
{ |
|
CTFPlayer *pTFPlayerTarget = ToTFPlayer( pTarget ); |
|
if ( pTFPlayerTarget ) |
|
{ |
|
// If our victim is wielding the weapon we're looking for, bump the damage way up. |
|
if ( pTFPlayerTarget->GetActiveTFWeapon() && pTFPlayerTarget->GetActiveTFWeapon()->IsHonorBound() ) |
|
{ |
|
fDamage = MAX( fDamage, pTFPlayerTarget->GetHealth() * 3 ); |
|
*piDamageType |= DMG_DONT_COUNT_DAMAGE_TOWARDS_CRIT_RATE; |
|
} |
|
} |
|
} |
|
|
|
return fDamage; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CTFKatana::GetActivityWeaponRole() const |
|
{ |
|
CTFPlayer *pPlayer = GetTFPlayerOwner(); |
|
if ( pPlayer && pPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_DEMOMAN ) |
|
{ |
|
// demo should use act table item1 |
|
return TF_WPN_TYPE_ITEM1; |
|
} |
|
|
|
return BaseClass::GetActivityWeaponRole(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFKatana::OnDecapitation( CTFPlayer *pDeadPlayer ) |
|
{ |
|
BaseClass::OnDecapitation( pDeadPlayer ); |
|
|
|
#ifndef CLIENT_DLL |
|
m_bIsBloody = true; |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CTFKatana::GetSkinOverride() const |
|
{ |
|
if ( m_bIsBloody && !UTIL_IsLowViolence() ) |
|
{ |
|
CTFPlayer *pPlayer = GetTFPlayerOwner(); |
|
if ( pPlayer ) |
|
{ |
|
return ( ( pPlayer->GetTeamNumber() == TF_TEAM_RED ) ? 2 : 3 ); |
|
} |
|
} |
|
|
|
return BaseClass::GetSkinOverride(); |
|
}
|
|
|