diff --git a/src/game/client/tf/c_tf_player.cpp b/src/game/client/tf/c_tf_player.cpp index 0c26a824a7b..f8a6615addd 100644 --- a/src/game/client/tf/c_tf_player.cpp +++ b/src/game/client/tf/c_tf_player.cpp @@ -3756,6 +3756,7 @@ IMPLEMENT_CLIENTCLASS_DT( C_TFPlayer, DT_TFPlayer, CTFPlayer ) RecvPropInt( RECVINFO( m_iPlayerSkinOverride ) ), RecvPropBool( RECVINFO( m_bViewingCYOAPDA ) ), RecvPropBool( RECVINFO( m_bRegenerating ) ), + RecvPropEHandle( RECVINFO( m_hOffHandWeapon ) ), END_RECV_TABLE() diff --git a/src/game/server/tf/tf_player.cpp b/src/game/server/tf/tf_player.cpp index c0b4ddee19f..0df26ec5b61 100644 --- a/src/game/server/tf/tf_player.cpp +++ b/src/game/server/tf/tf_player.cpp @@ -843,6 +843,7 @@ IMPLEMENT_SERVERCLASS_ST( CTFPlayer, DT_TFPlayer ) SendPropInt( SENDINFO( m_iPlayerSkinOverride ) ), SendPropBool( SENDINFO( m_bViewingCYOAPDA ) ), SendPropBool( SENDINFO( m_bRegenerating ) ), + SendPropEHandle( SENDINFO( m_hOffHandWeapon ) ), END_SEND_TABLE() // -------------------------------------------------------------------------------- // diff --git a/src/game/shared/tf/tf_player_shared.cpp b/src/game/shared/tf/tf_player_shared.cpp index b11a9818471..360a44555ae 100644 --- a/src/game/shared/tf/tf_player_shared.cpp +++ b/src/game/shared/tf/tf_player_shared.cpp @@ -455,6 +455,8 @@ BEGIN_PREDICTION_DATA_NO_BASE( CTFPlayerShared ) DEFINE_PRED_FIELD( m_nAirDucked, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_flDuckTimer, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_flInvisChangeCompleteTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_flLastStealthExposeTime, FIELD_FLOAT, 0 ), + DEFINE_PRED_FIELD( m_flStealthNextChangeTime, FIELD_INTEGER, 0 ), DEFINE_PRED_FIELD( m_nDisguiseTeam, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_nDisguiseClass, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_nDisguiseSkinOverride, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), @@ -6932,19 +6934,23 @@ void CTFPlayerShared::OnRemoveTmpDamageBonus( void ) void CTFPlayerShared::OnAddStealthed( void ) { #ifdef CLIENT_DLL - if ( m_pOuter->GetPredictable() && ( !prediction->IsFirstTimePredicted() || m_bSyncingConditions ) ) - return; + // Local player wants to predict the offhand weapon, but not the fancy effects multiple times! + // Other players always run everything + bool bFirstPrediction = !m_pOuter->GetPredictable() || ( prediction->IsFirstTimePredicted() && !m_bSyncingConditions ); - if ( !InCond( TF_COND_FEIGN_DEATH ) ) + if ( bFirstPrediction ) { - m_pOuter->EmitSound( "Player.Spy_Cloak" ); - } - m_pOuter->RemoveAllDecals(); - UpdateCritBoostEffect(); + if ( !InCond( TF_COND_FEIGN_DEATH ) ) + { + m_pOuter->EmitSound( "Player.Spy_Cloak" ); + } + m_pOuter->RemoveAllDecals(); + UpdateCritBoostEffect(); - if ( m_pOuter->m_pTempShield && GetCarryingRuneType() == RUNE_RESIST ) - { - RemoveResistShield( &m_pOuter->m_pTempShield, m_pOuter ); + if ( m_pOuter->m_pTempShield && GetCarryingRuneType() == RUNE_RESIST ) + { + RemoveResistShield( &m_pOuter->m_pTempShield, m_pOuter ); + } } #endif @@ -6956,7 +6962,7 @@ void CTFPlayerShared::OnAddStealthed( void ) bSetInvisChangeTime = false; } - if ( InCond( TF_COND_STEALTHED_USER_BUFF ) && m_pOuter->IsLocalPlayer() ) + if ( bFirstPrediction && InCond( TF_COND_STEALTHED_USER_BUFF ) && m_pOuter->IsLocalPlayer() ) { IMaterial *pMaterial = materials->FindMaterial( TF_SCREEN_OVERLAY_MATERIAL_STEALTH, TEXTURE_GROUP_CLIENT_EFFECTS, false ); if ( !IsErrorMaterial( pMaterial ) ) @@ -7003,11 +7009,14 @@ void CTFPlayerShared::OnAddStealthed( void ) m_pOuter->TeamFortress_SetSpeed(); #ifdef CLIENT_DLL - // Remove water balloon effect if it on player - m_pOuter->ParticleProp()->StopParticlesNamed( "balloontoss_drip", true ); + if ( bFirstPrediction ) + { + // Remove water balloon effect if it on player + m_pOuter->ParticleProp()->StopParticlesNamed( "balloontoss_drip", true ); - m_pOuter->UpdateSpyStateChange(); - m_pOuter->UpdateKillStreakEffects( GetStreak( kTFStreak_Kills ) ); + m_pOuter->UpdateSpyStateChange(); + m_pOuter->UpdateKillStreakEffects( GetStreak( kTFStreak_Kills ) ); + } #endif #ifdef GAME_DLL @@ -7021,39 +7030,43 @@ void CTFPlayerShared::OnAddStealthed( void ) void CTFPlayerShared::OnRemoveStealthed( void ) { #ifdef CLIENT_DLL - if ( !m_bSyncingConditions ) - return; + // Local player wants to predict the offhand weapon, but not the fancy effects multiple times! + // Other players always run everything + bool bFirstPrediction = !m_pOuter->GetPredictable() || ( prediction->IsFirstTimePredicted() && !m_bSyncingConditions ); - CTFWeaponInvis *pWpn = (CTFWeaponInvis *) m_pOuter->Weapon_OwnsThisID( TF_WEAPON_INVIS ); - - int iReducedCloak = 0; - CALL_ATTRIB_HOOK_INT_ON_OTHER( m_pOuter, iReducedCloak, set_quiet_unstealth ); - if ( iReducedCloak == 1 ) + if ( bFirstPrediction ) { - m_pOuter->EmitSound( "Player.Spy_UnCloakReduced" ); - } - else if ( pWpn && pWpn->HasFeignDeath() ) - { - m_pOuter->EmitSound( "Player.Spy_UnCloakFeignDeath" ); - } - else - { - m_pOuter->EmitSound( "Player.Spy_UnCloak" ); - } - UpdateCritBoostEffect( kCritBoost_ForceRefresh ); + CTFWeaponInvis* pWpn = (CTFWeaponInvis*)m_pOuter->Weapon_OwnsThisID( TF_WEAPON_INVIS ); - if ( m_pOuter->IsLocalPlayer() && !InCond( TF_COND_STEALTHED_USER_BUFF_FADING ) ) - { - IMaterial *pMaterial = view->GetScreenOverlayMaterial(); - if ( pMaterial && FStrEq( pMaterial->GetName(), TF_SCREEN_OVERLAY_MATERIAL_STEALTH ) ) + int iReducedCloak = 0; + CALL_ATTRIB_HOOK_INT_ON_OTHER( m_pOuter, iReducedCloak, set_quiet_unstealth ); + if ( iReducedCloak == 1 ) { - view->SetScreenOverlayMaterial( NULL ); + m_pOuter->EmitSound( "Player.Spy_UnCloakReduced" ); } - } + else if ( pWpn && pWpn->HasFeignDeath() ) + { + m_pOuter->EmitSound( "Player.Spy_UnCloakFeignDeath" ); + } + else + { + m_pOuter->EmitSound( "Player.Spy_UnCloak" ); + } + UpdateCritBoostEffect( kCritBoost_ForceRefresh ); - if ( !m_pOuter->m_pTempShield && GetCarryingRuneType() == RUNE_RESIST ) - { - AddResistShield( &m_pOuter->m_pTempShield, m_pOuter, TF_COND_RUNE_RESIST ); + if ( m_pOuter->IsLocalPlayer() && !InCond( TF_COND_STEALTHED_USER_BUFF_FADING ) ) + { + IMaterial* pMaterial = view->GetScreenOverlayMaterial(); + if ( pMaterial && FStrEq( pMaterial->GetName(), TF_SCREEN_OVERLAY_MATERIAL_STEALTH ) ) + { + view->SetScreenOverlayMaterial( NULL ); + } + } + + if ( !m_pOuter->m_pTempShield && GetCarryingRuneType() == RUNE_RESIST ) + { + AddResistShield( &m_pOuter->m_pTempShield, m_pOuter, TF_COND_RUNE_RESIST ); + } } #else if ( m_flCloakStartTime > 0 ) @@ -7087,8 +7100,11 @@ void CTFPlayerShared::OnRemoveStealthed( void ) m_bMotionCloak = false; #ifdef CLIENT_DLL - m_pOuter->UpdateSpyStateChange(); - m_pOuter->UpdateKillStreakEffects( GetStreak( kTFStreak_Kills ) ); + if ( bFirstPrediction ) + { + m_pOuter->UpdateSpyStateChange(); + m_pOuter->UpdateKillStreakEffects( GetStreak( kTFStreak_Kills ) ); + } #endif } diff --git a/src/game/shared/tf/tf_weapon_invis.cpp b/src/game/shared/tf/tf_weapon_invis.cpp index 4663ea7cec6..3333739f48b 100644 --- a/src/game/shared/tf/tf_weapon_invis.cpp +++ b/src/game/shared/tf/tf_weapon_invis.cpp @@ -28,8 +28,11 @@ IMPLEMENT_NETWORKCLASS_ALIASED( TFWeaponInvis, DT_TFWeaponInvis ) BEGIN_NETWORK_TABLE( CTFWeaponInvis, DT_TFWeaponInvis ) END_NETWORK_TABLE() +#if defined( CLIENT_DLL ) BEGIN_PREDICTION_DATA( CTFWeaponInvis ) + DEFINE_PRED_FIELD( m_flHideTime, FIELD_FLOAT, 0 ) END_PREDICTION_DATA() +#endif LINK_ENTITY_TO_CLASS( tf_weapon_invis, CTFWeaponInvis ); PRECACHE_WEAPON_REGISTER( tf_weapon_invis ); @@ -69,7 +72,20 @@ void CTFWeaponInvis::OnActiveStateChanged( int iOldState ) //----------------------------------------------------------------------------- void CTFWeaponInvis::HideThink( void ) { - SetWeaponVisible( false ); + // This is now done in ItemPostFrame/ItemBusyFrame + // HideThink uses a context think, which is evaluated with predicted clock on client + // but relative to server (not the player's!) clock on the server + // SetWeaponVisible( false ); +} + +//----------------------------------------------------------------------------- +void CTFWeaponInvis::CheckHideTime( void ) +{ + if ( m_flHideTime && m_flHideTime < gpGlobals->curtime ) + { + SetWeaponVisible( false ); + m_flHideTime = 0; + } } //----------------------------------------------------------------------------- @@ -131,11 +147,15 @@ void CTFWeaponInvis::SetWeaponVisible( bool visible ) //----------------------------------------------------------------------------- bool CTFWeaponInvis::Deploy( void ) { - bool b = BaseClass::Deploy(); + bool bDeploy = BaseClass::Deploy(); - SetWeaponIdleTime( gpGlobals->curtime + 1.5 ); + if ( bDeploy ) + { + SetWeaponIdleTime( gpGlobals->curtime + 1.5 ); + m_flHideTime = 0; + } - return b; + return bDeploy; } //----------------------------------------------------------------------------- @@ -143,8 +163,12 @@ bool CTFWeaponInvis::Holster( CBaseCombatWeapon *pSwitchingTo ) { bool bHolster = BaseClass::Holster( pSwitchingTo ); - // far in the future - SetWeaponIdleTime( gpGlobals->curtime + 10 ); + if ( bHolster ) + { + // far in the future + SetWeaponIdleTime( gpGlobals->curtime + 10 ); + m_flHideTime = gpGlobals->curtime + SequenceDuration(); + } return bHolster; } @@ -161,10 +185,18 @@ void CTFWeaponInvis::SecondaryAttack( void ) // do nothing } +//----------------------------------------------------------------------------- +void CTFWeaponInvis::ItemPostFrame( void ) +{ + CheckHideTime(); + BaseClass::ItemPostFrame(); +} + //----------------------------------------------------------------------------- void CTFWeaponInvis::ItemBusyFrame( void ) { - // do nothing + CheckHideTime(); + // intentionally don't call base } //----------------------------------------------------------------------------- diff --git a/src/game/shared/tf/tf_weapon_invis.h b/src/game/shared/tf/tf_weapon_invis.h index 3791aafb6bb..23468ebe595 100644 --- a/src/game/shared/tf/tf_weapon_invis.h +++ b/src/game/shared/tf/tf_weapon_invis.h @@ -46,6 +46,7 @@ class CTFWeaponInvis : public CTFWeaponBase virtual bool Deploy( void ); virtual void HideThink( void ); + void CheckHideTime(); virtual bool Holster( CBaseCombatWeapon *pSwitchingTo ); virtual int GetWeaponID( void ) const { return TF_WEAPON_INVIS; } @@ -59,6 +60,7 @@ class CTFWeaponInvis : public CTFWeaponBase virtual void SetWeaponVisible( bool visible ); + virtual void ItemPostFrame( void ); virtual void ItemBusyFrame( void ); int GetInvisType( void ) { int iMode = 0; CALL_ATTRIB_HOOK_INT( iMode, set_weapon_mode ); return iMode; }; @@ -81,6 +83,7 @@ class CTFWeaponInvis : public CTFWeaponBase #endif private: + float m_flHideTime; CTFWeaponInvis( const CTFWeaponInvis & ) {} }; diff --git a/src/game/shared/tf/tf_weaponbase.cpp b/src/game/shared/tf/tf_weaponbase.cpp index 91741d7add0..147521f9d3c 100644 --- a/src/game/shared/tf/tf_weaponbase.cpp +++ b/src/game/shared/tf/tf_weaponbase.cpp @@ -231,6 +231,8 @@ END_NETWORK_TABLE() BEGIN_PREDICTION_DATA( CTFWeaponBase ) #ifdef CLIENT_DLL DEFINE_PRED_FIELD( m_bLowered, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_bInAttack, FIELD_BOOLEAN, 0 ), + DEFINE_PRED_FIELD( m_bInAttack2, FIELD_BOOLEAN, 0 ), DEFINE_PRED_FIELD( m_iReloadMode, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_bReloadedThroughAnimEvent, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_bDisguiseWeapon, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),