summaryrefslogtreecommitdiff
path: root/game/shared/tf/tf_quest_restriction.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/tf/tf_quest_restriction.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/shared/tf/tf_quest_restriction.cpp')
-rw-r--r--game/shared/tf/tf_quest_restriction.cpp2700
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;
+}