//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Condition Objects // //============================================================================= #include "cbase.h" #include "tf_condition.h" #ifdef GAME_DLL #include "tf_player.h" #else #include "c_tf_player.h" #include "achievementmgr.h" #include "baseachievement.h" #include "achievements_tf.h" #endif #ifdef CLIENT_DLL BEGIN_PREDICTION_DATA_NO_BASE( CTFConditionList ) DEFINE_PRED_FIELD( _condition_bits, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), END_PREDICTION_DATA() BEGIN_RECV_TABLE_NOBASE( CTFConditionList, DT_TFPlayerConditionListExclusive ) RecvPropInt( RECVINFO( _condition_bits ) ), END_RECV_TABLE() #else BEGIN_SEND_TABLE_NOBASE( CTFConditionList, DT_TFPlayerConditionListExclusive ) SendPropInt( SENDINFO( _condition_bits ), MIN( TF_COND_LAST, 32 ), SPROP_UNSIGNED ), END_SEND_TABLE() #endif //----------------------------------------------------------------------------- // Ctor //----------------------------------------------------------------------------- CTFConditionList::CTFConditionList() { _condition_bits = _old_condition_bits = 0; } //----------------------------------------------------------------------------- // Condition factory. //----------------------------------------------------------------------------- bool CTFConditionList::Add( ETFCond type, float duration, CTFPlayer* outer, CBaseEntity* provider /*= NULL*/ ) { #ifdef GAME_DLL return _Add( type, duration, outer, provider ); #else return type == TF_COND_CRITBOOSTED; #endif } bool CTFConditionList::_Add( ETFCond type, float duration, CTFPlayer* outer, CBaseEntity* provider /*= NULL*/ ) { // If we already have a condition of this type, ask it to handle the addition of another. for ( int i = 0; i < _conditions.Count(); ++i ) { if ( _conditions[i]->GetType() == type ) { _conditions[i]->Add( duration ); _conditions[i]->SetProvider( provider ); return true; } } // Add a new condition. CTFCondition* newCond = NULL; switch ( type ) { // TODO: Register new conditions anonymously instead of switching. case TF_COND_CRITBOOSTED: newCond = new CTFCondition_CritBoost( type, duration, outer, provider ); break; } if ( newCond ) { _condition_bits |= (1<OnAdded(); newCond->SetProvider( provider ); return true; } return false; } //----------------------------------------------------------------------------- // Remove a condition from the player. //----------------------------------------------------------------------------- bool CTFConditionList::Remove( ETFCond type, bool ignore_duration ) { #ifdef GAME_DLL bool bConditionListHandledRemoval = _Remove( type, ignore_duration ); // The condition list only handles one type of condition. Written slightly weird // to avoid unused variable warnings with asserts disabled. if ( bConditionListHandledRemoval ) { Assert( type == TF_COND_CRITBOOSTED ); } #endif return type == TF_COND_CRITBOOSTED; } bool CTFConditionList::_Remove( ETFCond type, bool ignore_duration ) { for ( int i=_conditions.Count()-1; i>=0; --i ) { CTFCondition* cond = _conditions[i]; if ( !cond || cond->GetType() != type ) continue; if ( cond->UsesMinDuration() && !ignore_duration && cond->GetMinDuration() > 0 ) { cond->SetMaxDuration( cond->GetMinDuration() ); continue; // Can't remove conditions that haven't expired. } _conditions.Remove( i ); _condition_bits &= ~(1<OnRemoved(); delete cond; return true; } return false; } //----------------------------------------------------------------------------- // Clear all conditions from the player. //----------------------------------------------------------------------------- void CTFConditionList::RemoveAll() { _condition_bits = 0; _old_condition_bits = 0; for ( int i=0; i<_conditions.Count(); ++i ) { _conditions[i]->OnRemoved(); } _conditions.PurgeAndDeleteElements(); } //----------------------------------------------------------------------------- // Checks if we have at least one of a given condition applied. //----------------------------------------------------------------------------- bool CTFConditionList::InCond( ETFCond type ) const { return ( ( _condition_bits & (1<GetType() == type ) { pProvider = _conditions[i]->GetProvider(); break; } } return pProvider; } //----------------------------------------------------------------------------- // Client/Server periodic condition think. //----------------------------------------------------------------------------- void CTFConditionList::Think() { for ( int i=0; i<_conditions.Count(); ++i ) { _conditions[i]->OnThink(); } } //----------------------------------------------------------------------------- // Server only per-frame think. //----------------------------------------------------------------------------- void CTFConditionList::ServerThink() { #ifdef GAME_DLL for ( int i=0; i<_conditions.Count(); ++i ) { CTFCondition* cond = _conditions[i]; if ( cond->GetMaxDuration() > PERMANENT_CONDITION || cond->GetMinDuration() > PERMANENT_CONDITION ) { // Reduce the duration over time. float reduction = gpGlobals->frametime; // Healable conditions expire faster when we have healers. int numHealers = cond->GetOuter()->m_Shared.GetNumHealers(); if ( cond->IsHealable() && numHealers > 0 ) { reduction += numHealers * reduction * 4; } // Decrement min duration. if ( cond->GetMinDuration() > PERMANENT_CONDITION ) { cond->SetMinDuration( MAX( cond->GetMinDuration() - reduction, 0 ) ); } // Decrement max duration. if ( cond->GetMaxDuration() > PERMANENT_CONDITION ) { cond->SetMaxDuration( MAX( cond->GetMaxDuration() - reduction, 0 ) ); if ( cond->GetMaxDuration() < cond->GetMinDuration() ) { cond->SetMaxDuration( cond->GetMinDuration() ); } } if ( cond->GetMaxDuration() == 0 ) { Remove( cond->GetType() ); continue; } } _conditions[i]->OnServerThink(); } #endif } #ifdef CLIENT_DLL //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void CTFConditionList::OnPreDataChanged( void ) { // _old_condition_bits = _condition_bits; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void CTFConditionList::OnDataChanged( CTFPlayer* outer ) { // Is there a way to improve this by hooking directly into network state changed? if ( _old_condition_bits != _condition_bits ) { UpdateClientConditions( outer ); _old_condition_bits = _condition_bits; } } //----------------------------------------------------------------------------- // Creates or destroys conditions to make sure our state matches the server. //----------------------------------------------------------------------------- void CTFConditionList::UpdateClientConditions( CTFPlayer* outer ) { int nCondChanged = _condition_bits ^ _old_condition_bits; int nCondAdded = nCondChanged & _condition_bits; int nCondRemoved = nCondChanged & _old_condition_bits; int i; for ( i=0;i GetMinDuration() ) SetMinDuration( duration ); return; } } else if ( GetMaxDuration() != PERMANENT_CONDITION ) { // If our current duration is not permanent and we are adding a // permanent duration, make our old finite duration the new min duration. // This ensures we last at least that long. SetMinDuration( GetMaxDuration() ); } SetMaxDuration( duration ); } //============================================================================= // Crit Boost //============================================================================= CTFCondition_CritBoost::CTFCondition_CritBoost( ETFCond type, float duration, CTFPlayer* outer, CBaseEntity* provider /*= NULL*/ ) : CTFCondition( type, duration, outer, provider ) { Assert( type == TF_COND_CRITBOOSTED ); } void CTFCondition_CritBoost::OnAdded() { #ifdef CLIENT_DLL GetOuter()->m_Shared.UpdateCritBoostEffect(); if ( GetOuter()->IsLocalPlayer() && GetOuter()->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) ) { g_AchievementMgrTF.OnAchievementEvent( ACHIEVEMENT_TF_HEAVY_RECEIVE_UBER_GRIND ); } #endif } void CTFCondition_CritBoost::OnRemoved() { #ifdef CLIENT_DLL GetOuter()->m_Shared.UpdateCritBoostEffect(); #endif } void CTFCondition_CritBoost::OnThink() { #ifdef CLIENT_DLL if ( GetOuter()->m_pCritBoostEffect ) { CBaseEntity *pWeapon = NULL; // Use GetRenderedWeaponModel() instead? if ( GetOuter()->IsLocalPlayer() ) { pWeapon = GetOuter()->GetViewModel(0); } else { pWeapon = GetOuter()->GetActiveWeapon(); } // Transfer the crit boosted effect if we've switched weapons if ( GetOuter()->m_pCritBoostEffect->GetOwner() != pWeapon ) { GetOuter()->m_Shared.UpdateCritBoostEffect(); } } #endif } void CTFCondition_CritBoost::OnServerThink() { }