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