diff options
Diffstat (limited to 'game/shared/tf/tf_quest_restriction.cpp')
| -rw-r--r-- | game/shared/tf/tf_quest_restriction.cpp | 2700 |
1 files changed, 2700 insertions, 0 deletions
diff --git a/game/shared/tf/tf_quest_restriction.cpp b/game/shared/tf/tf_quest_restriction.cpp new file mode 100644 index 0000000..c6361cb --- /dev/null +++ b/game/shared/tf/tf_quest_restriction.cpp @@ -0,0 +1,2700 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "KeyValues.h" +#include "schemainitutils.h" + +#ifdef CLIENT_DLL +#include "c_tf_player.h" +#include <igameresources.h> +#include "tf_gc_client.h" +#else +#include "tf_player.h" +#include "iscorer.h" +#endif // #ifdef CLIENT_DLL + +#include "tf_quest_restriction.h" +#include "tf_gamerules.h" +#include "tf_item_schema.h" +#include "econ_item_system.h" +#include "tf_logic_robot_destruction.h" +#include "tier2/fileutils.h" +#include "steamworks_gamestats.h" +#include "tf_quickplay_shared.h" + +static const int s_nMinConnectedPlayersForQuestProgress = 2; +static const int s_nMaxInputCount = 100; +static const char *g_skQuestEventsFile = "tf/scripts/items/unencrypted/_quest_events.txt"; + +template<typename CTFQuestConditionSubClass_t> +CTFQuestCondition *CreateCTFQuestConditionSubClass() +{ + return new CTFQuestConditionSubClass_t(); +} +typedef CTFQuestCondition *(*pfnQuestCreate)(); + +#define FIELD_NONE 0 +#define FIELD_PLAYER 1<<0 +#define FIELD_OBJECT 1<<1 +#define FIELD_WEAPON_NAME 1<<2 +#define FIELD_SCORER 1<<3 +#define FIELD_CUSTOM_DAMAGE 1<<4 +#define FIELD_WEAPON_TYPE 1<<5 +#define FIELD_FLAG_EVENT 1<<6 +#define FIELD_TEAM 1<<7 +#define FIELD_LOADOUT_POSITION 1<<8 +#define FIELD_CONDITION 1<<9 +#define FIELD_CRIT 1<<10 +#define FIELD_WEAPON_DEF_INDEX 1<<11 +#define FIELD_HALLOWEEN_BOSS_TYPE 1<<12 +#define FIELD_HALLOWEEN_MINIGAME_TYPE 1<<13 +#define FIELD_WEAPON_CLASS 1<<14 +#define FIELD_BONUSEFFECT 1<<15 +#define FIELD_DEFLECTED_PROJECTILE 1<<16 +#define FIELD_LAST_FIELD FIELD_DEFLECTED_PROJECTILE + + +const char* k_pszQuestConditionRequiredFieldStrings[] = +{ + "player", // FIELD_PLAYER + "object_type", // FIELD_OBJECT + "weapon_name", // FIELD_WEAPON_NAME + "scorer", // FIELD_SCORER + "custom_damage", // FIELD_CUSTOM_DAMAGE + "weapon_type", // FIELD_WEAPON_TYPE + "flag_event", // FIELD_FLAG_EVENT + "team_restriction", // FIELD_TEAM + "loadout_position", // FIELD_LOADOUT_POSITION + "condition", // FIELD_CONDITION + "crit_kill", // FIELD_CRIT + "weapon_def_index", // FIELD_WEAPON_DEF_INDEX + "halloween_boss_type", // FIELD_HALLOWEEN_BOSS_TYPE + "minigame_type", // FIELD_HALLOWEEN_MINIGAME_TYPE + "weapon_class", // FIELD_WEAPON_CLASS + "bonuseffect", // FIELD_BONUSEFFECT + "deflected_projectile", // FIELD_DEFLECTED_PROJECTILE +}; + + +struct QuestConditionEntry_t; +CUtlMap< const char*, QuestConditionEntry_t* > k_mapConditions( StringLessThan ); +struct QuestConditionEntry_t +{ + QuestConditionEntry_t( const char* pszName, int nRequiredField, pfnQuestCreate pfnCreate ) + : m_nRequiredFields( nRequiredField ) + , m_pfnQuestCreate( pfnCreate ) + , m_pszFieldName( pszName ) + { + k_mapConditions.Insert( pszName, this ); + } + int m_nRequiredFields; + pfnQuestCreate m_pfnQuestCreate; + const char* m_pszFieldName; +}; + +#define REGISTER_QUEST_CONDITION_SUB_CLASS( derivedClass, condName, nCondReqFields ) QuestConditionEntry_t k_s##condName##RegisteredEntry( #condName, nCondReqFields, CreateCTFQuestConditionSubClass< derivedClass > ); + +bool IsValidServerForQuests( CSteamID steamIDQuestOwner ) +{ + // Check if we're on beta. If so, allow it. + if ( ( engine->GetAppID() == 810 || engine->GetAppID() == 440 ) + && ( steamIDQuestOwner.GetEUniverse() == k_EUniverseBeta || steamIDQuestOwner.GetEUniverse() == k_EUniverseDev ) ) + return true; + + // TODO Do we want to exclude quest progress after the match is over or during warm-up? We'd need another function + // and to check it in appropriate spots -- this guy returning false if the match is over gives the user + // "Invalid server" status on their quest display and so on. + + // We only allow for quests to be tracked on Valve servers -- check if we joined via MM. Don't care if the match is + // still running. + EMatchGroup eMatchGroup = TFGameRules()->GetCurrentMatchGroup(); + bool bTrustedMatch = ( eMatchGroup != k_nMatchGroup_Invalid ) && GetMatchGroupDescription( eMatchGroup )->BIsTrustedServersOnly(); + if ( !bTrustedMatch ) + return false; + + return true; +} + +void GetInvalidReasonsNames( const InvalidReasonsContainer_t& invalidReasons, CUtlVector< CUtlString >& vecStrings ) +{ + static const char* arReasons[] = + { + "#TF_QuestInvalid_WrongMap", // INVALID_QUEST_REASON_WRONG_MAP = 0, + "#TF_QuestInvalid_WrongClass", // INVALID_QUEST_REASON_WRONG_CLASS, + "#TF_QuestInvalid_GameMode", // INVALID_QUEST_REASON_WRONG_GAME_MODE, + "#TF_QuestInvalid_NotEnoughPlayers", // INVALID_QUEST_REASON_NOT_ENOUGH_PLAYERS, + "#TF_QuestInvalid_ValveServers", // INVALID_QUEST_REASON_VALVE_SERVERS_ONLY, + "#TF_QuestInvalid_MvM", // INVALID_QUEST_REASON_NO_MVM + }; + + for( int i=0; i < invalidReasons.m_bits.GetNumBits(); ++i ) + { + if ( invalidReasons.m_bits.IsBitSet( i ) ) + { + vecStrings.AddToTail( arReasons[i] ); + } + } +} + +KeyValues* GetQuestEventsKeyValues() +{ + static KeyValues *pQuestEvents = NULL; + if ( pQuestEvents == NULL ) + { + CUtlBuffer bufRawData; + bufRawData.SetBufferType( true, true ); + bool bReadFileOK = g_pFullFileSystem->ReadFile( g_skQuestEventsFile, NULL, bufRawData ); + if ( !bReadFileOK ) + { + AssertMsg1( false, "Couldn't load quest events file: %s!", g_skQuestEventsFile ); + return NULL; + } + + pQuestEvents = new KeyValues( "quest_events" ); + pQuestEvents->LoadFromBuffer( NULL, bufRawData ); + } + + return pQuestEvents; +} + +// getting key of params that are unique per event +void GetValidParamsKeyFromEvent( const char *pszKeyName, const char *pszRestrictionName, const char *pszEventName, KeyValues *pRequiredKeys ) +{ + KeyValues *pQuestEvents = GetQuestEventsKeyValues(); + + if ( pQuestEvents ) + { + KeyValues *pEvent = pQuestEvents->FindKey( pszEventName ); + AssertMsg1( pEvent, "Failed to find specified event name %s", pszEventName ); + if ( pEvent ) + { + KeyValues *pRestriction = pEvent->FindKey( pszRestrictionName ); + AssertMsg2( pRestriction, "Failed to find specified restriction name %s :: %s", pszEventName, pszRestrictionName ); + if ( pRestriction ) + { + KeyValues *pParamsKey = pRestriction->FindKey( pszKeyName ); + AssertMsg3( pParamsKey, "Failed to find specified param key name %s :: %s :: %s", pszEventName, pszRestrictionName, pszKeyName ); + if ( pParamsKey ) + { + if ( pParamsKey->GetString( "uses_method", NULL ) ) + { + KeyValues* pKVMethod = pParamsKey->FindKey( "method" ); + AssertMsg3( pKVMethod, "Failed to find method block in %s :: %s :: %s", pszEventName, pszRestrictionName, pszKeyName ); + if ( pKVMethod ) + { + const char* pszType = pKVMethod->GetString( "type", NULL ); + AssertMsg3( pszType, "Missing method type in %s :: %s :: %s", pszEventName, pszRestrictionName, pszKeyName ); + if ( FStrEq( pszType, "weapon_def_index" ) ) + { + KeyValues *pWeaponNames = new KeyValues( "value" ); + + const CTFItemDefinition* pItemDef = NULL; + const CEconItemSchema::SortedItemDefinitionMap_t& mapItems = GetItemSchema()->GetSortedItemDefinitionMap(); + FOR_EACH_MAP( mapItems, it ) + { + pItemDef = static_cast< const CTFItemDefinition* >( mapItems[it] ); + Assert( pItemDef->GetDefinitionIndex() != INVALID_ITEM_DEF_INDEX ); + if ( pItemDef->GetDefaultLoadoutSlot() == LOADOUT_POSITION_PRIMARY + || pItemDef->GetDefaultLoadoutSlot() == LOADOUT_POSITION_SECONDARY + || pItemDef->GetDefaultLoadoutSlot() == LOADOUT_POSITION_MELEE + || pItemDef->GetDefaultLoadoutSlot() == LOADOUT_POSITION_PDA + || pItemDef->GetDefaultLoadoutSlot() == LOADOUT_POSITION_PDA2 + || pItemDef->GetDefaultLoadoutSlot() == LOADOUT_POSITION_BUILDING ) + { + KeyValues *pNewWeapon = pWeaponNames->CreateNewKey(); + pNewWeapon->SetName( CFmtStr( "%d", pItemDef->GetDefinitionIndex() ) ); + pNewWeapon->SetString( "english_name", pItemDef->GetDefinitionName() ); + } + } + + pRequiredKeys->AddSubKey( pWeaponNames ); + } + else if ( FStrEq( pszType, "weapon_name" ) ) + { + KeyValues *pWeaponNames = new KeyValues( "value" ); + + // Add a few here that we magically use in the code elsewhere. Sigh... + pWeaponNames->AddSubKey( new KeyValues( "obj_attachment_sapper" ) ); + pWeaponNames->AddSubKey( new KeyValues( "building_carried_destroyed" ) ); + + const CEconItemDefinition* pItemDef = NULL; + const CEconItemSchema::SortedItemDefinitionMap_t& mapItems = GetItemSchema()->GetSortedItemDefinitionMap(); + for ( int it = mapItems.FirstInorder(); it != mapItems.InvalidIndex(); it = mapItems.NextInorder( it ) ) + { + pItemDef = mapItems[it]; + if ( pItemDef->GetIconClassname() ) + { + pWeaponNames->AddSubKey( new KeyValues( pItemDef->GetIconClassname() ) ); + } + } + + pRequiredKeys->AddSubKey( pWeaponNames ); + } + else if ( FStrEq( pszType, "weapon_type" ) ) + { + KeyValues *pWeaponType = new KeyValues( "value" ); + for ( int i=0; i<TF_WEAPON_COUNT; ++i ) + { + const char *pszWeaponName = WeaponIdToAlias( i ); + KeyValues *pNewWeapon = pWeaponType->CreateNewKey(); + pNewWeapon->SetName( CFmtStr( "%d", i ) ); + pNewWeapon->SetString( "english_name", pszWeaponName ); + } + + pRequiredKeys->AddSubKey( pWeaponType ); + } + else if ( FStrEq( pszType, "item_class" ) ) + { + KeyValues *pItemClassNames = new KeyValues( "value" ); + + const CEconItemDefinition* pItemDef = NULL; + const CEconItemSchema::SortedItemDefinitionMap_t& mapItems = GetItemSchema()->GetSortedItemDefinitionMap(); + CUtlMap< const char*, int > mapTypeNames( StringLessThan ); + for ( int it = mapItems.FirstInorder(); it != mapItems.InvalidIndex(); it = mapItems.NextInorder( it ) ) + { + pItemDef = mapItems[it]; + const char *pszClass = pItemDef->GetItemClass(); + if ( pszClass && mapTypeNames.Find( pszClass ) == mapTypeNames.InvalidIndex() ) + { + mapTypeNames.Insert( pszClass ); + pItemClassNames->AddSubKey( new KeyValues( pItemDef->GetItemClass() ) ); + } + } + + pRequiredKeys->AddSubKey( pItemClassNames ); + } + else + { + AssertMsg5( false, "Type %s in %s :: %s :: %s didnt match any types defined in %s", pszType, pszEventName, pszRestrictionName, pszKeyName, __FUNCTION__ ); + } + } + } + else + { + pRequiredKeys->AddSubKey( pParamsKey->MakeCopy() ); + } + } + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTFQuestCondition::CTFQuestCondition() + : m_pParent( NULL ) +{} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTFQuestCondition::~CTFQuestCondition() +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CTFQuestCondition::BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) +{ + // check if restriction name matches the keyvalue name + //const char *pszType = pKVItem->GetString( "type" ); + //SCHEMA_INIT_CHECK( FStrEq( pszType, GetConditionName() ), "%s", CFmtStr( "Invalid quest restriction name '%s' for '%s'", pszType, GetConditionName() ).Get() ) + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Print out debug text +//----------------------------------------------------------------------------- +void CTFQuestCondition::PrintDebugText() const +{ + DevMsg( "'%s %s'", GetConditionName(), GetValueString() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Get the owner player +//----------------------------------------------------------------------------- +const CTFPlayer *CTFQuestCondition::GetQuestOwner() const +{ + if ( m_pParent ) + return m_pParent->GetQuestOwner(); + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Global quest objective validation checks +//----------------------------------------------------------------------------- +bool CTFQuestCondition::IsValidForPlayer( const CTFPlayer *pOwner, InvalidReasonsContainer_t& invalidReasons ) const +{ + if ( pOwner ) + { + CSteamID steamIDOwner; + const_cast< CTFPlayer* >( pOwner )->GetSteamID( &steamIDOwner ); // Ugh + + // Can only do quests on Valve servers + if ( !IsValidServerForQuests( steamIDOwner ) ) + { + invalidReasons.m_bits.Set( INVALID_QUEST_REASON_VALVE_SERVERS_ONLY ); + } + } + + // Cannot do quests in MvM + if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() ) + { + invalidReasons.m_bits.Set( INVALID_QUEST_REASON_NO_MVM ); + } + + return true; +} + +void CTFQuestCondition::GetValidRestrictions( CUtlVector< const char* >& vecOutValidChildren ) const +{ + const CTFQuestCondition* pParent = this; + const CTFQuestEvaluator* pEvaluatorParent = NULL; + while( pParent && !pEvaluatorParent ) + { + pEvaluatorParent = dynamic_cast< const CTFQuestEvaluator* >( pParent ); + pParent = pParent->GetParent(); + } + + KeyValues* pKVQuestEvents = GetQuestEventsKeyValues(); + KeyValues* pKVEvent = pKVQuestEvents->FindKey( pEvaluatorParent->GetEventName() ); + FOR_EACH_MAP( k_mapConditions, i ) + { + QuestConditionEntry_t* pCondEntry = k_mapConditions[ i ]; + // All fields need to be present in the event for the condition to be a valid child + bool bAnyFound = false; + bool bAllFound = true; + if ( pCondEntry->m_nRequiredFields != FIELD_NONE ) + { + int nMaxCount = Q_log2( FIELD_LAST_FIELD ); + for( int nField = 0; nField <= nMaxCount && bAllFound; ++nField ) + { + if ( pCondEntry->m_nRequiredFields & (1<<nField) ) + { + bAnyFound = true; + bAllFound &= pKVEvent->FindKey( k_pszQuestConditionRequiredFieldStrings[ nField ] ) != NULL; + } + } + } + else + { + bAllFound = true; + bAnyFound = true; + } + + if ( bAnyFound && bAllFound ) + { + vecOutValidChildren.AddToTail( k_mapConditions.Key( i ) ); + } + } +} + +void CTFQuestCondition::GetValidEvaluators( CUtlVector< const char* >& vecOutValidChildren ) const +{ + vecOutValidChildren.AddToTail( "event_listener" ); + vecOutValidChildren.AddToTail( "counter" ); +} + +void CTFQuestRestriction::GetValidTypes( CUtlVector< const char* >& vecOutValidChildren ) const +{ + GetValidRestrictions( vecOutValidChildren ); +} + +void CTFQuestRestriction::GetValidChildren( CUtlVector< const char* >& vecOutValidChildren ) const +{ + GetValidRestrictions( vecOutValidChildren ); +} + + +CTFQuestEvaluator::CTFQuestEvaluator() +{ + m_pszAction = NULL; +} + + +void CTFQuestEvaluator::GetOutputKeyValues( KeyValues *pOutputKeys ) +{ + if ( m_pszAction ) + { + pOutputKeys->SetString( "action", m_pszAction ); + } +} + +void CTFQuestEvaluator::GetValidTypes( CUtlVector< const char* >& vecOutValidChildren ) const +{ + GetValidEvaluators( vecOutValidChildren ); +} + +void CTFQuestEvaluator::GetValidChildren( CUtlVector< const char* >& vecOutValidChildren ) const +{ + GetValidRestrictions( vecOutValidChildren ); +} + + +//----------------------------------------------------------------------------- +// Purpose: base quest operator restriction +//----------------------------------------------------------------------------- +class CTFQuestOperatorRestriction: public CTFQuestRestriction +{ +public: + DECLARE_CLASS( CTFQuestOperatorRestriction, CTFQuestRestriction ) + + virtual ~CTFQuestOperatorRestriction() + { + m_vecRestrictions.PurgeAndDeleteElements(); + } + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + if ( !CTFQuestRestriction::BInitFromKV( pKVItem, pVecErrors ) ) + return false; + + int nInputCount = 0; + FOR_EACH_TRUE_SUBKEY( pKVItem, pSubKey ) + { + if ( GetMaxInputCount() > 0 ) + { + SCHEMA_INIT_CHECK( nInputCount < GetMaxInputCount(), "%s", CFmtStr( "Too many input for operator '%s'. expected %d input(s)", GetConditionName(), GetMaxInputCount() ).Get() ); + } + + const char *pszType = pSubKey->GetString( "type" ); + CTFQuestRestriction *pNewRestriction = CreateRestrictionByName( pszType, this ); + SCHEMA_INIT_CHECK( pNewRestriction != NULL, "%s", CFmtStr( "Failed to create quest restriction name '%s' for '%s'", pszType, GetConditionName() ).Get() ); + + SCHEMA_INIT_CHECK( pNewRestriction->BInitFromKV( pSubKey, pVecErrors ), "Failed to init from KeyValues" ); + + m_vecRestrictions.AddToTail( pNewRestriction ); + nInputCount++; + } + SCHEMA_INIT_CHECK( nInputCount > 0 && nInputCount <= GetMaxInputCount(), "%s", CFmtStr( "Invalid number of specified input. Expected from 0 to %d inputs.", GetMaxInputCount() ).Get() ); + + return true; + } + + virtual bool IsOperator() const OVERRIDE { return true; } + + virtual void PrintDebugText() const OVERRIDE + { + DevMsg( "( " ); + FOR_EACH_VEC( m_vecRestrictions, i ) + { + if ( i == 0 ) + { + m_vecRestrictions[i]->PrintDebugText(); + } + else + { + DevMsg( " %s ", GetConditionName() ); + m_vecRestrictions[i]->PrintDebugText(); + } + } + DevMsg( " )" ); + } + + CTFQuestCondition* AddChildByName( const char *pszChildName ) OVERRIDE + { + if ( m_vecRestrictions.Count() >= GetMaxInputCount() ) + { + Assert( m_vecRestrictions.Count() < GetMaxInputCount() ); + return NULL; + } + + CTFQuestRestriction *pNewRestriction = CreateRestrictionByName( pszChildName, this ); + if ( pNewRestriction ) + { + m_vecRestrictions.AddToTail( pNewRestriction ); + } + + return pNewRestriction; + } + + virtual int GetChildren( CUtlVector< CTFQuestCondition* >& vecChildren ) OVERRIDE + { + FOR_EACH_VEC( m_vecRestrictions, i ) + { + vecChildren.AddToTail( m_vecRestrictions[i] ); + } + + return vecChildren.Count(); + } + + bool RemoveAndDeleteChild( CTFQuestCondition *pChild ) OVERRIDE + { + CTFQuestRestriction *pRestrictionChild = assert_cast< CTFQuestRestriction* >( pChild ); + bool bRemoved = m_vecRestrictions.FindAndFastRemove( pRestrictionChild ); + Assert( bRemoved ); + + if ( bRemoved ) + { + delete pChild; + } + + return bRemoved; + } + + + virtual int GetMaxInputCount() const OVERRIDE { return s_nMaxInputCount; } + + protected: + + CUtlVector< CTFQuestRestriction* > m_vecRestrictions; +}; + +//----------------------------------------------------------------------------- +// Purpose: AND quest operator restriction +//----------------------------------------------------------------------------- +class CTFQuestAndOperatorRestriction : public CTFQuestOperatorRestriction +{ +public: + DECLARE_CLASS( CTFQuestAndOperatorRestriction, CTFQuestOperatorRestriction ) + + virtual bool PassesRestrictions( IGameEvent *pEvent ) const OVERRIDE + { + FOR_EACH_VEC( m_vecRestrictions, i ) + { + if ( !m_vecRestrictions[i]->PassesRestrictions( pEvent ) ) + return false; + } + + return true; + } + + virtual bool IsValidForPlayer( const CTFPlayer *pOwner, InvalidReasonsContainer_t& invalidReasons ) const + { + BaseClass::IsValidForPlayer( pOwner, invalidReasons ); + + bool bIsForLocalPlayer = false; + InvalidReason operatorReasons; + FOR_EACH_VEC( m_vecRestrictions, i ) + { + bIsForLocalPlayer |= m_vecRestrictions[i]->IsValidForPlayer( pOwner, operatorReasons ); + } + + if ( bIsForLocalPlayer ) + { + invalidReasons.m_bits.Or( operatorReasons.m_bits, &invalidReasons.m_bits ); + } + + return bIsForLocalPlayer; + } +}; +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFQuestAndOperatorRestriction, AND, FIELD_NONE ); + +//----------------------------------------------------------------------------- +// Purpose: OR quest operator restriction +//----------------------------------------------------------------------------- +class CTFQuestOrOperatorRestriction : public CTFQuestOperatorRestriction +{ +public: + DECLARE_CLASS( CTFQuestOrOperatorRestriction, CTFQuestOperatorRestriction ) + + virtual bool PassesRestrictions( IGameEvent *pEvent ) const OVERRIDE + { + FOR_EACH_VEC( m_vecRestrictions, i ) + { + if ( m_vecRestrictions[i]->PassesRestrictions( pEvent ) ) + return true; + } + + return false; + } + + virtual bool IsValidForPlayer( const CTFPlayer *pOwner, InvalidReasonsContainer_t& invalidReasons ) const + { + BaseClass::IsValidForPlayer( pOwner, invalidReasons ); + + bool bIsForLocalPlayer = false; + + FOR_EACH_VEC( m_vecRestrictions, i ) + { + InvalidReason operatorReason; + if ( m_vecRestrictions[i]->IsValidForPlayer( pOwner, operatorReason ) ) + { + bIsForLocalPlayer = true; + invalidReasons.m_bits.Or( operatorReason.m_bits, &invalidReasons.m_bits ); + } + } + + return bIsForLocalPlayer; + } +}; +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFQuestOrOperatorRestriction, OR, FIELD_NONE ); + +//----------------------------------------------------------------------------- +// Purpose: NOT quest operator restriction +//----------------------------------------------------------------------------- +class CTFQuestNotOperatorRestriction : public CTFQuestOperatorRestriction +{ +public: + virtual bool PassesRestrictions( IGameEvent *pEvent ) const OVERRIDE + { + return !m_vecRestrictions[0]->PassesRestrictions( pEvent ); + } + + virtual bool IsValidForPlayer( const CTFPlayer *pOwner, InvalidReasonsContainer_t& invalidReasons ) const + { + return false; + } + + virtual void PrintDebugText() const OVERRIDE + { + CTFQuestRestriction *pRestriction = m_vecRestrictions[0]; + if ( pRestriction->IsOperator() ) + { + DevMsg( "%s ", GetConditionName() ); + pRestriction->PrintDebugText(); + } + else + { + // add () for non-operator to keep the debug text format consistent + DevMsg( "%s ( ", GetConditionName() ); + pRestriction->PrintDebugText(); + DevMsg( " )" ); + } + } + +protected: + virtual int GetMaxInputCount() const OVERRIDE { return 1; } +}; +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFQuestNotOperatorRestriction, NOT, FIELD_NONE ); + +class CTFGenericStringRestriction : public CTFQuestRestriction +{ +public: + CTFGenericStringRestriction() + : m_pszKeyName( NULL ) + , m_pszValue( NULL ) + {} + virtual const char *GetConditionName() const OVERRIDE { return m_pszFieldName; } + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + if ( !CTFQuestRestriction::BInitFromKV( pKVItem, pVecErrors ) ) + return false; + + m_pszKeyName = pKVItem->GetString( "key_to_lookup" ); + SCHEMA_INIT_CHECK( m_pszKeyName != NULL, "Missing key to lookup for generic_string restriction!" ); + + m_pszValue = pKVItem->GetString( "value" ); + SCHEMA_INIT_CHECK( m_pszValue != NULL, "Missing value to compare against for generic_string restriction!" ); + + return true; + } + + virtual bool PassesRestrictions( IGameEvent *pEvent ) const OVERRIDE + { + const char* pszValue = pEvent->GetString( m_pszKeyName ); + if ( pszValue ) + { + return FStrEq( pszValue, m_pszValue ); + } + + return false; + } + + virtual void GetOutputKeyValues( KeyValues *pOutputKeys ) OVERRIDE + { + CTFQuestRestriction::GetOutputKeyValues( pOutputKeys ); + + pOutputKeys->SetString( "key_to_lookup", m_pszKeyName ); + pOutputKeys->SetString( "value", m_pszValue ); + } + + virtual void GetRequiredParamKeys( KeyValues *pRequiredKeys ) OVERRIDE + { + CTFQuestRestriction::GetRequiredParamKeys( pRequiredKeys ); + + GetValidParamsKeyFromEvent( "key_to_lookup", m_pszFieldName, m_pszEventName, pRequiredKeys ); + GetValidParamsKeyFromEvent( "value", m_pszFieldName, m_pszEventName, pRequiredKeys ); + } + +protected: + + const char *m_pszKeyName; + const char *m_pszValue; +}; + +class CTFGenericSubStringRestriction : public CTFGenericStringRestriction +{ +public: + CTFGenericSubStringRestriction() + {} + + virtual bool PassesRestrictions( IGameEvent *pEvent ) const OVERRIDE + { + const char* pszValue = pEvent->GetString( m_pszKeyName ); + if ( pszValue ) + { + return V_stristr( pszValue, m_pszValue ) != NULL; + } + + return false; + } +}; + +class CTFWeaponClassRestriction : public CTFGenericStringRestriction +{ +public: + CTFWeaponClassRestriction() + {} + + virtual bool PassesRestrictions( IGameEvent *pEvent ) const OVERRIDE + { + const char* pszValue = pEvent->GetString( m_pszKeyName ); + if ( pszValue ) + { + const CEconItemDefinition* pItemDef = GetItemSchema()->GetItemDefinition( atoi( pszValue ) ); + Assert( pItemDef ); + if ( pItemDef ) + { + return FStrEq( pItemDef->GetItemClass(), m_pszValue ); + } + } + + return false; + } + +}; + +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFGenericStringRestriction, crit_kill, FIELD_CRIT ); +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFGenericStringRestriction, weapon_def_index, FIELD_WEAPON_DEF_INDEX ); +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFGenericStringRestriction, weapon_name, FIELD_WEAPON_NAME ); +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFGenericStringRestriction, halloween_boss_type, FIELD_HALLOWEEN_BOSS_TYPE ); +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFGenericStringRestriction, minigame_type, FIELD_HALLOWEEN_MINIGAME_TYPE ); +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFGenericStringRestriction, bonuseffect, FIELD_BONUSEFFECT ); +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFWeaponClassRestriction, weapon_class, FIELD_WEAPON_CLASS ); +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFGenericSubStringRestriction, deflected_projectile, FIELD_DEFLECTED_PROJECTILE ); + +//----------------------------------------------------------------------------- +// Purpose: quest player restriction +//----------------------------------------------------------------------------- +class CTFQuestBasePlayerRestriction : public CTFQuestRestriction +{ +public: + CTFQuestBasePlayerRestriction() + : m_pszPlayerKey( NULL ) + , m_pszPlayerMethod( NULL ) + {} + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + if ( !CTFQuestRestriction::BInitFromKV( pKVItem, pVecErrors ) ) + return false; + + m_pszPlayerKey = pKVItem->GetString( "player_key", NULL ); + Assert( m_pszPlayerKey ); + SCHEMA_INIT_CHECK( m_pszPlayerKey != NULL, "missing 'player_key'" ); + + static const char *s_pszValidGetMethod[] = + { + "by_id", + "by_entindex", + "by_cappers" + }; + m_pszPlayerMethod = pKVItem->GetString( "get_player" ); + bool bIsValidMethod = false; + for ( int i=0; i<ARRAYSIZE( s_pszValidGetMethod ); ++i ) + { + if ( FStrEq( m_pszPlayerMethod, s_pszValidGetMethod[i] ) ) + { + bIsValidMethod = true; + break; + } + } + SCHEMA_INIT_CHECK( bIsValidMethod, "Invalid 'get_player'" ); + + + return true; + } + + virtual bool PassesRestrictions( IGameEvent *pEvent ) const OVERRIDE + { + CTFPlayer *pPlayer = GetPlayerFromEvent( pEvent ); + if ( !pPlayer ) + return false; + + return BPlayerCheck( pPlayer, pEvent ); + } + + CTFPlayer *GetPlayerFromEvent( IGameEvent *pEvent ) const + { + CTFPlayer *pPlayer = NULL; + if ( FStrEq( m_pszPlayerMethod, "by_id" ) ) + { + int iPlayerID = pEvent->GetInt( m_pszPlayerKey ); + pPlayer = ToTFPlayer( UTIL_PlayerByUserId( iPlayerID ) ); + } + else if ( FStrEq( m_pszPlayerMethod, "by_entindex" ) ) + { + int iPlayerIndex = pEvent->GetInt( m_pszPlayerKey ); + pPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayerIndex ) ); + } + else if ( FStrEq( m_pszPlayerMethod, "by_cappers" ) ) + { + Assert( FStrEq( m_pszPlayerKey, "cappers" ) ); + const CTFPlayer *pQuestOwner = GetQuestOwner(); + const char *cappers = pEvent->GetString( m_pszPlayerKey ); + for ( int i = 0; i < Q_strlen( cappers ); i++ ) + { + int iPlayerIndex = (int)cappers[i]; + CTFPlayer *pCapper = ToTFPlayer( UTIL_PlayerByIndex( iPlayerIndex ) ); + if ( pCapper == pQuestOwner ) + { + pPlayer = pCapper; + break; + } + } + } + + return pPlayer; + } + + virtual void GetRequiredParamKeys( KeyValues *pRequiredKeys ) OVERRIDE + { + CTFQuestRestriction::GetRequiredParamKeys( pRequiredKeys ); + + GetValidParamsKeyFromEvent( "player_key", "player", m_pszEventName, pRequiredKeys ); + GetValidParamsKeyFromEvent( "get_player", "player", m_pszEventName, pRequiredKeys ); + } + + virtual void GetOutputKeyValues( KeyValues *pOutputKeys ) OVERRIDE + { + CTFQuestRestriction::GetOutputKeyValues( pOutputKeys ); + + pOutputKeys->SetString( "player_key", m_pszPlayerKey ); + pOutputKeys->SetString( "get_player", m_pszPlayerMethod ); + } + +protected: + + virtual bool BPlayerCheck( const CTFPlayer* pPlayer, IGameEvent *pEvent ) const = 0; + + const char *m_pszPlayerKey; + const char *m_pszPlayerMethod; +}; + +//----------------------------------------------------------------------------- +// Purpose: quest player restriction +//----------------------------------------------------------------------------- +class CTFQuestPlayerDisguiseRestriction : public CTFQuestBasePlayerRestriction +{ +public: + CTFQuestPlayerDisguiseRestriction() {} + + enum EDisguiseTargetState_t + { + DISGUISE_STATE_OWNER_IS_PLAYER, + DISGUISE_STATE_OWNER_IS_NOT_PLAYER, + DISGUISE_STATE_PLAYER_IS_OWNER, + DISGUISE_STATE_PLAYER_IS_NOT_OWNER, + DISGUISE_STATE_DONT_CARE + }; + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + if ( !CTFQuestBasePlayerRestriction::BInitFromKV( pKVItem, pVecErrors ) ) + return false; + + m_eDisguiseState = (EDisguiseTargetState_t)pKVItem->GetInt( "disguise_target", DISGUISE_STATE_DONT_CARE ); + + return true; + } + + + virtual void GetRequiredParamKeys( KeyValues *pRequiredKeys ) OVERRIDE + { + CTFQuestBasePlayerRestriction::GetRequiredParamKeys( pRequiredKeys ); + + // Disguise state + { + KeyValues *pKVDisguiseKeys = new KeyValues( "disguise_target" ); + pKVDisguiseKeys->SetString( "english_name", "Disguise" ); + + // DISGUISE_STATE_OWNER_IS_PLAYER + KeyValues * pKVDisguiseState = pKVDisguiseKeys->CreateNewKey(); + pKVDisguiseState->SetName( "0" ); + pKVDisguiseState->SetString( "english_name", "Owner disguised as the player" ); + + // DISGUISE_STATE_OWNER_IS_NOT_PLAYER + pKVDisguiseState = pKVDisguiseKeys->CreateNewKey(); + pKVDisguiseState->SetName( "1" ); + pKVDisguiseState->SetString( "english_name", "Owner NOT disguised as the player" ); + + // DISGUISE_STATE_PLAYER_IS_OWNER + pKVDisguiseState = pKVDisguiseKeys->CreateNewKey(); + pKVDisguiseState->SetName( "2" ); + pKVDisguiseState->SetString( "english_name", "Player disguised as the owner" ); + + // DISGUISE_STATE_PLAYER_IS_NOT_OWNER + pKVDisguiseState = pKVDisguiseKeys->CreateNewKey(); + pKVDisguiseState->SetName( "3" ); + pKVDisguiseState->SetString( "english_name", "Player NOT disguised as the owner" ); + + pRequiredKeys->AddSubKey( pKVDisguiseKeys ); + } + } + + virtual void GetOutputKeyValues( KeyValues *pOutputKeys ) OVERRIDE + { + CTFQuestBasePlayerRestriction::GetOutputKeyValues( pOutputKeys ); + + pOutputKeys->SetInt( "disguise_target", m_eDisguiseState ); + } + +protected: + + virtual bool BPlayerCheck( const CTFPlayer* pPlayer, IGameEvent *pEvent ) const OVERRIDE + { + // Disguise state check + const CTFPlayer* pPlayerDisguiseTarget = ToTFPlayer( pPlayer->m_Shared.GetDisguiseTarget() ); + const CTFPlayer* pOwnerDisguiseTarget = ToTFPlayer( GetQuestOwner()->m_Shared.GetDisguiseTarget() ); + + // owner in disguise + if ( pOwnerDisguiseTarget ) + { + // must disguise as same class to look the same + if ( m_eDisguiseState == DISGUISE_STATE_OWNER_IS_PLAYER && pOwnerDisguiseTarget == pPlayer && GetQuestOwner()->m_Shared.GetDisguiseClass() == pPlayer->GetPlayerClass()->GetClassIndex() ) + return true; + + if ( m_eDisguiseState == DISGUISE_STATE_OWNER_IS_NOT_PLAYER && pOwnerDisguiseTarget != pPlayer ) + return true; + } + + // player in disguise + if ( pPlayerDisguiseTarget ) + { + // must disguise as same class to look the same + if ( m_eDisguiseState == DISGUISE_STATE_PLAYER_IS_OWNER && pPlayerDisguiseTarget == GetQuestOwner() && pPlayer->m_Shared.GetDisguiseClass() == GetQuestOwner()->GetPlayerClass()->GetClassIndex() ) + return true; + + if ( m_eDisguiseState == DISGUISE_STATE_PLAYER_IS_NOT_OWNER && pPlayerDisguiseTarget != GetQuestOwner() ) + return true; + } + + return false; + } + + EDisguiseTargetState_t m_eDisguiseState; +}; +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFQuestPlayerDisguiseRestriction, player_disguise, FIELD_PLAYER ); + +//----------------------------------------------------------------------------- +// Purpose: quest player jumping-state restriction +//----------------------------------------------------------------------------- +class CTFQuestPlayerJumpingRestriction : public CTFQuestBasePlayerRestriction +{ +public: + CTFQuestPlayerJumpingRestriction() {} + + enum EJumpingState_t + { + JUMPING_STATE_IS_NOT_JUMPING = 0, + JUMPING_STATE_IS_JUMPING, + JUMPING_STATE_IS_DOUBLE_JUMPING, + JUMPING_STATE_IS_TRIPLE_JUMPING, + }; + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + if ( !CTFQuestBasePlayerRestriction::BInitFromKV( pKVItem, pVecErrors ) ) + return false; + + m_eJumpingState = (EJumpingState_t)pKVItem->GetInt( "jumping_state", JUMPING_STATE_IS_NOT_JUMPING ); + + return true; + } + + virtual void GetRequiredParamKeys( KeyValues *pRequiredKeys ) OVERRIDE + { + CTFQuestBasePlayerRestriction::GetRequiredParamKeys( pRequiredKeys ); + + // Jumping state + { + KeyValues *pKVJumpingKeys = new KeyValues( "jumping_state" ); + pKVJumpingKeys->SetString( "english_name", "Jumping" ); + KeyValues * pKVJumpState = pKVJumpingKeys->CreateNewKey(); + pKVJumpState->SetName( "0" ); + pKVJumpState->SetString( "english_name", "Must NOT be jumping" ); + + pKVJumpState = pKVJumpingKeys->CreateNewKey(); + pKVJumpState->SetName( "1" ); + pKVJumpState->SetString( "english_name", "Must be at least jumping" ); + + pKVJumpState = pKVJumpingKeys->CreateNewKey(); + pKVJumpState->SetName( "2" ); + pKVJumpState->SetString( "english_name", "Must be at least double-jumping" ); + + pKVJumpState = pKVJumpingKeys->CreateNewKey(); + pKVJumpState->SetName( "3" ); + pKVJumpState->SetString( "english_name", "Must be at least triple-jumping" ); + + pRequiredKeys->AddSubKey( pKVJumpingKeys ); + } + } + + virtual void GetOutputKeyValues( KeyValues *pOutputKeys ) OVERRIDE + { + CTFQuestBasePlayerRestriction::GetOutputKeyValues( pOutputKeys ); + pOutputKeys->SetInt( "jumping_state", m_eJumpingState ); + } + +protected: + + virtual bool BPlayerCheck( const CTFPlayer* pPlayer, IGameEvent *pEvent ) const OVERRIDE + { +#ifdef GAME_DLL + CTFPlayer* pNonConstPlayer = const_cast< CTFPlayer* >( pPlayer ); + + int nNumJumps = pNonConstPlayer->GetGroundEntity() == NULL ? 1 : 0; + nNumJumps += pPlayer->m_Shared.GetAirDash(); + nNumJumps += pPlayer->m_bScattergunJump; + + if ( m_eJumpingState == JUMPING_STATE_IS_NOT_JUMPING ) + { + return nNumJumps == 0; + } + else if ( m_eJumpingState == JUMPING_STATE_IS_JUMPING ) + { + return nNumJumps >= 1; + } + else if ( m_eJumpingState == JUMPING_STATE_IS_DOUBLE_JUMPING ) + { + return nNumJumps >= 2; + } + else if ( m_eJumpingState == JUMPING_STATE_IS_TRIPLE_JUMPING ) + { + return nNumJumps >= 3; + } + else + { + AssertMsg1( false, "Unhandled EJumpingState_t case %d!", m_eJumpingState ); + } +#endif + return false; + } + + EJumpingState_t m_eJumpingState; +}; +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFQuestPlayerJumpingRestriction, player_jumping, FIELD_PLAYER ); + +//----------------------------------------------------------------------------- +// Purpose: quest player alive-state restriction +//----------------------------------------------------------------------------- +class CTFQuestPlayerAliveRestriction : public CTFQuestBasePlayerRestriction +{ +public: + CTFQuestPlayerAliveRestriction() {} + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + if ( !CTFQuestBasePlayerRestriction::BInitFromKV( pKVItem, pVecErrors ) ) + return false; + + m_bAliveState = pKVItem->GetBool( "alive_state", true ); + + return true; + } + + virtual void GetRequiredParamKeys( KeyValues *pRequiredKeys ) OVERRIDE + { + CTFQuestBasePlayerRestriction::GetRequiredParamKeys( pRequiredKeys ); + + KeyValues *pKVAliveKeys = new KeyValues( "alive_state" ); + pKVAliveKeys->SetString( "english_name", "Alive" ); + KeyValues * pKVAliveState = pKVAliveKeys->CreateNewKey(); + pKVAliveState->SetName( "1" ); + pKVAliveState->SetString( "english_name", "Must be alive" ); + + pKVAliveState = pKVAliveKeys->CreateNewKey(); + pKVAliveState->SetName( "0" ); + pKVAliveState->SetString( "english_name", "Must be dead" ); + + pRequiredKeys->AddSubKey( pKVAliveKeys ); + } + + virtual void GetOutputKeyValues( KeyValues *pOutputKeys ) OVERRIDE + { + CTFQuestBasePlayerRestriction::GetOutputKeyValues( pOutputKeys ); + pOutputKeys->SetInt( "alive_state", m_bAliveState ); + } + +protected: + + virtual bool BPlayerCheck( const CTFPlayer* pPlayer, IGameEvent *pEvent ) const OVERRIDE + { + return ( pPlayer->m_iHealth > 0 ) == m_bAliveState; + } + + bool m_bAliveState; +}; +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFQuestPlayerAliveRestriction, player_alive, FIELD_PLAYER ); + +//----------------------------------------------------------------------------- +// Purpose: quest player distance restriction relative to the owner +//----------------------------------------------------------------------------- +class CTFQuestPlayerDistanceRestriction : public CTFQuestBasePlayerRestriction +{ +public: + CTFQuestPlayerDistanceRestriction() {} + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + if ( !CTFQuestBasePlayerRestriction::BInitFromKV( pKVItem, pVecErrors ) ) + return false; + + m_eCheckType = (EDistanceCheck_t)pKVItem->GetInt( "distance_check_type", INVALID_CHECK_TYPE ); + SCHEMA_INIT_CHECK( m_eCheckType != INVALID_CHECK_TYPE, "Invalid distance_check_type %d!", m_eCheckType ); + + m_nDistance = pKVItem->GetInt( "distance_to_check", 0 ); + SCHEMA_INIT_CHECK( m_eCheckType != 0, "Distance must be non-zero!" ); + + return true; + } + + virtual void GetRequiredParamKeys( KeyValues *pRequiredKeys ) OVERRIDE + { + CTFQuestBasePlayerRestriction::GetRequiredParamKeys( pRequiredKeys ); + + KeyValues *pKVDistanceCheckTypeKeys = new KeyValues( "distance_check_type" ); + pKVDistanceCheckTypeKeys->SetString( "english_name", "Alive" ); + KeyValues * pKVType = pKVDistanceCheckTypeKeys->CreateNewKey(); + pKVType->SetName( "1" ); + pKVType->SetString( "english_name", "Closer than" ); + + pKVType = pKVDistanceCheckTypeKeys->CreateNewKey(); + pKVType->SetName( "2" ); + pKVType->SetString( "english_name", "Further than" ); + + pRequiredKeys->AddSubKey( pKVDistanceCheckTypeKeys ); + + KeyValues *pDistanceKey = new KeyValues( "distance_to_check" ); + pDistanceKey->SetString( "control_type", "text_entry" ); + + pRequiredKeys->AddSubKey( pDistanceKey ); + } + + virtual void GetOutputKeyValues( KeyValues *pOutputKeys ) OVERRIDE + { + CTFQuestBasePlayerRestriction::GetOutputKeyValues( pOutputKeys ); + pOutputKeys->SetInt( "distance_check_type", m_eCheckType ); + pOutputKeys->SetInt( "distance_to_check", m_nDistance ); + } + +protected: + + enum EDistanceCheck_t + { + INVALID_CHECK_TYPE = 0, + CLOSER_THAN, + FURTHER_THAN + }; + + virtual bool BPlayerCheck( const CTFPlayer* pPlayer, IGameEvent *pEvent ) const OVERRIDE + { + int nTestDist = m_nDistance * m_nDistance; + int nPlayerDistSq = ( pPlayer->GetAbsOrigin() - GetQuestOwner()->GetAbsOrigin() ).LengthSqr(); + + if ( m_eCheckType == CLOSER_THAN ) + { + return nPlayerDistSq < nTestDist; + } + else + { + return nPlayerDistSq > nTestDist; + } + } + + EDistanceCheck_t m_eCheckType; + float m_nDistance; +}; +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFQuestPlayerDistanceRestriction, player_distance, FIELD_PLAYER ); + + +//----------------------------------------------------------------------------- +// Purpose: quest player restriction +//----------------------------------------------------------------------------- +class CTFQuestPlayerIsOwnerRestriction : public CTFQuestBasePlayerRestriction +{ +public: + CTFQuestPlayerIsOwnerRestriction() {} + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + if ( !CTFQuestBasePlayerRestriction::BInitFromKV( pKVItem, pVecErrors ) ) + return false; + + // should check if this player is quest owner? + m_bIsOwner = pKVItem->GetBool( "is_owner" ); + + return true; + } + + virtual bool IsValidForPlayer( const CTFPlayer *pOwner, InvalidReasonsContainer_t& invalidReasons ) const + { + CTFQuestBasePlayerRestriction::IsValidForPlayer( pOwner, invalidReasons ); + + return m_bIsOwner && pOwner == GetQuestOwner(); + } + + virtual void GetRequiredParamKeys( KeyValues *pRequiredKeys ) OVERRIDE + { + CTFQuestBasePlayerRestriction::GetRequiredParamKeys( pRequiredKeys ); + + KeyValues *pIsOwnerKey = new KeyValues( "is_owner" ); + pIsOwnerKey->SetString( "english_name", "is_owner" ); + + KeyValues *pIsOwnerKeyChoiceTrue = new KeyValues( "1" ); + pIsOwnerKeyChoiceTrue->SetString( "english_name", "true" ); + + KeyValues *pIsOwnerKeyChoiceFalse = new KeyValues( "0" ); + pIsOwnerKeyChoiceFalse->SetString( "english_name", "false" ); + + pIsOwnerKey->AddSubKey( pIsOwnerKeyChoiceTrue ); + pIsOwnerKey->AddSubKey( pIsOwnerKeyChoiceFalse ); + + pRequiredKeys->AddSubKey( pIsOwnerKey ); + } + + virtual void GetOutputKeyValues( KeyValues *pOutputKeys ) OVERRIDE + { + CTFQuestBasePlayerRestriction::GetOutputKeyValues( pOutputKeys ); + pOutputKeys->SetBool( "is_owner", m_bIsOwner ); + } + +private: + + virtual bool BPlayerCheck( const CTFPlayer* pPlayer, IGameEvent *pEvent ) const OVERRIDE + { + return m_bIsOwner == ( pPlayer == GetQuestOwner() ); + } + + bool m_bIsOwner; +}; +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFQuestPlayerIsOwnerRestriction, player_is_owner, FIELD_PLAYER ); + +//----------------------------------------------------------------------------- +// Purpose: quest class restriction +//----------------------------------------------------------------------------- +class CTFQuestPlayerClassRestriction : public CTFQuestBasePlayerRestriction +{ +public: + DECLARE_CLASS( CTFQuestPlayerClassRestriction, CTFQuestBasePlayerRestriction ) + + CTFQuestPlayerClassRestriction() + { + m_iClass = TF_CLASS_UNDEFINED; + } + + virtual const char *GetValueString() const OVERRIDE + { + return GetItemSchema()->GetClassUsabilityStrings()[ m_iClass ]; + } + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + if ( !CTFQuestBasePlayerRestriction::BInitFromKV( pKVItem, pVecErrors ) ) + return false; + + const char *pszClassName = pKVItem->GetString( "value", NULL ); + m_iClass = StringFieldToInt( pszClassName, GetItemSchema()->GetClassUsabilityStrings() ); + SCHEMA_INIT_CHECK( IsValidTFPlayerClass( m_iClass ), "%s", CFmtStr( "Invalid owner class restriction '%s' for quest objective", pszClassName ).Get() ); + + return true; + } + + virtual bool IsValidForPlayer( const CTFPlayer *pOwner, InvalidReasonsContainer_t& invalidReasons ) const + { + BaseClass::IsValidForPlayer( pOwner, invalidReasons ); + + if ( !BPlayerCheck( pOwner, NULL ) ) + invalidReasons.m_bits.Set( INVALID_QUEST_REASON_WRONG_CLASS ); + + return false; + } + + virtual void GetRequiredParamKeys( KeyValues *pRequiredKeys ) OVERRIDE + { + CTFQuestBasePlayerRestriction::GetRequiredParamKeys( pRequiredKeys ); + + KeyValues *pClassesKey = new KeyValues( "value" ); + for ( int i=0; i<GetItemSchema()->GetClassUsabilityStrings().Count(); ++i ) + { + const char *pszClassName = GetItemSchema()->GetClassUsabilityStrings()[i]; + pClassesKey->AddSubKey( new KeyValues( pszClassName ) ); + } + + pRequiredKeys->AddSubKey( pClassesKey ); + } + + virtual void GetOutputKeyValues( KeyValues *pOutputKeys ) OVERRIDE + { + CTFQuestBasePlayerRestriction::GetOutputKeyValues( pOutputKeys ); + + pOutputKeys->SetString( "value", GetValueString() ); + } + +private: + + virtual bool BPlayerCheck( const CTFPlayer* pPlayer, IGameEvent *pEvent ) const OVERRIDE + { + // Check if the classes match + int iClass = pPlayer->GetPlayerClass()->GetClassIndex(); + return m_iClass == iClass; + } + + int m_iClass; +}; +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFQuestPlayerClassRestriction, player_class, FIELD_PLAYER ); + +//----------------------------------------------------------------------------- +// Purpose: quest player condition restriction +//----------------------------------------------------------------------------- +class CTFQuestPlayerConditionRestriction : public CTFQuestBasePlayerRestriction +{ +public: + virtual const char *GetValueString() const OVERRIDE + { + return GetTFConditionName( m_eCondition ); + } + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + if ( !CTFQuestBasePlayerRestriction::BInitFromKV( pKVItem, pVecErrors ) ) + return false; + + const char *pszConditionName = pKVItem->GetString( "value", NULL ); + m_eCondition = GetTFConditionFromName( pszConditionName ); + SCHEMA_INIT_CHECK( m_eCondition != TF_COND_INVALID, "%s", CFmtStr( "Invalid %s restriction '%s' for quest objective", GetConditionName(), pszConditionName ).Get() ); + + m_bOwnerMustBeProvider = pKVItem->GetBool( "provider_must_be_owner", false ); + + return true; + } + + virtual void GetRequiredParamKeys( KeyValues *pRequiredKeys ) OVERRIDE + { + CTFQuestBasePlayerRestriction::GetRequiredParamKeys( pRequiredKeys ); + + KeyValues *pConditionsKey = new KeyValues( "value" ); + for ( int i=0; i<TF_COND_LAST; ++i ) + { + const char *pszConditionName = GetTFConditionName( ETFCond( i ) ); + pConditionsKey->AddSubKey( new KeyValues( pszConditionName ) ); + } + pRequiredKeys->AddSubKey( pConditionsKey ); + + KeyValues *pRequireOwnerIsProvider = new KeyValues( "provider_must_be_owner" ); + + KeyValues *pIsOwnerKeyChoiceTrue = new KeyValues( "1" ); + pIsOwnerKeyChoiceTrue->SetString( "english_name", "true" ); + pRequireOwnerIsProvider->AddSubKey( pIsOwnerKeyChoiceTrue ); + + KeyValues *pIsOwnerKeyChoiceFalse = new KeyValues( "0" ); + pIsOwnerKeyChoiceFalse->SetString( "english_name", "false" ); + pRequireOwnerIsProvider->AddSubKey( pIsOwnerKeyChoiceFalse ); + + pRequiredKeys->AddSubKey( pRequireOwnerIsProvider ); + } + + virtual void GetOutputKeyValues( KeyValues *pOutputKeys ) OVERRIDE + { + CTFQuestBasePlayerRestriction::GetOutputKeyValues( pOutputKeys ); + + pOutputKeys->SetString( "value", GetValueString() ); + pOutputKeys->SetBool( "provider_must_be_owner", m_bOwnerMustBeProvider ); + } + +private: + + virtual bool BPlayerCheck( const CTFPlayer* pPlayer, IGameEvent *pEvent ) const OVERRIDE + { + // Must be in the condition + if ( !pPlayer->m_Shared.InCond( m_eCondition ) ) + return false; + + // Might require the owner is the provider + if ( m_bOwnerMustBeProvider && pPlayer->m_Shared.GetConditionProvider( m_eCondition ) != GetQuestOwner() ) + return false; + + return true; + } + + ETFCond m_eCondition; + bool m_bOwnerMustBeProvider; +}; +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFQuestPlayerConditionRestriction, player_condition, FIELD_PLAYER ); + +//----------------------------------------------------------------------------- +// Purpose: quest player condition restriction +//----------------------------------------------------------------------------- +class CTFQuestPlayerObjectRestriction : public CTFQuestBasePlayerRestriction +{ +public: + DECLARE_CLASS( CTFQuestPlayerObjectRestriction, CTFQuestBasePlayerRestriction ) + + CTFQuestPlayerObjectRestriction() + : m_pszObjectKey( NULL ), + m_eObjectType( OBJ_DISPENSER ) + {} + + virtual const char *GetValueString() const OVERRIDE + { + return GetObjectInfo( m_eObjectType )->m_pObjectName; + } + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + if ( !CTFQuestBasePlayerRestriction::BInitFromKV( pKVItem, pVecErrors ) ) + return false; + + m_pszObjectKey = pKVItem->GetString( "object_key", NULL ); + SCHEMA_INIT_CHECK( m_pszObjectKey != NULL, "Missing object_key" ); + + const char *pszObjectTypeName = pKVItem->GetString( "value", NULL ); + m_eObjectType = (ObjectType_t)GetBuildableId( pszObjectTypeName ); + SCHEMA_INIT_CHECK( m_eObjectType != OBJ_LAST, "%s", CFmtStr( "Invalid %s restriction '%s' for quest objective", GetConditionName(), pszObjectTypeName ).Get() ); + + return true; + } + + virtual bool IsValidForPlayer( const CTFPlayer *pOwner, InvalidReasonsContainer_t& invalidReasons ) const + { + CTFQuestBasePlayerRestriction::IsValidForPlayer( pOwner, invalidReasons ); + + if ( !pOwner->IsPlayerClass( TF_CLASS_ENGINEER ) ) + invalidReasons.m_bits.Set( INVALID_QUEST_REASON_WRONG_CLASS ); + + return false; + } + + virtual void GetRequiredParamKeys( KeyValues *pRequiredKeys ) OVERRIDE + { + CTFQuestBasePlayerRestriction::GetRequiredParamKeys( pRequiredKeys ); + + GetValidParamsKeyFromEvent( "object_key", CTFQuestPlayerObjectRestriction::GetConditionName(), m_pszEventName, pRequiredKeys ); + + KeyValues *pObjectKey = new KeyValues( "value" ); + for ( int i=0; i<OBJ_LAST; ++i ) + { + const char *pszObjectName = GetObjectInfo( ObjectType_t(i) )->m_pObjectName; + pObjectKey->AddSubKey( new KeyValues( pszObjectName ) ); + } + + pRequiredKeys->AddSubKey( pObjectKey ); + } + + virtual void GetOutputKeyValues( KeyValues *pOutputKeys ) OVERRIDE + { + CTFQuestBasePlayerRestriction::GetOutputKeyValues( pOutputKeys ); + + pOutputKeys->SetString( "object_key", m_pszObjectKey ); + pOutputKeys->SetString( "value", GetValueString() ); + } + +private: + + virtual bool BPlayerCheck( const CTFPlayer* pPlayer, IGameEvent *pEvent ) const OVERRIDE + { + int nEntIndex = pEvent->GetInt( m_pszObjectKey ); +#ifdef GAME_DLL + CBaseEntity *pObj = UTIL_EntityByIndex( nEntIndex ); +#else + CBaseEntity *pObj = ClientEntityList().GetEnt( nEntIndex ); +#endif + CBaseObject *pPlayerObj = pPlayer->GetObjectOfType( m_eObjectType ); + return pObj && pPlayerObj && pObj == pPlayerObj; + } + const char *m_pszObjectKey; + ObjectType_t m_eObjectType; +}; +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFQuestPlayerObjectRestriction, object_type, FIELD_PLAYER | FIELD_OBJECT ); + +//----------------------------------------------------------------------------- +// Purpose: quest player condition restriction +//----------------------------------------------------------------------------- +class CTFQuestScorerRestriction : public CTFQuestBasePlayerRestriction +{ +public: + CTFQuestScorerRestriction() + : m_pszScorerKey( NULL ) + {} + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + if ( !CTFQuestBasePlayerRestriction::BInitFromKV( pKVItem, pVecErrors ) ) + return false; + + m_pszScorerKey = pKVItem->GetString( "scorer_key", NULL ); + SCHEMA_INIT_CHECK( m_pszScorerKey != NULL, "Missing scorer key" ); + + return true; + } + + virtual bool IsValidForPlayer( const CTFPlayer *pOwner, InvalidReasonsContainer_t& invalidReasons ) const + { + BaseClass::IsValidForPlayer( pOwner, invalidReasons ); + + if ( !pOwner->IsPlayerClass( TF_CLASS_ENGINEER ) ) + invalidReasons.m_bits.Set( INVALID_QUEST_REASON_WRONG_CLASS ); + + return false; + } + + virtual void GetRequiredParamKeys( KeyValues *pRequiredKeys ) OVERRIDE + { + CTFQuestBasePlayerRestriction::GetRequiredParamKeys( pRequiredKeys ); + + GetValidParamsKeyFromEvent( "scorer_key", GetConditionName(), m_pszEventName, pRequiredKeys ); + } + + virtual void GetOutputKeyValues( KeyValues *pOutputKeys ) OVERRIDE + { + CTFQuestBasePlayerRestriction::GetOutputKeyValues( pOutputKeys ); + + pOutputKeys->SetString( "scorer_key", m_pszScorerKey ); + } + +private: + + virtual bool BPlayerCheck( const CTFPlayer* pPlayer, IGameEvent *pEvent ) const OVERRIDE + { +#ifdef GAME_DLL + int nEntIndex = pEvent->GetInt( m_pszScorerKey ); + + CBaseEntity *pEnt = UTIL_EntityByIndex( nEntIndex ); + IScorer* pScorer = dynamic_cast< IScorer* >( pEnt ); + + if ( !pScorer ) + return false; + + return pScorer->GetScorer() == pPlayer; +#else + return true; +#endif + } + + const char *m_pszScorerKey; +}; +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFQuestScorerRestriction, scorer, FIELD_PLAYER | FIELD_SCORER ); + +//----------------------------------------------------------------------------- +// Purpose: quest weapon restriction +//----------------------------------------------------------------------------- +class CTFQuestWeaponTypeRestriction : public CTFGenericStringRestriction +{ +public: + virtual const char *GetValueString() const OVERRIDE + { + return WeaponIdToAlias( m_eWeaponType ); + } + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + if ( !CTFGenericStringRestriction::BInitFromKV( pKVItem, pVecErrors ) ) + return false; + + m_eWeaponType = (ETFWeaponType)atoi( m_pszValue ); + SCHEMA_INIT_CHECK( m_eWeaponType != TF_WEAPON_NONE, "%s", CFmtStr( "Invalid weapon restriction '%s' for quest objective", m_pszValue ).Get() ); + + return true; + } + + virtual bool PassesRestrictions( IGameEvent *pEvent ) const OVERRIDE + { + ETFWeaponType weaponID = (ETFWeaponType)pEvent->GetInt( m_pszKeyName ); + return m_eWeaponType == weaponID; + } + +private: + ETFWeaponType m_eWeaponType; +}; +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFQuestWeaponTypeRestriction, weapon_type, FIELD_WEAPON_TYPE ); + + +//----------------------------------------------------------------------------- +// Purpose: quest custom damage restriction +//----------------------------------------------------------------------------- +class CTFQuestCustomDamageRestriction : public CTFQuestRestriction +{ +public: + virtual const char *GetValueString() const OVERRIDE + { + return GetCustomDamageName( m_eCustomDamageType ); + } + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + if ( !CTFQuestRestriction::BInitFromKV( pKVItem, pVecErrors ) ) + return false; + + const char *pszCustomDamageName = pKVItem->GetString( "value", NULL ); + m_eCustomDamageType = GetCustomDamageFromName( pszCustomDamageName ); + SCHEMA_INIT_CHECK( m_eCustomDamageType != TF_DMG_CUSTOM_NONE, "%s", CFmtStr( "Invalid weapon restriction '%s' for quest objective", pszCustomDamageName ).Get() ); + + m_pszCustomDamageKey = pKVItem->GetString( "custom_damage_key", NULL ); + SCHEMA_INIT_CHECK( m_pszCustomDamageKey != NULL, "Invalid custom_damage_key!" ); + + return true; + } + + virtual bool PassesRestrictions( IGameEvent *pEvent ) const OVERRIDE + { + ETFDmgCustom customDamageType = (ETFDmgCustom)pEvent->GetInt( m_pszCustomDamageKey ); + return m_eCustomDamageType == customDamageType; + } + + virtual void GetRequiredParamKeys( KeyValues *pRequiredKeys ) OVERRIDE + { + CTFQuestRestriction::GetRequiredParamKeys( pRequiredKeys ); + + GetValidParamsKeyFromEvent( "custom_damage_key", GetConditionName(), m_pszEventName, pRequiredKeys ); + + KeyValues *pDamageKey = new KeyValues( "value" ); + for ( int i=0; i<TF_DMG_CUSTOM_END; ++i ) + { + const char *pszCustomDamageName = GetCustomDamageName( ETFDmgCustom( i ) ); + pDamageKey->AddSubKey( new KeyValues( pszCustomDamageName ) ); + } + + pRequiredKeys->AddSubKey( pDamageKey ); + } + + virtual void GetOutputKeyValues( KeyValues *pOutputKeys ) OVERRIDE + { + CTFQuestRestriction::GetOutputKeyValues( pOutputKeys ); + + pOutputKeys->SetString( "value", GetValueString() ); + } + +private: + ETFDmgCustom m_eCustomDamageType; + const char *m_pszCustomDamageKey; +}; +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFQuestCustomDamageRestriction, custom_damage, FIELD_CUSTOM_DAMAGE ); + + +//----------------------------------------------------------------------------- +// Purpose: quest flag event restriction +//----------------------------------------------------------------------------- +class CTFFlagEventTypeRestriction : public CTFQuestRestriction +{ +public: + + CTFFlagEventTypeRestriction() + : m_eEventType( TF_FLAGEVENT_CAPTURE ) + , m_pszKeyName( NULL ) + {} + + virtual const char *GetValueString() const OVERRIDE + { + return GetCTFEventName( m_eEventType ); + } + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + if ( !CTFQuestRestriction::BInitFromKV( pKVItem, pVecErrors ) ) + return false; + + const char *pszEventType = pKVItem->GetString( "value", NULL ); + m_eEventType = GetCTFEventTypeFromName( pszEventType ); + SCHEMA_INIT_CHECK( m_eEventType != TF_NUM_FLAG_EVENTS, "%s", CFmtStr( "Invalid CTF Event Type '%s' for quest objective", pszEventType ).Get() ); + + m_pszKeyName = pKVItem->GetString( "event_key", "eventtype" ); + SCHEMA_INIT_CHECK( m_pszKeyName != NULL, "Missing \"event_key\" for flag_event_type" ); + + return true; + } + + virtual bool PassesRestrictions( IGameEvent *pEvent ) const OVERRIDE + { + ETFFlagEventTypes eEventType = (ETFFlagEventTypes)pEvent->GetInt( m_pszKeyName ); + return m_eEventType == eEventType; + } + + virtual void GetRequiredParamKeys( KeyValues *pRequiredKeys ) OVERRIDE + { + CTFQuestRestriction::GetRequiredParamKeys( pRequiredKeys ); + + GetValidParamsKeyFromEvent( "event_key", "flag_event", m_pszEventName, pRequiredKeys ); + + KeyValues *pEventKey = new KeyValues( "value" ); + for ( int i=1; i<TF_NUM_FLAG_EVENTS; ++i ) + { + const char *pszEventType = GetCTFEventName( ETFFlagEventTypes( i ) ); + pEventKey->AddSubKey( new KeyValues( pszEventType ) ); + } + + pRequiredKeys->AddSubKey( pEventKey ); + } + + virtual void GetOutputKeyValues( KeyValues *pOutputKeys ) OVERRIDE + { + CTFQuestRestriction::GetOutputKeyValues( pOutputKeys ); + + pOutputKeys->SetString( "value", GetValueString() ); + pOutputKeys->SetString( "event_key", m_pszKeyName ); + } + +private: + ETFFlagEventTypes m_eEventType; + const char* m_pszKeyName; +}; +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFFlagEventTypeRestriction, flag_event_type, FIELD_FLAG_EVENT ); + +static const char *s_pszTeamRestrictionNames[] = +{ + "TEAM_ANY", + "TEAM_IS_OWNERS", + "TEAM_IS_NOT_OWNERS", +}; +//----------------------------------------------------------------------------- +// Purpose: quest custom damage restriction +//----------------------------------------------------------------------------- +class CTFQuestTeamRestriction : public CTFQuestRestriction +{ +public: + + enum ETeamRestriction + { + TEAM_RESTRICTION_ANY = 0, + TEAM_RESTRICTION_IS_OWNERS, + TEAM_RESTRICTION_IS_NOT_OWNERS, + + TEAM_RESTRICTION_MAX + }; + + CTFQuestTeamRestriction() + : m_eTeamRestriction( TEAM_RESTRICTION_ANY ) + , m_pszTeamKey( NULL ) + {} + + virtual const char *GetValueString() const OVERRIDE + { + return s_pszTeamRestrictionNames[ m_eTeamRestriction ]; + } + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + if ( !CTFQuestRestriction::BInitFromKV( pKVItem, pVecErrors ) ) + return false; + + m_eTeamRestriction = (ETeamRestriction)pKVItem->GetInt( "team_requirement", TEAM_RESTRICTION_ANY ); + + m_pszTeamKey = pKVItem->GetString( "team_key", NULL ); + SCHEMA_INIT_CHECK( m_pszTeamKey != NULL, "Missing \"m_pszTeamKey\" in team_restriction" ); + + return true; + } + + virtual bool PassesRestrictions( IGameEvent *pEvent ) const OVERRIDE + { + int nTeam = pEvent->GetInt( m_pszTeamKey, TEAM_INVALID ); + if ( nTeam == TEAM_INVALID ) + { + AssertMsg( 0, "This event doesn't specify a team." ); + return false; + } + + const CTFPlayer *pOwner = GetQuestOwner(); + bool bTeamIsOwners = nTeam == pOwner->GetTeamNumber(); + if ( ( m_eTeamRestriction == TEAM_RESTRICTION_IS_OWNERS && !bTeamIsOwners ) || + ( m_eTeamRestriction == TEAM_RESTRICTION_IS_NOT_OWNERS && bTeamIsOwners ) ) + { + return false; + } + + return true; + } + + virtual void GetRequiredParamKeys( KeyValues *pRequiredKeys ) OVERRIDE + { + CTFQuestRestriction::GetRequiredParamKeys( pRequiredKeys ); + + GetValidParamsKeyFromEvent( "team_key", GetConditionName(), m_pszEventName, pRequiredKeys ); + + KeyValues *pTeamReq = new KeyValues( "team_requirement" ); + + for( int i=0; i<TEAM_RESTRICTION_MAX; ++i ) + { + KeyValues *pTeam = new KeyValues( CFmtStr( "%d", i ) ); + pTeam->SetString( "english_name", s_pszTeamRestrictionNames[ i ] ); + pTeamReq->AddSubKey( pTeam ); + } + + pRequiredKeys->AddSubKey( pTeamReq ); + } + + virtual void GetOutputKeyValues( KeyValues *pOutputKeys ) OVERRIDE + { + CTFQuestRestriction::GetOutputKeyValues( pOutputKeys ); + + pOutputKeys->SetInt( "team_requirement", m_eTeamRestriction ); + pOutputKeys->SetString( "team_key", m_pszTeamKey ); + } + +private: + ETeamRestriction m_eTeamRestriction; + const char* m_pszTeamKey; +}; +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFQuestTeamRestriction, team_restriction, FIELD_TEAM ); + +//----------------------------------------------------------------------------- +// Purpose: quest map restriction +//----------------------------------------------------------------------------- +class CTFQuestMapRestriction : public CTFQuestRestriction +{ +public: + DECLARE_CLASS( CTFQuestMapRestriction, CTFQuestRestriction ) + + CTFQuestMapRestriction() + : m_pszMapName( NULL ) + { + } + + virtual const char *GetValueString() const OVERRIDE + { + return m_pszMapName; + } + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + if ( !CTFQuestRestriction::BInitFromKV( pKVItem, pVecErrors ) ) + return false; + + m_pszMapName = pKVItem->GetString( "value", NULL ); + SCHEMA_INIT_CHECK( m_pszMapName != NULL, "Missing map name." ); + + // TODO: validate map name against some white list here + + return true; + } + + virtual bool PassesRestrictions( IGameEvent *pEvent ) const OVERRIDE + { + return IsValidMap(); + } + + virtual bool IsValidForPlayer( const CTFPlayer *pOwner, InvalidReasonsContainer_t& invalidReasons ) const + { + BaseClass::IsValidForPlayer( pOwner, invalidReasons ); + + if ( !IsValidMap() ) + invalidReasons.m_bits.Set( INVALID_QUEST_REASON_WRONG_MAP ); + + return true; + } + + virtual void GetRequiredParamKeys( KeyValues *pRequiredKeys ) OVERRIDE + { + //const CUtlVector<SchemaMap_t>& maps = GetItemSchema()->GetMaps(); + + KeyValues *pMapKey = new KeyValues( "value" ); + pMapKey->SetString( "control_type", "text_entry" ); + /*for ( int i=0; i<maps.Count(); ++i ) + { + const char *pszMapName = maps[i].pszMapName; + pMapKey->AddSubKey( new KeyValues( pszMapName ) ); + }*/ + + pRequiredKeys->AddSubKey( pMapKey ); + } + + virtual void GetOutputKeyValues( KeyValues *pOutputKeys ) OVERRIDE + { + CTFQuestRestriction::GetOutputKeyValues( pOutputKeys ); + + pOutputKeys->SetString( "value", GetValueString() ); + } + +private: + bool IsValidMap() const + { +#ifdef CLIENT_DLL + const char *pszMapName = TFGameRules()->MapName(); +#else + const char *pszMapName = gpGlobals->mapname.ToCStr(); +#endif + + return !V_stricmp( m_pszMapName, pszMapName ); + } + + const char *m_pszMapName; +}; +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFQuestMapRestriction, map, FIELD_NONE ); + + +//----------------------------------------------------------------------------- +// Purpose: quest game type restriction +//----------------------------------------------------------------------------- +class CTFQuestGameTypeRestriction : public CTFQuestRestriction +{ +public: + DECLARE_CLASS( CTFQuestGameTypeRestriction, CTFQuestRestriction ) + + CTFQuestGameTypeRestriction() + : m_eGameType( (ETFGameType)0 ) + {} + + virtual const char *GetValueString() const OVERRIDE + { + return GetEnumGameTypeName( m_eGameType ); + } + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + if ( !CTFQuestRestriction::BInitFromKV( pKVItem, pVecErrors ) ) + return false; + + const char *pszVal = pKVItem->GetString( "value", NULL ); + SCHEMA_INIT_CHECK( pszVal != NULL, "Missing game_type name." ); + m_eGameType = GetGameTypeFromName( pszVal ); + SCHEMA_INIT_CHECK( m_eGameType != TF_GAMETYPE_UNDEFINED, "Invalid game_type name." ); + + return true; + } + + virtual bool PassesRestrictions( IGameEvent *pEvent ) const OVERRIDE + { + return TFGameRules()->GetGameType() == m_eGameType; + } + + virtual bool IsValidForPlayer( const CTFPlayer *pOwner, InvalidReasonsContainer_t& invalidReasons ) const + { + BaseClass::IsValidForPlayer( pOwner, invalidReasons ); + + if ( TFGameRules()->GetGameType() != m_eGameType ) + invalidReasons.m_bits.Set( INVALID_QUEST_REASON_WRONG_GAME_MODE ); + + return false; + } + + virtual void GetRequiredParamKeys( KeyValues *pRequiredKeys ) OVERRIDE + { + CTFQuestRestriction::GetRequiredParamKeys( pRequiredKeys ); + + KeyValues *pGameType = new KeyValues( "value" ); + for ( int i=0; i<TF_GAMETYPE_COUNT; ++i ) + { + const char *pszGameType = GetEnumGameTypeName( ETFGameType( i ) ); + pGameType->AddSubKey( new KeyValues( pszGameType ) ); + } + + pRequiredKeys->AddSubKey( pGameType ); + } + + virtual void GetOutputKeyValues( KeyValues *pOutputKeys ) OVERRIDE + { + CTFQuestRestriction::GetOutputKeyValues( pOutputKeys ); + + pOutputKeys->SetString( "value", GetValueString() ); + } + +private: + ETFGameType m_eGameType; +}; +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFQuestGameTypeRestriction, game_type, FIELD_NONE ); + + +static const char *s_loadout_position_names[] = +{ + // Weapons & Equipment + "LOADOUT_POSITION_PRIMARY", + "LOADOUT_POSITION_SECONDARY", + "LOADOUT_POSITION_MELEE", + "LOADOUT_POSITION_UTILITY", + "LOADOUT_POSITION_BUILDING", + "LOADOUT_POSITION_PDA", + "LOADOUT_POSITION_PDA2", + + // Wearables. If you add new wearable slots, make sure you add them to IsWearableSlot() below this. + "LOADOUT_POSITION_HEAD", + "LOADOUT_POSITION_MISC", + + // other + "LOADOUT_POSITION_ACTION", + + // More wearables, yay! + "LOADOUT_POSITION_MISC2", + + // taunts + "LOADOUT_POSITION_TAUNT", + "LOADOUT_POSITION_TAUNT2", + "LOADOUT_POSITION_TAUNT3", + "LOADOUT_POSITION_TAUNT4", + "LOADOUT_POSITION_TAUNT5", + "LOADOUT_POSITION_TAUNT6", + "LOADOUT_POSITION_TAUNT7", + "LOADOUT_POSITION_TAUNT8", + +#ifdef STAGING_ONLY + // Extra PDA mod slots + "LOADOUT_POSITION_PDA_ADDON1", + "LOADOUT_POSITION_PDA_ADDON2", + + "LOADOUT_POSITION_PDA3", + //LOADOUT_POSITION_MISC3, + //LOADOUT_POSITION_MISC4, + //LOADOUT_POSITION_MISC5, + //LOADOUT_POSITION_MISC6, + //LOADOUT_POSITION_MISC7, + //LOADOUT_POSITION_MISC8, + //LOADOUT_POSITION_MISC9, + //LOADOUT_POSITION_MISC10, + "LOADOUT_POSITION_BUILDING2", +#endif // STAGING_ONLY +}; +COMPILE_TIME_ASSERT( ARRAYSIZE( s_loadout_position_names ) == CLASS_LOADOUT_POSITION_COUNT ); + + +//----------------------------------------------------------------------------- +// Purpose: quest loadout position restriction +//----------------------------------------------------------------------------- +class CTFQuestLoadoutPositionRestriction : public CTFQuestBasePlayerRestriction +{ +public: + DECLARE_CLASS( CTFQuestLoadoutPositionRestriction, CTFQuestRestriction ) + + CTFQuestLoadoutPositionRestriction() + : m_eLoadoutPosition( LOADOUT_POSITION_INVALID ) + , m_pszLoadoutKey( NULL ) + {} + + virtual const char *GetValueString() const OVERRIDE + { + if ( m_eLoadoutPosition == LOADOUT_POSITION_INVALID ) + { + return "LOADOUT_POSITION_INVALID"; + } + return s_loadout_position_names[ m_eLoadoutPosition ]; + } + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + if ( !CTFQuestBasePlayerRestriction::BInitFromKV( pKVItem, pVecErrors ) ) + return false; + + const char *pszVal = pKVItem->GetString( "value", NULL ); + SCHEMA_INIT_CHECK( pszVal != NULL, "Missing loadout_position name." ); + m_eLoadoutPosition = GetLoadoutPositionByName( pszVal ); + SCHEMA_INIT_CHECK( m_eLoadoutPosition != LOADOUT_POSITION_INVALID, "Invalid loadout_position name." ); + + m_pszPlayerKey = pKVItem->GetString( "player_key", NULL ); + SCHEMA_INIT_CHECK( m_pszPlayerKey != NULL, "Missing \"player_key\" in loadout_position" ); + + m_pszLoadoutKey = pKVItem->GetString( "loadout_key", NULL ); + SCHEMA_INIT_CHECK( m_pszLoadoutKey != NULL, "Missing \"loadout_key\" in loadout_position" ); + + return true; + } + + virtual void GetRequiredParamKeys( KeyValues *pRequiredKeys ) OVERRIDE + { + CTFQuestBasePlayerRestriction::GetRequiredParamKeys( pRequiredKeys ); + + GetValidParamsKeyFromEvent( "loadout_key", GetConditionName(), m_pszEventName, pRequiredKeys ); + + KeyValues *pLoadoutPositions = new KeyValues( "value" ); + int iLoadoutPositionCount = ARRAYSIZE( s_loadout_position_names ); + for ( int i=0; i<iLoadoutPositionCount; ++i ) + { + const char *pszLoadoutPosition = s_loadout_position_names[i]; + pLoadoutPositions->AddSubKey( new KeyValues( pszLoadoutPosition ) ); + } + + pRequiredKeys->AddSubKey( pLoadoutPositions ); + } + + virtual void GetOutputKeyValues( KeyValues *pOutputKeys ) OVERRIDE + { + CTFQuestBasePlayerRestriction::GetOutputKeyValues( pOutputKeys ); + + pOutputKeys->SetString( "value", GetValueString() ); + + pOutputKeys->SetString( "loadout_key", m_pszLoadoutKey ); + } + +private: + loadout_positions_t GetLoadoutPositionByName( const char *pszLoadoutPositionName ) + { + int iLoadoutPositionCount = ARRAYSIZE( s_loadout_position_names ); + for ( int i=0; i<iLoadoutPositionCount; ++i ) + { + if ( FStrEq( pszLoadoutPositionName, s_loadout_position_names[i] ) ) + { + return loadout_positions_t(i); + } + } + + return LOADOUT_POSITION_INVALID; + } + + virtual bool BPlayerCheck( const CTFPlayer* pPlayer, IGameEvent *pEvent ) const OVERRIDE + { + int iClass = pPlayer->GetPlayerClass()->GetClassIndex(); + + item_definition_index_t iItemDef = pEvent->GetInt( m_pszLoadoutKey, INVALID_ITEM_DEF_INDEX ); + if ( iItemDef != INVALID_ITEM_DEF_INDEX ) + { + GameItemDefinition_t *pDef = ItemSystem()->GetStaticDataForItemByDefIndex( iItemDef ); + Assert( pDef ); + if ( pDef ) + { + return pDef->GetLoadoutSlot( iClass ) == m_eLoadoutPosition; + } + } + return false; + } + + loadout_positions_t m_eLoadoutPosition; + + const char* m_pszLoadoutKey; +}; +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFQuestLoadoutPositionRestriction, loadout_position, FIELD_PLAYER | FIELD_LOADOUT_POSITION ); + +//----------------------------------------------------------------------------- +// Purpose: quest player condition restriction +//----------------------------------------------------------------------------- +class CTFConditionQuestCondition : public CTFQuestRestriction +{ +public: + CTFConditionQuestCondition() + : m_pszKeyName( NULL ) + {} + + virtual const char *GetValueString() const OVERRIDE + { + return GetTFConditionName( m_eCondition ); + } + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + const char *pszKeyName = pKVItem->GetString( "condition_key", NULL ); + SCHEMA_INIT_CHECK( pszKeyName != NULL, "Missing key_to_lookup in condition" ); + + const char *pszConditionName = pKVItem->GetString( "value" ); + m_eCondition = GetTFConditionFromName( pszConditionName ); + SCHEMA_INIT_CHECK( m_eCondition != TF_COND_INVALID, "%s", CFmtStr( "Invalid %s restriction '%s' for quest objective", GetConditionName(), pszConditionName ).Get() ); + + return true; + } + + virtual void GetRequiredParamKeys( KeyValues *pRequiredKeys ) OVERRIDE + { + KeyValues *pConditionsKey = new KeyValues( "value" ); + for ( int i=0; i<TF_COND_LAST; ++i ) + { + const char *pszConditionName = GetTFConditionName( ETFCond( i ) ); + pConditionsKey->AddSubKey( new KeyValues( pszConditionName ) ); + } + + GetValidParamsKeyFromEvent( "condition_key", GetConditionName(), m_pszEventName, pRequiredKeys ); + + pRequiredKeys->AddSubKey( pConditionsKey ); + } + + virtual void GetOutputKeyValues( KeyValues *pOutputKeys ) OVERRIDE + { + pOutputKeys->SetString( "value", GetValueString() ); + pOutputKeys->SetString( "condition_key", m_pszKeyName ); + } + +private: + + virtual bool PassesRestrictions( IGameEvent *pEvent ) const OVERRIDE + { + return pEvent->GetInt( m_pszKeyName ) == m_eCondition; + } + + ETFCond m_eCondition; + const char *m_pszKeyName; +}; +REGISTER_QUEST_CONDITION_SUB_CLASS( CTFConditionQuestCondition, condition, FIELD_CONDITION ); + +CTFQuestRestriction *CreateRestrictionByName( const char *pszName, CTFQuestCondition* pParent ) +{ + CTFQuestRestriction *pNewRestriction = NULL; + auto idx = k_mapConditions.Find( pszName ); + if ( idx != k_mapConditions.InvalidIndex() ) + { + pNewRestriction = static_cast<CTFQuestRestriction*>( k_mapConditions[ idx ]->m_pfnQuestCreate() ); + pNewRestriction->SetFieldName( k_mapConditions[ idx ]->m_pszFieldName ); + pNewRestriction->SetTypeName( k_mapConditions.Key( idx ) ); + } + + if ( pNewRestriction ) + { + pNewRestriction->SetParent( pParent ); + + const char *pszEventName = pParent->GetEventName(); + + // in the case that parent has no event name (new node from editor) + // default the event name to the first valid event from the global event list + if ( !pszEventName ) + { + KeyValues *pQuestEvents = GetQuestEventsKeyValues(); + if ( pQuestEvents ) + { + const KeyValues *pFirstKey = pQuestEvents->GetFirstTrueSubKey(); + if ( pFirstKey ) + { + pszEventName = pFirstKey->GetName(); + } + } + } + + pNewRestriction->SetEventName( pszEventName ); + } + + AssertMsg( pNewRestriction, "Invalid quest restriction type '%s'", pszName ); + return pNewRestriction; +} + + +//----------------------------------------------------------------------------- +// Purpose: quest event +//----------------------------------------------------------------------------- +class CTFQuestEventListener : public CTFQuestEvaluator, public CGameEventListener +{ +public: + DECLARE_CLASS( CTFQuestEventListener, CTFQuestEvaluator ) + + CTFQuestEventListener() + { + m_pRestrictions = NULL; + m_pszEventName = NULL; + m_pszOverrideScoreKeyName = NULL; + } + + virtual ~CTFQuestEventListener() + { + if ( m_pRestrictions ) + { + delete m_pRestrictions; + } + } + + virtual const char *GetConditionName() const OVERRIDE { return "event_listener"; } + virtual const char *GetValueString() const OVERRIDE { return m_pszEventName; } + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + if ( !CTFQuestEvaluator::BInitFromKV( pKVItem, pVecErrors ) ) + return false; + + m_pszEventName = pKVItem->GetString( "event_name", NULL ); + SCHEMA_INIT_CHECK( m_pszEventName != NULL, "%s", CFmtStr( "Invalid %s condition. Missing 'event_name'", GetConditionName() ).Get() ); +#ifdef GAME_DLL // Only the server needs to listen for events + ListenForGameEvent( m_pszEventName ); +#endif + + m_pszOverrideScoreKeyName = pKVItem->GetString( "score_key_name", "none" ); + SCHEMA_INIT_CHECK( m_pszOverrideScoreKeyName != NULL, "Missing score_key_name" ); + + FOR_EACH_TRUE_SUBKEY( pKVItem, pSubKey ) + { + SCHEMA_INIT_CHECK( !m_pRestrictions, "%s", CFmtStr( "Too many input for operator '%s'.", GetConditionName() ).Get() ); + + const char *pszType = pSubKey->GetString( "type" ); + m_pRestrictions = CreateRestrictionByName( pszType, this ); + SCHEMA_INIT_CHECK( m_pRestrictions != NULL, "%s", CFmtStr( "Failed to create quest restriction name '%s' for '%s'", pszType, GetConditionName() ).Get() ); + + SCHEMA_INIT_CHECK( m_pRestrictions->BInitFromKV( pSubKey, pVecErrors ), "Failed to init from KeyValues" ); + } + + return true; + } + + virtual bool IsValidForPlayer( const CTFPlayer *pOwner, InvalidReasonsContainer_t& invalidReasons ) const + { + BaseClass::IsValidForPlayer( pOwner, invalidReasons ); + + int nNumFound = 0; + for ( int i = 1; i <= gpGlobals->maxClients && nNumFound < s_nMinConnectedPlayersForQuestProgress; ++i ) + { +#ifdef CLIENT_DLL + IGameResources *gr = GameResources(); + if ( !gr || !gr->IsConnected( i ) ) + continue; +#else + CBasePlayer* pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) ); + if ( !pPlayer ) + continue; +#endif + + ++nNumFound; + } + + if ( nNumFound < s_nMinConnectedPlayersForQuestProgress ) + invalidReasons.m_bits.Set( INVALID_QUEST_REASON_NOT_ENOUGH_PLAYERS ); + + if ( m_pRestrictions ) + { + m_pRestrictions->IsValidForPlayer( pOwner, invalidReasons ); + } + + return true; + } + + virtual void FireGameEvent( IGameEvent *pEvent ) OVERRIDE + { + // This can happen when the player's SteamID isn't setup yet after a + // disconnect -> reconnect + if ( GetQuestOwner() == NULL ) + return; + + InvalidReasonsContainer_t invalidReasons; + IsValidForPlayer( GetQuestOwner(), invalidReasons ); + + if ( !invalidReasons.m_bits.IsAllClear() ) + return; + + const char *pszEventName = pEvent->GetName(); + if ( FStrEq( m_pszEventName, pszEventName ) ) + { + if ( !m_pRestrictions || m_pRestrictions->PassesRestrictions( pEvent ) ) + { + int nScoreOverride = m_pszOverrideScoreKeyName ? pEvent->GetInt( m_pszOverrideScoreKeyName, 1 ) : 1; + + EvaluateCondition( this, nScoreOverride ); + } + } + } + + virtual void EvaluateCondition( CTFQuestEvaluator *pSender, int nScore ) OVERRIDE + { + Assert( GetParent() && GetParent()->IsEvaluator() ); + assert_cast< CTFQuestEvaluator* >( GetParent() )->EvaluateCondition( pSender, nScore ) ; + } + + + virtual void ResetCondition() OVERRIDE + { + // DO NOTHING + } + + CTFQuestCondition* AddChildByName( const char *pszChildName ) OVERRIDE + { + if ( m_pRestrictions ) + { + Assert( m_pRestrictions == NULL ); + return NULL; + } + + m_pRestrictions = CreateRestrictionByName( pszChildName, this ); + Assert( m_pRestrictions ); + return m_pRestrictions; + } + + virtual int GetChildren( CUtlVector< CTFQuestCondition* >& vecChildren ) OVERRIDE + { + if ( m_pRestrictions ) + { + vecChildren.AddToTail( m_pRestrictions ); + return vecChildren.Count(); + } + + return 0; + } + + bool RemoveAndDeleteChild( CTFQuestCondition *pChild ) OVERRIDE + { + bool bRemoved = m_pRestrictions == pChild; + Assert( bRemoved ); + + if ( bRemoved ) + { + delete m_pRestrictions; + m_pRestrictions = NULL; + } + + return bRemoved; + } + + virtual void GetRequiredParamKeys( KeyValues *pRequiredKeys ) OVERRIDE + { + CTFQuestEvaluator::GetRequiredParamKeys( pRequiredKeys ); + + KeyValues *pQuestEvents = GetQuestEventsKeyValues(); + if ( pQuestEvents ) + { + KeyValues *pScoreKeys = new KeyValues( "score_key_name" ); + KeyValues *pEventsKey = new KeyValues( "event_name" ); + FOR_EACH_TRUE_SUBKEY( pQuestEvents, pSubKey ) + { + const char *pszEventName = pSubKey->GetName(); + pEventsKey->AddSubKey( new KeyValues( pszEventName ) ); + + if ( m_pszEventName && FStrEq( pszEventName, m_pszEventName ) ) + { + KeyValues* pScoreSubKeys = pSubKey->FindKey( "score_keys" ); + if ( pScoreSubKeys ) + { + FOR_EACH_TRUE_SUBKEY( pScoreSubKeys, pScore ) + { + pScoreKeys->AddSubKey( new KeyValues( pScore->GetName() ) ); + } + } + } + } + + pRequiredKeys->AddSubKey( pEventsKey ); + if ( pScoreKeys->GetFirstTrueSubKey() ) + { + pScoreKeys->AddSubKey( new KeyValues( "none" ) ); + pRequiredKeys->AddSubKey( pScoreKeys ); + } + } + + } + + virtual void GetOutputKeyValues( KeyValues *pOutputKeys ) OVERRIDE + { + CTFQuestEvaluator::GetOutputKeyValues( pOutputKeys ); + + pOutputKeys->SetString( "event_name", GetValueString() ); + pOutputKeys->SetString( "score_key_name", m_pszOverrideScoreKeyName ); + } + + virtual const char* GetEventName() const OVERRIDE + { + return m_pszEventName; + } + + virtual void SetEventName( const char *pszEventName ) OVERRIDE + { + m_pszEventName = pszEventName; + } + + virtual int GetMaxInputCount() const { return 1; } + +protected: + + + const char *m_pszEventName; + const char *m_pszOverrideScoreKeyName; + CTFQuestRestriction *m_pRestrictions; +}; + + +//----------------------------------------------------------------------------- +// Purpose: count evaluator +//----------------------------------------------------------------------------- +class CTFQuestCountEvaluator : public CTFQuestEvaluator +{ +public: + DECLARE_CLASS( CTFQuestCountEvaluator, CTFQuestEvaluator ) + + virtual ~CTFQuestCountEvaluator() + { + m_vecChildren.PurgeAndDeleteElements(); + } + + virtual const char *GetConditionName() const OVERRIDE { return "counter"; } + + virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) OVERRIDE + { + if ( !CTFQuestEvaluator::BInitFromKV( pKVItem, pVecErrors ) ) + return false; + + m_nStart = m_nCount = pKVItem->GetInt( "start" ); + m_nEnd = pKVItem->GetInt( "end" ); + + FOR_EACH_TRUE_SUBKEY( pKVItem, pSubKey ) + { + const char *pszType = pSubKey->GetString( "type" ); + CTFQuestEvaluator *pNewCond = assert_cast< CTFQuestEvaluator* >( CreateEvaluatorByName( pszType, this ) ); + SCHEMA_INIT_CHECK( pNewCond && pNewCond->BInitFromKV( pSubKey, pVecErrors ), "Failed to init from KeyValues" ); + + const char *pszAction = pSubKey->GetString( "action", NULL ); + SCHEMA_INIT_CHECK( pszAction != NULL, "Missing action key" ); + pNewCond->SetAction( pszAction ); + + m_vecChildren.AddToTail( pNewCond ); + } + + return true; + } + + virtual bool IsValidForPlayer( const CTFPlayer *pOwner, InvalidReasonsContainer_t& invalidReasons ) const + { + BaseClass::IsValidForPlayer( pOwner, invalidReasons ); + + bool bIsForLocalPlayer = false; + FOR_EACH_VEC( m_vecChildren, i ) + { + bIsForLocalPlayer |= m_vecChildren[i]->IsValidForPlayer( pOwner, invalidReasons ); + } + + return bIsForLocalPlayer; + } + + virtual void EvaluateCondition( CTFQuestEvaluator *pSender, int nScore ) OVERRIDE + { + const char *pszAction = pSender->GetAction(); + if ( FStrEq( pszAction, "increment" ) ) + { + m_nCount += nScore; + } + else if ( FStrEq( pszAction, "decrement" ) ) + { + m_nCount -= nScore; + // Don't dip below 0! + m_nCount = Max( 0, m_nCount ); + } + else if ( FStrEq( pszAction, "reset" ) ) + { + m_nCount = 0; + } + else + { + AssertMsg( 0, "Invalid evaluation condition '%s' for '%s'", pSender->GetConditionName(), GetConditionName() ); + } + + // Check how many time over we've scored + int nNumScored = m_nCount / m_nEnd; + // Store the remainded + m_nCount -= ( nNumScored * m_nEnd ); + + if ( nNumScored > 0 ) + { + Assert( GetParent() && GetParent()->IsEvaluator() ); + assert_cast< CTFQuestEvaluator* >( GetParent() )->EvaluateCondition( this, nNumScored ) ; + } + } + + virtual void ResetCondition() OVERRIDE + { + m_nCount = 0; + + FOR_EACH_VEC( m_vecChildren, i ) + { + m_vecChildren[i]->ResetCondition(); + } + } + + enum ECounterSubType + { + COUNTER_INCREMENT = 0, + COUNTER_DECREMENT, + COUNTER_RESET, + + COUNTER_TYPE_COUNT + }; + + + virtual int GetChildrenSubTypeCount() const { return COUNTER_TYPE_COUNT; } + + virtual int GetChildren( CUtlVector< CTFQuestCondition* >& vecChildren ) OVERRIDE + { + for ( int i=0; i<m_vecChildren.Count(); ++i ) + { + vecChildren.AddToTail( m_vecChildren[i] ); + } + return vecChildren.Count(); + } + + bool RemoveAndDeleteChild( CTFQuestCondition *pChild ) OVERRIDE + { + CTFQuestEvaluator *pEvaluatorChild = assert_cast< CTFQuestEvaluator* >( pChild ); + + bool bRemoved = m_vecChildren.FindAndFastRemove( pEvaluatorChild ); + Assert( bRemoved ); + + if ( bRemoved ) + { + delete pChild; + } + + return bRemoved; + } + + CTFQuestCondition* AddChildByName( const char *pszChildName ) OVERRIDE + { + CTFQuestEvaluator *pNewEvaluator = CreateEvaluatorByName( pszChildName, this ); + if ( pNewEvaluator ) + { + m_vecChildren.AddToTail( pNewEvaluator ); + } + return pNewEvaluator; + } + + virtual int GetMaxInputCount() const { return s_nMaxInputCount; } + + virtual void GetRequiredParamKeys( KeyValues *pRequiredKeys ) OVERRIDE + { + CTFQuestEvaluator::GetRequiredParamKeys( pRequiredKeys ); + + KeyValues* pKVCounts = pRequiredKeys->CreateNewKey(); + pKVCounts->SetName( "end" ); + pKVCounts->SetString( "control_type", "text_entry" ); + } + + virtual void GetOutputKeyValues( KeyValues *pOutputKeys ) OVERRIDE + { + CTFQuestEvaluator::GetOutputKeyValues( pOutputKeys ); + + pOutputKeys->SetInt( "end", m_nEnd ); + } + + virtual void GetValidChildren( CUtlVector< const char* >& vecOutValidChildren ) const OVERRIDE + { + vecOutValidChildren.AddToTail( "event_listener" ); + vecOutValidChildren.AddToTail( "counter" ); + } + +private: + + CUtlVector< CTFQuestEvaluator* > m_vecChildren; + int m_nCount; + int m_nStart; + int m_nEnd; +}; + + +void CTFQuestEvaluator::GetRequiredParamKeys( KeyValues *pRequiredKeys ) +{ + if ( GetParent() && dynamic_cast< CTFQuestCountEvaluator* >( GetParent() ) ) + { + KeyValues *pActionsKey = new KeyValues( "action" ); + pActionsKey->AddSubKey( new KeyValues( "increment" ) ); + pActionsKey->AddSubKey( new KeyValues( "decrement" ) ); + pActionsKey->AddSubKey( new KeyValues( "reset" ) ); + + pRequiredKeys->AddSubKey( pActionsKey ); + } +} + + +CTFQuestEvaluator *CreateEvaluatorByName( const char *pszName, CTFQuestCondition* pParent ) +{ + CTFQuestEvaluator *pNewEvaluator = NULL; + if ( FStrEq( pszName, "event_listener" ) ) + { + pNewEvaluator = new CTFQuestEventListener; + } + else if ( FStrEq( pszName, "counter" ) ) + { + pNewEvaluator = new CTFQuestCountEvaluator; + } + + if ( pNewEvaluator ) + { + pNewEvaluator->SetParent( pParent ); + } + + AssertMsg( pNewEvaluator, "Invalid quest evaluator type '%s'", pszName ); + return pNewEvaluator; +} |