Team Fortress 2 Source Code as on 22/4/2020
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.
 
 
 
 
 
 

579 lines
15 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "cbase.h"
#include "tf_weapon_wrench.h"
#include "decals.h"
#include "baseobject_shared.h"
#include "tf_viewmodel.h"
// Client specific.
#ifdef CLIENT_DLL
#include "c_tf_player.h"
#include "in_buttons.h"
#include "tf_hud_menu_eureka_teleport.h"
// NVNT haptics system interface
#include "haptics/ihaptics.h"
// Server specific.
#else
#include "tf_player.h"
#include "variant_t.h"
#include "tf_gamerules.h"
#include "particle_parse.h"
#include "tf_fx.h"
#include "tf_obj_sentrygun.h"
#endif
// Maximum time between robo arm hits to maintain the three-hit-combo
#define ROBOARM_COMBO_TIMEOUT 1.0f
//=============================================================================
//
// Weapon Wrench tables.
//
IMPLEMENT_NETWORKCLASS_ALIASED( TFWrench, DT_TFWeaponWrench )
BEGIN_NETWORK_TABLE( CTFWrench, DT_TFWeaponWrench )
END_NETWORK_TABLE()
BEGIN_PREDICTION_DATA( CTFWrench )
END_PREDICTION_DATA()
LINK_ENTITY_TO_CLASS( tf_weapon_wrench, CTFWrench );
PRECACHE_WEAPON_REGISTER( tf_weapon_wrench );
//=============================================================================
//
// Robot Arm tables.
//
IMPLEMENT_NETWORKCLASS_ALIASED( TFRobotArm, DT_TFWeaponRobotArm )
BEGIN_NETWORK_TABLE( CTFRobotArm, DT_TFWeaponRobotArm )
#ifdef GAME_DLL
SendPropEHandle(SENDINFO(m_hRobotArm)),
#else
RecvPropEHandle(RECVINFO(m_hRobotArm)),
#endif
END_NETWORK_TABLE()
#ifdef CLIENT_DLL
BEGIN_PREDICTION_DATA( CTFRobotArm )
// DEFINE_PRED_FIELD( name, fieldtype, flags )
DEFINE_PRED_FIELD( m_iComboCount, FIELD_INTEGER, 0 ),
DEFINE_PRED_FIELD( m_flLastComboHit, FIELD_FLOAT, 0 ),
END_PREDICTION_DATA()
#endif
LINK_ENTITY_TO_CLASS( tf_weapon_robot_arm, CTFRobotArm );
PRECACHE_WEAPON_REGISTER( tf_weapon_robot_arm );
IMPLEMENT_NETWORKCLASS_ALIASED( TFWearableRobotArm, DT_TFWearableRobotArm )
BEGIN_NETWORK_TABLE( CTFWearableRobotArm, DT_TFWearableRobotArm )
END_NETWORK_TABLE()
LINK_ENTITY_TO_CLASS( tf_wearable_robot_arm, CTFWearableRobotArm );
//=============================================================================
//
// Weapon Wrench functions.
//
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFWrench::CTFWrench()
: m_bReloadDown( false )
{}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFWrench::Spawn()
{
BaseClass::Spawn();
}
#ifdef GAME_DLL
void CTFWrench::OnFriendlyBuildingHit( CBaseObject *pObject, CTFPlayer *pPlayer, Vector hitLoc )
{
bool bHelpTeammateBuildStructure = pObject->IsBuilding() && pObject->GetOwner() != GetOwner();
// Did this object hit do any work? repair or upgrade?
bool bUsefulHit = pObject->InputWrenchHit( pPlayer, this, hitLoc );
// award achievement if we helped a teammate build a structure
if ( bUsefulHit && bHelpTeammateBuildStructure )
{
CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
if ( pOwner && pOwner->IsPlayerClass( TF_CLASS_ENGINEER ) )
{
pOwner->AwardAchievement( ACHIEVEMENT_TF_ENGINEER_HELP_BUILD_STRUCTURE );
}
}
CDisablePredictionFiltering disabler;
if ( pObject->IsDisposableBuilding() )
{
CSingleUserRecipientFilter singleFilter( pPlayer );
EmitSound( singleFilter, pObject->entindex(), "Player.UseDeny" );
}
else
{
if ( bUsefulHit )
{
// play success sound
WeaponSound( SPECIAL1 );
}
else
{
// play failure sound
WeaponSound( SPECIAL2 );
}
}
}
#endif
void CTFWrench::Smack( void )
{
// see if we can hit an object with a higher range
// Get the current player.
CTFPlayer *pPlayer = GetTFPlayerOwner();
if ( !pPlayer )
return;
if ( !CanAttack() )
return;
// Setup a volume for the melee weapon to be swung - approx size, so all melee behave the same.
static Vector vecSwingMins( -18, -18, -18 );
static Vector vecSwingMaxs( 18, 18, 18 );
// Setup the swing range.
Vector vecForward;
AngleVectors( pPlayer->EyeAngles(), &vecForward );
Vector vecSwingStart = pPlayer->Weapon_ShootPosition();
Vector vecSwingEnd = vecSwingStart + vecForward * 70;
// only trace against objects
// See if we hit anything.
trace_t trace;
CTraceFilterIgnorePlayers traceFilter( NULL, COLLISION_GROUP_NONE );
UTIL_TraceLine( vecSwingStart, vecSwingEnd, MASK_SOLID, &traceFilter, &trace );
if ( trace.fraction >= 1.0 )
{
UTIL_TraceHull( vecSwingStart, vecSwingEnd, vecSwingMins, vecSwingMaxs, MASK_SOLID, &traceFilter, &trace );
}
// We hit, setup the smack.
if ( trace.fraction < 1.0f &&
trace.m_pEnt &&
trace.m_pEnt->IsBaseObject() &&
trace.m_pEnt->GetTeamNumber() == pPlayer->GetTeamNumber() )
{
#ifdef GAME_DLL
OnFriendlyBuildingHit( dynamic_cast< CBaseObject * >( trace.m_pEnt ), pPlayer, trace.endpos );
#else
// NVNT if the local player is the owner of this wrench
// Notify the haptics system we just repaired something.
if(pPlayer==C_TFPlayer::GetLocalTFPlayer() && haptics)
haptics->ProcessHapticEvent(2,"Weapons","tf_weapon_wrench_fix");
#endif
}
else
{
// if we cannot, Smack as usual for player hits
BaseClass::Smack();
}
}
#ifdef CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFWrench::ItemPostFrame()
{
BaseClass::ItemPostFrame();
if ( !CanAttack() )
{
return;
}
CTFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
if ( !pOwner )
{
return;
}
// Just pressed reload?
if ( pOwner->m_nButtons & IN_RELOAD && !m_bReloadDown )
{
m_bReloadDown = true;
int iAltFireTeleportToSpawn = 0;
CALL_ATTRIB_HOOK_INT( iAltFireTeleportToSpawn, alt_fire_teleport_to_spawn );
if ( iAltFireTeleportToSpawn )
{
// Tell the teleport menu to show
CHudEurekaEffectTeleportMenu *pTeleportMenu = ( CHudEurekaEffectTeleportMenu * )GET_HUDELEMENT( CHudEurekaEffectTeleportMenu );
if ( pTeleportMenu )
{
pTeleportMenu->WantsToTeleport();
}
}
}
else if ( !(pOwner->m_nButtons & IN_RELOAD) && m_bReloadDown )
{
m_bReloadDown = false;
}
}
#endif
//-----------------------------------------------------------------------------
// Purpose: Kill all buildings when wrench is changed.
//-----------------------------------------------------------------------------
#ifdef GAME_DLL
void CTFWrench::Equip( CBaseCombatCharacter *pOwner )
{
// STAGING_ENGY
CTFPlayer *pPlayer = ToTFPlayer( pOwner );
if ( pPlayer )
{
// if switching too gunslinger, blow up other sentry
int iMiniSentry = 0;
CALL_ATTRIB_HOOK_INT( iMiniSentry, wrench_builds_minisentry );
if ( iMiniSentry )
{
// Just detonate Sentries
CObjectSentrygun *pSentry = dynamic_cast<CObjectSentrygun*>( pPlayer->GetObjectOfType( OBJ_SENTRYGUN ) );
if ( pSentry )
{
pSentry->DetonateObject();
}
}
}
BaseClass::Equip( pOwner );
}
//-----------------------------------------------------------------------------
// Purpose: Kill all buildings when wrench is changed.
//-----------------------------------------------------------------------------
void CTFWrench::Detach( void )
{
// STAGING_ENGY
CTFPlayer *pPlayer = GetTFPlayerOwner();
if ( pPlayer )
{
bool bDetonateObjects = true;
// In MvM mode, leave engineer's buildings after he dies
if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
{
if ( pPlayer->GetTeamNumber() != TF_TEAM_PVE_DEFENDERS )
{
bDetonateObjects = false;
}
}
// Only detonate if we are unequipping gunslinger
if ( bDetonateObjects )
{
// if switching off of gunslinger detonate
int iMiniSentry = 0;
CALL_ATTRIB_HOOK_INT( iMiniSentry, wrench_builds_minisentry );
if ( iMiniSentry )
{
// Just detonate Sentries
CObjectSentrygun *pSentry = dynamic_cast<CObjectSentrygun*>( pPlayer->GetObjectOfType( OBJ_SENTRYGUN ) );
if ( pSentry )
{
pSentry->DetonateObject();
}
}
}
}
BaseClass::Detach();
}
//-----------------------------------------------------------------------------
// Purpose: Apply health upgrade to our existing buildings
//-----------------------------------------------------------------------------
void CTFWrench::ApplyBuildingHealthUpgrade( void )
{
CTFPlayer *pPlayer = GetTFPlayerOwner();
if ( !pPlayer )
return;
for ( int i = pPlayer->GetObjectCount()-1; i >= 0; i-- )
{
CBaseObject *pObj = pPlayer->GetObject(i);
if ( pObj )
{
pObj->ApplyHealthUpgrade();
}
}
}
#endif
// STAGING_ENGY
ConVar tf_construction_build_rate_multiplier( "tf_construction_build_rate_multiplier", "1.5f", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
float CTFWrench::GetConstructionValue( void )
{
float flValue = tf_construction_build_rate_multiplier.GetFloat();
CALL_ATTRIB_HOOK_FLOAT( flValue, mult_construction_value );
return flValue;
}
float CTFWrench::GetRepairValue( void )
{
float flValue = 1.0;
CALL_ATTRIB_HOOK_FLOAT( flValue, mult_repair_value );
#ifdef GAME_DLL
if ( GetOwner() )
{
CBaseCombatWeapon* pWpn = GetOwner()->Weapon_GetSlot( TF_WPN_TYPE_PRIMARY );
if ( pWpn )
{
CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pWpn, flValue, mult_repair_value );
}
}
#endif
return flValue;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFWrench::Holster( CBaseCombatWeapon *pSwitchingTo )
{
return BaseClass::Holster( pSwitchingTo );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFRobotArm::CTFRobotArm()
{
m_iComboCount = 0;
m_flLastComboHit = 0.f;
m_bBigIdle = false;
m_bBigHit = false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFRobotArm::Precache()
{
BaseClass::Precache();
extern const char *g_HACK_GunslingerEngineerArmsOverride;
PrecacheModel( g_HACK_GunslingerEngineerArmsOverride );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
#ifdef GAME_DLL
void CTFRobotArm::Equip( CBaseCombatCharacter* pOwner )
{
BaseClass::Equip( pOwner );
if ( !IsPDQ() )
return;
CTFWearable* pArmItem = dynamic_cast<CTFWearable*>( CreateEntityByName( "tf_wearable_robot_arm" ) );
if ( pArmItem )
{
pArmItem->AddSpawnFlags( SF_NORESPAWN );
pArmItem->SetAlwaysAllow( true );
DispatchSpawn( pArmItem );
pArmItem->GiveTo( pOwner );
pArmItem->AddHiddenBodyGroup( "rightarm" );
pArmItem->SetOwnerEntity( pOwner );
m_hRobotArm.Set( pArmItem );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFRobotArm::Drop( const Vector &vecVelocity )
{
RemoveRobotArm();
BaseClass::Drop( vecVelocity );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFRobotArm::UpdateOnRemove( void )
{
RemoveRobotArm();
BaseClass::UpdateOnRemove();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFRobotArm::RemoveRobotArm( void )
{
if ( m_hRobotArm )
{
m_hRobotArm->RemoveFrom( GetOwnerEntity() );
m_hRobotArm = NULL;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFRobotArm::OnActiveStateChanged( int iOldState )
{
if ( m_iState == WEAPON_NOT_CARRIED )
{
RemoveRobotArm();
}
}
#endif
// -----------------------------------------------------------------------------
// Purpose:
// -----------------------------------------------------------------------------
void CTFRobotArm::PrimaryAttack()
{
CTFPlayer *pPlayer = GetTFPlayerOwner();
if ( !pPlayer )
return;
if ( gpGlobals->curtime - m_flLastComboHit > ROBOARM_COMBO_TIMEOUT )
{
m_iComboCount = 0;
}
if ( m_iComboCount == 2 && CanAttack() )
{
pPlayer->m_Shared.SetNextMeleeCrit( MELEE_CRIT );
}
BaseClass::PrimaryAttack();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFRobotArm::Smack( void )
{
CTFPlayer *pPlayer = GetTFPlayerOwner();
if ( !pPlayer )
return;
trace_t trace;
bool btrace = DoSwingTrace( trace );
if ( btrace && trace.DidHitNonWorldEntity() && trace.m_pEnt && trace.m_pEnt->IsPlayer() &&
trace.m_pEnt->GetTeamNumber() != pPlayer->GetTeamNumber() )
{
m_iComboCount++;
m_flLastComboHit = gpGlobals->curtime;
if ( m_iComboCount == 3 )
{
m_iComboCount = 0;
m_bBigIdle = true;
m_bBigHit = true;
}
}
else
{
m_iComboCount = 0;
}
BaseClass::Smack();
m_bBigHit = false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFRobotArm::DoViewModelAnimation( void )
{
if ( m_iComboCount == 2 )
{
SendWeaponAnim( ACT_ITEM2_VM_SWINGHARD );
}
else
{
SendWeaponAnim( ACT_ITEM2_VM_HITCENTER );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
#ifdef GAME_DLL
int CTFRobotArm::GetDamageCustom()
{
if ( m_bBigHit )
{
return TF_DMG_CUSTOM_COMBO_PUNCH;
}
else
{
return BaseClass::GetDamageCustom();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float CTFRobotArm::GetForceScale( void )
{
if ( m_bBigHit )
{
return 500.f;
}
else
{
return BaseClass::GetForceScale();
}
}
#endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFRobotArm::WeaponIdle( void )
{
#ifdef GAME_DLL
if ( m_bBigIdle )
{
m_bBigIdle = false;
SendWeaponAnim( ACT_ITEM2_VM_IDLE_2 );
m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration();
return;
}
#endif
BaseClass::WeaponIdle();
}