|
|
//========= 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(); }
|