//========= 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;
}