1
0
mirror of https://github.com/alliedmodders/hl2sdk.git synced 2025-01-10 02:58:48 +08:00
hl2sdk/game/missionchooser/layout_system/tilegen_class_factories.h
2010-07-22 01:46:14 -05:00

303 lines
14 KiB
C++

//============ Copyright (c) Valve Corporation, All rights reserved. ============
//
// Boilerplate template code to generically instantiate tilegen
// classes by name and parse them from KeyValues files.
//
//===============================================================================
#ifndef TILEGEN_CLASS_FACTORIES_H
#define TILEGEN_CLASS_FACTORIES_H
#if defined( COMPILER_MSVC )
#pragma once
#endif
#include "utlvector.h"
#include "KeyValues.h"
#include "tilegen_class_interfaces.h"
//-----------------------------------------------------------------------------
// This function must be called at least once before any tilegen
// expression, action, or range classes are parsed.
// Subsequent calls are ignored.
//-----------------------------------------------------------------------------
void RegisterAllTilegenClasses();
//-----------------------------------------------------------------------------
// The most generic, abstract base form of a tilegen class factory.
// Each derived template instantiation is capable of instantiating a single
// kind of tilegen class derived from TTilegenInterface.
//
// TTilegenInterface - the type of interface this class factory returns,
// e.g. ITilegenExpression< int >, ITilegenRange< const CExit * >,
// ITilegenAction
//-----------------------------------------------------------------------------
template< typename TTilegenInterface >
class ITilegenClassFactory
{
public:
//-----------------------------------------------------------------------------
// Creates an instance of the tilegen class this factory is templated on.
//-----------------------------------------------------------------------------
virtual TTilegenInterface * CreateInstance() = 0;
//-----------------------------------------------------------------------------
// Gets the friendly string name of the tilegen class this factory produces.
//-----------------------------------------------------------------------------
virtual const char *GetName() = 0;
static TTilegenInterface *ReadLiteralValue( KeyValues *pKeyValues )
{
// Only some tilegen classes can be loaded this way.
Log_Warning( LOG_TilegenLayoutSystem, "Tilegen class does not support literal values.\n" );
return NULL;
}
};
// Annoying workaround due to template specialization rules.
// I want these implemented in a .cpp elsewhere but if I simply do a template prototype and define the function elsewhere,
// it won't specialize. So I need a fully implemented template specialization to call these functions.
ITilegenExpression< int > *ReadLiteralIntValue( KeyValues *pKeyValues );
ITilegenExpression< bool > *ReadLiteralBoolValue( KeyValues *pKeyValues );
ITilegenExpression< float > *ReadLiteralFloatValue( KeyValues *pKeyValues );
ITilegenExpression< const char * > *ReadLiteralStringValue( KeyValues *pKeyValues );
// Class factory specializations to handle parsing literal values...
ITilegenExpression< int > *ITilegenClassFactory< ITilegenExpression< int > >::ReadLiteralValue( KeyValues *pKeyValues )
{
return ReadLiteralIntValue( pKeyValues );
}
ITilegenExpression< bool > *ITilegenClassFactory< ITilegenExpression< bool > >::ReadLiteralValue( KeyValues *pKeyValues )
{
return ReadLiteralBoolValue( pKeyValues );
}
ITilegenExpression< float > *ITilegenClassFactory< ITilegenExpression< float > >::ReadLiteralValue( KeyValues *pKeyValues )
{
return ReadLiteralFloatValue( pKeyValues );
}
ITilegenExpression< char const * > *ITilegenClassFactory< ITilegenExpression< char const * > >::ReadLiteralValue( KeyValues *pKeyValues )
{
return ReadLiteralStringValue( pKeyValues );
}
//-----------------------------------------------------------------------------
// Implementation of a generic tilegen class factory which
// can be specialized to implement either expression, action, and range class
// factories.
//
// TTilegenInterface - the type of interface this class factory produces,
// e.g. ITilegenExpression< int >, ITilegenRange< const CExit * >,
// ITilegenAction
// TTilegenClass - the specific class (which must derive from
// TTilegenInterface) that this factory actually produduces,
// e.g. CTilegenExpression_Add, CTilegenRange_NewOpenExits,
// CTilegenAction_SwitchState
//-----------------------------------------------------------------------------
template< typename TTilegenInterface, typename TTilegenClass >
class CTilegenClassFactory : public ITilegenClassFactory< TTilegenInterface >
{
public:
virtual TTilegenInterface * CreateInstance() { return new TTilegenClass(); }
virtual const char *GetName();
};
//-----------------------------------------------------------------------------
// Class factory type to produce tilegen expressions.
//
// TExpressionValue - the type the expression class returns, e.g. bool, int
// TExpressionClass - the specific type of the expression, e.g.
// CTilegenExpression_Add
//
// TExpressionClass must derive from ITilegenExpression< TExpressionValue >
//-----------------------------------------------------------------------------
template< typename TExpressionValue, typename TExpressionClass >
class CTilegenExpressionClassFactory : public CTilegenClassFactory< ITilegenExpression< TExpressionValue >, TExpressionClass >
{
public:
// Must be implemented for each specialization of CTilegenExpressionClassFactory
// (see macro IMPLEMENT_TILEGEN_EXPRESSION in tilegen_class_factories.cpp).
virtual const char *GetName();
};
//-----------------------------------------------------------------------------
// Class factory type to produce tilegen actions.
//
// TActionClass - the specific type of the action, e.g.
// CTilegenAction_SwitchState
//
// TActionClass must derive from ITilegenAction
//-----------------------------------------------------------------------------
template< typename TActionClass >
class CTilegenActionClassFactory : public CTilegenClassFactory< ITilegenAction, TActionClass >
{
public:
// Must be implemented for each specialization of CTilegenActionClassFactory
// (see macro IMPLEMENT_TILEGEN_ACTION in tilegen_class_factories.cpp).
virtual const char *GetName();
};
//-----------------------------------------------------------------------------
// Class factory type to produce tilegen ranges.
//
// TElementValue - the type of each element in the range, e.g. const CExit *
// TRangeClass - the specific type of the range, e.g.
// CTilegenRange_NewOpenExits, CTilegenRange_ClosedExits
//
// TRangeClass must derive from ITilegenRange< TElementValue >
//-----------------------------------------------------------------------------
template< typename TElementValue, typename TRangeClass >
class CTilegenRangeClassFactory : public CTilegenClassFactory< ITilegenRange< TElementValue >, TRangeClass >
{
public:
// Must be implemented for each specialization of CTilegenRangeClassFactory
// (see macro IMPLEMENT_TILEGEN_RANGE in tilegen_class_factories.cpp).
virtual const char *GetName();
};
//-----------------------------------------------------------------------------
// A class registry which is capable of instantiating any class
// which has a class factory instance registered with it.
// A list of class factories is tracked and searched by string when
// an instance of a particular type is requested.
//
// TTilegenInterface - base interface class which is returned when a
// new instance is created.
//-----------------------------------------------------------------------------
template< typename TTilegenInterface >
class CTilegenClassRegistry
{
typedef ITilegenClassFactory< TTilegenInterface > TTilegenClassFactoryInterface;
public:
// @TODO: do we need cleanup the CUtlVector for each instance of this class?
// If so, we're better off making implicit linked lists, since
// each factory class is statically allocated..
//-----------------------------------------------------------------------------
// Registers a new factory instance which can produce a specific type of
// tilegen class.
//-----------------------------------------------------------------------------
static void AddFactory( TTilegenClassFactoryInterface *pFactory )
{
m_FactoryList.AddToTail( pFactory );
}
//-----------------------------------------------------------------------------
// Creates an instance of the class specified by pClassName
//-----------------------------------------------------------------------------
static TTilegenInterface *CreateInstance( const char *pClassName )
{
for ( int i = 0; i < m_FactoryList.Count(); ++ i )
{
if ( Q_stricmp( m_FactoryList[i]->GetName(), pClassName ) == 0 )
{
return m_FactoryList[i]->CreateInstance();
}
}
Log_Warning( LOG_TilegenLayoutSystem, "Class %s not found in %s. Check the class name spelling and the context in which it is used.\n", pClassName, m_pFactoryName );
return NULL;
}
private:
static CUtlVector< TTilegenClassFactoryInterface * > m_FactoryList;
static const char *m_pFactoryName;
};
//-----------------------------------------------------------------------------
// Parses KeyValues data and instantiates the appropriate class
//
// bCreateEmptyInstance is used in the rare case where the caller needs
// the object created but not actually loaded from KeyValues.
// (One example of this is CTilegenExpression_MapReduce, which uses binary
// operators to fold/reduce values together but does not need any state
// in the operators to function.
//-----------------------------------------------------------------------------
template< typename TTilegenInterface >
static TTilegenInterface *CreateFromKeyValues( KeyValues *pKeyValues, bool bCreateEmptyInstance = false )
{
if ( pKeyValues->GetFirstSubKey() == NULL )
{
// Code to handle literal/inline values
return ITilegenClassFactory< TTilegenInterface >::ReadLiteralValue( pKeyValues );
}
const char *pClassName = pKeyValues->GetString( "class", NULL );
if ( pClassName == NULL )
{
Log_Warning( LOG_TilegenLayoutSystem, "No class name specified for class instantiation in key values.\n" );
return NULL;
}
TTilegenInterface *pNewClass = CTilegenClassRegistry< TTilegenInterface >::CreateInstance( pClassName );
if ( pNewClass != NULL && !bCreateEmptyInstance && !pNewClass->LoadFromKeyValues( pKeyValues ) )
{
delete pNewClass;
pNewClass = NULL;
}
return pNewClass;
}
//-----------------------------------------------------------------------------
// Creates the appropriate tilegen class from the specified sub-key
// of KeyValues data.
//-----------------------------------------------------------------------------
template< typename TTilegenClass >
bool CreateFromKeyValuesBlock( KeyValues *pParentKV, const char *pKeyName, const char *pParentClassName, TTilegenClass **ppClass, bool bOptional = false, bool bCreateEmptyInstance = false )
{
KeyValues *pSubKey = pParentKV->FindKey( pKeyName );
*ppClass = NULL;
if ( pSubKey == NULL )
{
if ( bOptional )
{
return true;
}
Log_Warning( LOG_TilegenLayoutSystem, "Could not load sub-key '%s' for parent class '%s'.\n", pKeyName, pParentClassName );
return false;
}
*ppClass = CreateFromKeyValues< TTilegenClass >( pSubKey, bCreateEmptyInstance );
if ( *ppClass == NULL )
{
Log_Warning( LOG_TilegenLayoutSystem, "Could not create class for sub-key '%s' for parent class '%s'.\n", pKeyName, pParentClassName );
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Creates the appropriate tilegen expression class from the specified sub-key
// of KeyValues data.
//-----------------------------------------------------------------------------
template< typename TExpressionValue >
bool CreateExpressionFromKeyValuesBlock( KeyValues *pParentKV, const char *pKeyName, const char *pParentClassName, ITilegenExpression< TExpressionValue > **ppClass, bool bOptional = false, bool bCreateEmptyInstance = false )
{
return CreateFromKeyValuesBlock< ITilegenExpression< TExpressionValue > >( pParentKV, pKeyName, pParentClassName, ppClass, bOptional, bCreateEmptyInstance );
}
//-----------------------------------------------------------------------------
// Creates the appropriate tilegen action and boolean expression class
// (if present) from the specified KeyValues data.
//-----------------------------------------------------------------------------
bool CreateActionAndCondition( KeyValues *pKeyValues, ITilegenAction **ppAction, ITilegenExpression< bool > **ppCondition );
//-----------------------------------------------------------------------------
// Creates the appropriate tilegen action and boolean expression class
// (if present) from the specified sub-key of KeyValues data.
//-----------------------------------------------------------------------------
bool CreateActionAndConditionFromKeyValuesBlock( KeyValues *pParentKV, const char *pKeyName, const char *pParentClassName, ITilegenAction **ppAction, ITilegenExpression< bool > **ppCondition );
//-----------------------------------------------------------------------------
// Creates the appropriate tilegen range class from the specified sub-key
// of KeyValues data.
//-----------------------------------------------------------------------------
template< typename TElementValue >
bool CreateRangeFromKeyValuesBlock( KeyValues *pParentKV, const char *pKeyName, const char *pParentClassName, ITilegenRange< TElementValue > **ppClass )
{
return CreateFromKeyValuesBlock< ITilegenRange< TElementValue > >( pParentKV, pKeyName, pParentClassName, ppClass );
}
#endif // TILEGEN_CLASS_FACTORIES_H