summaryrefslogtreecommitdiff
path: root/game/shared/tf/tf_weapon_wrench.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/shared/tf/tf_weapon_wrench.cpp')
-rw-r--r--game/shared/tf/tf_weapon_wrench.cpp579
1 files changed, 579 insertions, 0 deletions
diff --git a/game/shared/tf/tf_weapon_wrench.cpp b/game/shared/tf/tf_weapon_wrench.cpp
new file mode 100644
index 0000000..1156a71
--- /dev/null
+++ b/game/shared/tf/tf_weapon_wrench.cpp
@@ -0,0 +1,579 @@
+//========= 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();
+}