1
0
mirror of https://github.com/alliedmodders/hl2sdk.git synced 2025-01-10 10:59:39 +08:00
hl2sdk/game/missionchooser/layout_system/tilegen_mission_preprocessor.cpp
2010-07-22 01:46:14 -05:00

203 lines
6.0 KiB
C++

//============ Copyright (c) Valve Corporation, All rights reserved. ============
//
// Definition of a class to pre-process mission files and perform
// rule substitution.
//
//===============================================================================
#include "KeyValues.h"
#include "tilegen_core.h"
#include "tilegen_enum.h"
#include "tilegen_rule.h"
#include "tilegen_mission_preprocessor.h"
//////////////////////////////////////////////////////////////////////////
// Public Implementation
//////////////////////////////////////////////////////////////////////////
CTilegenMissionPreprocessor::~CTilegenMissionPreprocessor()
{
m_Rules.PurgeAndDeleteElements();
}
const CTilegenRule *CTilegenMissionPreprocessor::FindRule( const char *pRuleName ) const
{
for ( int i = 0; i < m_Rules.Count(); ++ i )
{
if ( Q_stricmp( m_Rules[i]->GetName(), pRuleName ) == 0 )
{
return m_Rules[i];
}
}
return NULL;
}
const CTilegenEnum *CTilegenMissionPreprocessor::FindEnum( const char *pEnumName ) const
{
for ( int i = 0; i < m_Enums.Count(); ++ i )
{
if ( Q_stricmp( m_Enums[i]->GetName(), pEnumName ) == 0 )
{
return m_Enums[i];
}
}
return NULL;
}
bool CTilegenMissionPreprocessor::ParseAndStripRules( KeyValues *pRulesKV )
{
for ( KeyValues *pSubKey = pRulesKV->GetFirstSubKey(); pSubKey != NULL; ) // sub-key gets advanced in the loop
{
if ( Q_stricmp( pSubKey->GetName(), "rule" ) == 0 )
{
KeyValues *pKeyValuesToRemove = pSubKey;
pSubKey = pSubKey->GetNextKey();
pRulesKV->RemoveSubKey( pKeyValuesToRemove );
CTilegenRule *pNewRule = new CTilegenRule();
if ( !pNewRule->LoadFromKeyValues( pKeyValuesToRemove ) )
{
delete pNewRule;
return false;
}
m_Rules.AddToTail( pNewRule );
}
else if ( Q_stricmp( pSubKey->GetName(), "enum" ) == 0 )
{
KeyValues *pKeyValuesToRemove = pSubKey;
pSubKey = pSubKey->GetNextKey();
pRulesKV->RemoveSubKey( pKeyValuesToRemove );
CTilegenEnum *pNewEnum = new CTilegenEnum();
if ( !pNewEnum->LoadFromKeyValues( pKeyValuesToRemove ) )
{
delete pNewEnum;
return false;
}
m_Enums.AddToTail( pNewEnum );
}
else
{
pSubKey = pSubKey->GetNextKey();
}
}
return true;
}
bool CTilegenMissionPreprocessor::SubstituteRules( KeyValues *pKeyValues )
{
m_nUniqueIndex = 0;
for ( KeyValues *pSubKey = pKeyValues->GetFirstSubKey(); pSubKey != NULL; )
{
KeyValues *pNextSubKey = pSubKey->GetNextKey();
// Rules should have been stripped out.
Assert( Q_stricmp( pSubKey->GetName(), "rule" ) != 0 );
if ( !SubstituteRules_Recursive( pKeyValues, pSubKey ) )
{
return false;
}
// pSubKey may have been deleted during the call and is no longer valid
pSubKey = pNextSubKey;
}
return true;
}
//////////////////////////////////////////////////////////////////////////
// Private Implementation
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
// During macro substitution, there are some meta-variables (starting with
// a '%' sign, e.g. %UniqueName) which are substituted for values
// using special logic in this function.
//-----------------------------------------------------------------------------
void CTilegenMissionPreprocessor::EvaluateMetaVariables( KeyValues *pKeyValues )
{
const char *pString = pKeyValues->GetString();
const char *pFind = "%Unique";
if ( Q_stristr( pString, pFind ) != NULL )
{
char uniqueNumber[15];
Q_snprintf( uniqueNumber, 15, "%02d", m_nUniqueIndex );
char outputString[MAX_TILEGEN_IDENTIFIER_LENGTH];
Q_StrSubst( pString, pFind, uniqueNumber, outputString, MAX_TILEGEN_IDENTIFIER_LENGTH );
pKeyValues->SetString( NULL, outputString );
}
}
//-----------------------------------------------------------------------------
// Recursively looks for rule_instance blocks, finds the corresponding rule,
// performs the substitution recursively, and deletes the old rule_instance
// block.
//-----------------------------------------------------------------------------
bool CTilegenMissionPreprocessor::SubstituteRules_Recursive( KeyValues *pParent, KeyValues *pKeyValues )
{
if ( Q_stricmp( pKeyValues->GetName(), "rule_instance" ) == 0 )
{
if ( pKeyValues->GetBool( "disabled", false ) )
{
// The rule_instance has been disabled from the UI, ignore it and generate nothing in its place.
pParent->RemoveSubKey( pKeyValues );
pKeyValues->deleteThis();
}
else
{
const char *pRuleName = pKeyValues->GetString( "name", "" );
if ( pRuleName[0] == '\0' )
{
Log_Warning( LOG_TilegenLayoutSystem, "Rule instance must have a valid 'name' key.\n" );
return false;
}
const CTilegenRule *pRule = FindRule( pRuleName );
if ( pRule == NULL )
{
Log_Warning( LOG_TilegenLayoutSystem, "Rule %s not found in rule list.\n", pRuleName );
return false;
}
// Each instantiated rule needs a unique index to generate unique names.
++ m_nUniqueIndex;
KeyValues *pInstantiatedKV = pRule->InstantiateRule( pKeyValues, this );
if ( pInstantiatedKV == NULL )
{
Log_Warning( LOG_TilegenLayoutSystem, "Error instantiating rule %s.\n", pRuleName );
return false;
}
pParent->SwapSubKey( pKeyValues, pInstantiatedKV );
// Recursively apply rules as needed.
if ( !SubstituteRules_Recursive( pParent, pInstantiatedKV ) )
{
return false;
}
pParent->ElideSubKey( pInstantiatedKV );
// Delete the rule_instance
pKeyValues->deleteThis();
}
}
else
{
for ( KeyValues *pSubKey = pKeyValues->GetFirstSubKey(); pSubKey != NULL; )
{
KeyValues *pNextSubKey = pSubKey->GetNextKey();
if ( !SubstituteRules_Recursive( pKeyValues, pSubKey ) )
{
return false;
}
// pSubKey may have been deleted during the call and is no longer valid
pSubKey = pNextSubKey;
}
}
return true;
}