mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-31 22:49:19 +00:00
upload "kind" alien swarm
This commit is contained in:
@@ -0,0 +1,584 @@
|
||||
//============ Copyright (c) Valve Corporation, All rights reserved. ============
|
||||
//
|
||||
// Definitions for the rule- and state-based level generation system.
|
||||
//
|
||||
//===============================================================================
|
||||
|
||||
#include "MapLayout.h"
|
||||
#include "Room.h"
|
||||
#include "tilegen_class_factories.h"
|
||||
#include "tilegen_mission_preprocessor.h"
|
||||
#include "tilegen_listeners.h"
|
||||
#include "tilegen_ranges.h"
|
||||
#include "tilegen_layout_system.h"
|
||||
#include "asw_npcs.h"
|
||||
|
||||
ConVar tilegen_break_on_iteration( "tilegen_break_on_iteration", "-1", FCVAR_CHEAT, "If set to a non-negative value, tilegen will break at the start of iteration #N if a debugger is attached." );
|
||||
DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_TilegenLayoutSystem, "TilegenLayoutSystem", 0, LS_MESSAGE, Color( 192, 255, 192, 255 ) );
|
||||
|
||||
void AddListeners( CLayoutSystem *pLayoutSystem )
|
||||
{
|
||||
// @TODO: need a better mechanism to detect which of these listeners are required. For now, add them all.
|
||||
pLayoutSystem->AddListener( new CTilegenListener_NumTilesPlaced() );
|
||||
}
|
||||
|
||||
CTilegenState *CTilegenStateList::FindState( const char *pStateName )
|
||||
{
|
||||
for ( int i = 0; i < m_States.Count(); ++ i )
|
||||
{
|
||||
if ( Q_stricmp( pStateName, m_States[i]->GetStateName() ) == 0 )
|
||||
{
|
||||
return m_States[i];
|
||||
}
|
||||
CTilegenState *pState = m_States[i]->GetStateList()->FindState( pStateName );
|
||||
if ( pState != NULL )
|
||||
{
|
||||
return pState;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CTilegenState *CTilegenStateList::GetNextState( CTilegenState *pState )
|
||||
{
|
||||
int i;
|
||||
for ( i = 0; i < m_States.Count(); ++ i )
|
||||
{
|
||||
if ( m_States[i] == pState )
|
||||
break;
|
||||
}
|
||||
|
||||
if ( i < ( m_States.Count() - 1 ) )
|
||||
{
|
||||
return m_States[i + 1];
|
||||
}
|
||||
else if ( i == m_States.Count() )
|
||||
{
|
||||
// This should never happen unless there's a bug in the layout code
|
||||
Log_Warning( LOG_TilegenLayoutSystem, "State %s not found in state list.\n", pState->GetStateName() );
|
||||
m_pLayoutSystem->OnError();
|
||||
return NULL;
|
||||
}
|
||||
else if ( i == ( m_States.Count() - 1 ) )
|
||||
{
|
||||
// We're on the last state
|
||||
CTilegenStateList *pParentStateList = GetParentStateList();
|
||||
if ( pParentStateList == NULL )
|
||||
{
|
||||
// No more states left in the layout system.
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( pState->GetParentState() != NULL );
|
||||
return pParentStateList->GetNextState( pState->GetParentState() );
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CTilegenStateList *CTilegenStateList::GetParentStateList()
|
||||
{
|
||||
if ( m_pOwnerState == NULL )
|
||||
{
|
||||
// No parent state implies that this state list is owned by the layout system, so there
|
||||
// is no parent state list.
|
||||
return NULL;
|
||||
}
|
||||
else if ( m_pOwnerState->GetParentState() == NULL )
|
||||
{
|
||||
// A parent state with a NULL parent state implies that this list is owned by a top-level
|
||||
// state, so the parent state list belongs to the layout system.
|
||||
return m_pLayoutSystem->GetStateList();
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_pOwnerState->GetParentState()->GetStateList();
|
||||
}
|
||||
}
|
||||
|
||||
void CTilegenStateList::OnBeginGeneration()
|
||||
{
|
||||
for ( int i = 0; i < m_States.Count(); ++ i )
|
||||
{
|
||||
m_States[i]->OnBeginGeneration( m_pLayoutSystem );
|
||||
}
|
||||
}
|
||||
|
||||
CTilegenState::~CTilegenState()
|
||||
{
|
||||
for ( int i = 0; i < m_Actions.Count(); ++ i )
|
||||
{
|
||||
delete m_Actions[i].m_pAction;
|
||||
delete m_Actions[i].m_pCondition;
|
||||
}
|
||||
}
|
||||
|
||||
bool CTilegenState::LoadFromKeyValues( KeyValues *pKeyValues )
|
||||
{
|
||||
const char *pStateName = pKeyValues->GetString( "name", NULL );
|
||||
if ( pStateName == NULL )
|
||||
{
|
||||
Log_Warning( LOG_TilegenLayoutSystem, "No state name found in State key values.\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
m_StateName[0] = '\0';
|
||||
// @TODO: support nested state names?
|
||||
// if ( pParentStateName != NULL )
|
||||
// {
|
||||
// Q_strncat( m_StateName, pParentStateName, MAX_TILEGEN_IDENTIFIER_LENGTH );
|
||||
// Q_strncat( m_StateName, ".", MAX_TILEGEN_IDENTIFIER_LENGTH );
|
||||
// }
|
||||
Q_strncat( m_StateName, pStateName, MAX_TILEGEN_IDENTIFIER_LENGTH );
|
||||
|
||||
for ( KeyValues *pSubKey = pKeyValues->GetFirstSubKey(); pSubKey != NULL; pSubKey = pSubKey->GetNextKey() )
|
||||
{
|
||||
if ( Q_stricmp( pSubKey->GetName(), "action" ) == 0 )
|
||||
{
|
||||
if ( !ParseAction( pSubKey ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ( Q_stricmp( pSubKey->GetName(), "state") == 0 )
|
||||
{
|
||||
// Nested state
|
||||
CTilegenState *pNewState = new CTilegenState( GetLayoutSystem(), this );
|
||||
if ( !pNewState->LoadFromKeyValues( pSubKey ) )
|
||||
{
|
||||
delete pNewState;
|
||||
return false;
|
||||
}
|
||||
m_ChildStates.AddState( pNewState );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CTilegenState::ExecuteIteration( CLayoutSystem *pLayoutSystem )
|
||||
{
|
||||
if ( m_Actions.Count() == 0 )
|
||||
{
|
||||
// No actions in this state, go to the first nested state if one exists
|
||||
// or move on to the next sibling state.
|
||||
if ( m_ChildStates.GetStateCount() > 0 )
|
||||
{
|
||||
Log_Msg( LOG_TilegenLayoutSystem, "No actions found in state %s, transitioning to first child state.\n", GetStateName() );
|
||||
pLayoutSystem->TransitionToState( m_ChildStates.GetState( 0 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to switch to the next sibling state.
|
||||
CTilegenState *pNextState = m_ChildStates.GetParentStateList()->GetNextState( this );
|
||||
if ( pNextState != NULL )
|
||||
{
|
||||
Log_Msg( LOG_TilegenLayoutSystem, "No actions or child states found in state %s, transitioning to next state.\n", GetStateName() );
|
||||
pLayoutSystem->TransitionToState( pNextState );
|
||||
}
|
||||
else
|
||||
{
|
||||
// This must be the last state in the entire layout system.
|
||||
Log_Msg( LOG_TilegenLayoutSystem, "No more states to which to transition." );
|
||||
pLayoutSystem->OnFinished();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pLayoutSystem->GetFreeVariables()->SetOrCreateFreeVariable( "CurrentState", this );
|
||||
for ( int i = 0; i < m_Actions.Count(); ++ i )
|
||||
{
|
||||
if ( pLayoutSystem->ShouldStopProcessingActions() )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
pLayoutSystem->ExecuteAction( m_Actions[i].m_pAction, m_Actions[i].m_pCondition );
|
||||
}
|
||||
pLayoutSystem->GetFreeVariables()->SetOrCreateFreeVariable( "CurrentState", NULL );
|
||||
}
|
||||
}
|
||||
|
||||
void CTilegenState::OnBeginGeneration( CLayoutSystem *pLayoutSystem )
|
||||
{
|
||||
for ( int i = 0; i < m_Actions.Count(); ++ i )
|
||||
{
|
||||
m_Actions[i].m_pAction->OnBeginGeneration( pLayoutSystem );
|
||||
}
|
||||
|
||||
m_ChildStates.OnBeginGeneration();
|
||||
}
|
||||
|
||||
void CTilegenState::OnStateChanged( CLayoutSystem *pLayoutSystem )
|
||||
{
|
||||
for ( int i = 0; i < m_Actions.Count(); ++ i )
|
||||
{
|
||||
m_Actions[i].m_pAction->OnStateChanged( pLayoutSystem );
|
||||
}
|
||||
}
|
||||
|
||||
bool CTilegenState::ParseAction( KeyValues *pKeyValues )
|
||||
{
|
||||
ITilegenAction *pNewAction = NULL;
|
||||
ITilegenExpression< bool > *pCondition = NULL;
|
||||
if ( CreateActionAndCondition( pKeyValues, &pNewAction, &pCondition ) )
|
||||
{
|
||||
AddAction( pNewAction, pCondition );
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CLayoutSystem::CLayoutSystem() :
|
||||
m_nRandomSeed( 0 ),
|
||||
m_pGlobalActionState( NULL ),
|
||||
m_pCurrentState( NULL ),
|
||||
m_pMapLayout( NULL ),
|
||||
m_ActionData( DefLessFunc( ITilegenAction *) ),
|
||||
m_bLayoutError( false ),
|
||||
m_bGenerating( false ),
|
||||
m_nIterations( 0 )
|
||||
{
|
||||
m_States.SetLayoutSystem( this );
|
||||
}
|
||||
|
||||
CLayoutSystem::~CLayoutSystem()
|
||||
{
|
||||
m_TilegenListeners.PurgeAndDeleteElements();
|
||||
delete m_pGlobalActionState;
|
||||
}
|
||||
|
||||
bool CLayoutSystem::LoadFromKeyValues( KeyValues *pKeyValues )
|
||||
{
|
||||
// Make sure all tilegen class factories have been registered.
|
||||
RegisterAllTilegenClasses();
|
||||
|
||||
for ( KeyValues *pSubKey = pKeyValues->GetFirstSubKey(); pSubKey != NULL; pSubKey = pSubKey->GetNextKey() )
|
||||
{
|
||||
if ( Q_stricmp( pSubKey->GetName(), "state" ) == 0 )
|
||||
{
|
||||
CTilegenState *pNewState = new CTilegenState( this, NULL );
|
||||
if ( !pNewState->LoadFromKeyValues( pSubKey ) )
|
||||
{
|
||||
delete pNewState;
|
||||
return false;
|
||||
}
|
||||
m_States.AddState( pNewState );
|
||||
}
|
||||
else if ( Q_stricmp( pSubKey->GetName(), "action" ) == 0 )
|
||||
{
|
||||
// Global actions, executed at the beginning of every state
|
||||
ITilegenAction *pNewAction = NULL;
|
||||
ITilegenExpression< bool > *pCondition = NULL;
|
||||
if ( CreateActionAndCondition( pSubKey, &pNewAction, &pCondition ) )
|
||||
{
|
||||
if ( m_pGlobalActionState == NULL )
|
||||
{
|
||||
m_pGlobalActionState = new CTilegenState( this, NULL );
|
||||
}
|
||||
m_pGlobalActionState->AddAction( pNewAction, pCondition );
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ( Q_stricmp( pSubKey->GetName(), "mission_settings" ) == 0 )
|
||||
{
|
||||
m_nRandomSeed = pSubKey->GetInt( "RandomSeed", 0 );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// @TODO: add rotation/mirroring support here by adding rotation argument
|
||||
bool CLayoutSystem::TryPlaceRoom( const CRoomCandidate *pRoomCandidate )
|
||||
{
|
||||
int nX = pRoomCandidate->m_iXPos;
|
||||
int nY = pRoomCandidate->m_iYPos;
|
||||
const CRoomTemplate *pRoomTemplate = pRoomCandidate->m_pRoomTemplate;
|
||||
CRoom *pSourceRoom = pRoomCandidate->m_pExit ? pRoomCandidate->m_pExit->pSourceRoom : NULL;
|
||||
|
||||
if ( m_pMapLayout->TemplateFits( pRoomTemplate, nX, nY, false ) )
|
||||
{
|
||||
// This has the side-effect of attaching itself to the layout; no need to keep track of it.
|
||||
CRoom *pRoom = new CRoom( m_pMapLayout, pRoomTemplate, nX, nY );
|
||||
|
||||
// Remove exits covered up by the room
|
||||
for ( int i = m_OpenExits.Count() - 1; i >= 0; -- i )
|
||||
{
|
||||
CExit *pExit = &m_OpenExits[i];
|
||||
if ( pExit->X >= nX && pExit->Y >= nY &&
|
||||
pExit->X < nX + pRoomTemplate->GetTilesX() &&
|
||||
pExit->Y < nY + pRoomTemplate->GetTilesY() )
|
||||
{
|
||||
m_OpenExits.FastRemove( i );
|
||||
}
|
||||
}
|
||||
|
||||
if ( pSourceRoom != NULL )
|
||||
{
|
||||
++ pSourceRoom->m_iNumChildren;
|
||||
}
|
||||
|
||||
AddOpenExitsFromRoom( pRoom );
|
||||
OnRoomPlaced();
|
||||
|
||||
GetFreeVariables()->SetOrCreateFreeVariable( "LastPlacedRoomTemplate", ( void * )pRoomTemplate );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CLayoutSystem::TransitionToState( const char *pStateName )
|
||||
{
|
||||
CTilegenState *pState = m_States.FindState( pStateName );
|
||||
if ( pState == NULL )
|
||||
{
|
||||
Log_Warning( LOG_TilegenLayoutSystem, "Tilegen state %s not found.\n", pStateName );
|
||||
OnError();
|
||||
}
|
||||
else
|
||||
{
|
||||
TransitionToState( pState );
|
||||
}
|
||||
}
|
||||
|
||||
void CLayoutSystem::TransitionToState( CTilegenState *pState )
|
||||
{
|
||||
CTilegenState *pOldState = m_pCurrentState;
|
||||
m_pCurrentState = pState;
|
||||
m_CurrentIterationState.m_bStopIteration = true;
|
||||
OnStateChanged( pOldState );
|
||||
Log_Msg( LOG_TilegenLayoutSystem, "Transitioning to state %s.\n", pState->GetStateName() );
|
||||
StopProcessingActions();
|
||||
}
|
||||
|
||||
void CLayoutSystem::OnError()
|
||||
{
|
||||
m_bLayoutError = true;
|
||||
m_bGenerating = false;
|
||||
Log_Warning( LOG_TilegenLayoutSystem, "An error occurred during level generation.\n" );
|
||||
}
|
||||
|
||||
void CLayoutSystem::OnFinished()
|
||||
{
|
||||
m_bGenerating = false;
|
||||
m_CurrentIterationState.m_bStopIteration = true;
|
||||
Log_Msg( LOG_TilegenLayoutSystem, "CLayoutSystem: finished layout generation.\n" );
|
||||
|
||||
// Temp hack to setup fixed alien spawns
|
||||
// TODO: Move this into a required rule
|
||||
CASWMissionChooserNPCs::InitFixedSpawns( this, m_pMapLayout );
|
||||
}
|
||||
|
||||
void CLayoutSystem::ExecuteAction( ITilegenAction *pAction, ITilegenExpression< bool > *pCondition )
|
||||
{
|
||||
Assert( pAction != NULL );
|
||||
|
||||
// Since actions can be nested, only expose the inner-most nested action.
|
||||
ITilegenAction *pOldAction = ( ITilegenAction * )GetFreeVariables()->GetFreeVariableOrNULL( "Action" );
|
||||
GetFreeVariables()->SetOrCreateFreeVariable( "Action", pAction );
|
||||
|
||||
// Get data associated with the current action and set it to free variables
|
||||
int nIndex = m_ActionData.Find( pAction );
|
||||
if ( nIndex == m_ActionData.InvalidIndex() )
|
||||
{
|
||||
ActionData_t actionData = { 0 };
|
||||
actionData.m_nNumTimesExecuted = 0;
|
||||
nIndex = m_ActionData.Insert( pAction, actionData );
|
||||
Assert( nIndex != m_ActionData.InvalidIndex() );
|
||||
}
|
||||
GetFreeVariables()->SetOrCreateFreeVariable( "NumTimesExecuted", ( void * )m_ActionData[nIndex].m_nNumTimesExecuted );
|
||||
|
||||
// Execute the action if the condition is met
|
||||
if ( pCondition == NULL || pCondition->Evaluate( GetFreeVariables() ) )
|
||||
{
|
||||
// Log_Msg( LOG_TilegenLayoutSystem, "Executing action %s.\n", pAction->GetTypeName() );
|
||||
pAction->Execute( this );
|
||||
++ m_ActionData[nIndex].m_nNumTimesExecuted;
|
||||
OnActionExecuted( pAction );
|
||||
}
|
||||
|
||||
// Restore free variable state
|
||||
GetFreeVariables()->SetOrCreateFreeVariable( "Action", pOldAction );
|
||||
nIndex = m_ActionData.Find( pOldAction );
|
||||
Assert( nIndex != m_ActionData.InvalidIndex() );
|
||||
GetFreeVariables()->SetOrCreateFreeVariable( "NumTimesExecuted", ( void * )m_ActionData[nIndex].m_nNumTimesExecuted );
|
||||
}
|
||||
|
||||
void CLayoutSystem::OnActionExecuted( const ITilegenAction *pAction )
|
||||
{
|
||||
for ( int i = 0; i < m_TilegenListeners.Count(); ++ i )
|
||||
{
|
||||
m_TilegenListeners[i]->OnActionExecuted( this, pAction, GetFreeVariables() );
|
||||
}
|
||||
}
|
||||
|
||||
void CLayoutSystem::OnRoomPlaced()
|
||||
{
|
||||
for ( int i = 0; i < m_TilegenListeners.Count(); ++ i )
|
||||
{
|
||||
m_TilegenListeners[i]->OnRoomPlaced( this, GetFreeVariables() );
|
||||
}
|
||||
}
|
||||
|
||||
void CLayoutSystem::OnStateChanged( const CTilegenState *pOldState )
|
||||
{
|
||||
for ( int i = 0; i < m_TilegenListeners.Count(); ++ i )
|
||||
{
|
||||
m_TilegenListeners[i]->OnStateChanged( this, pOldState, GetFreeVariables() );
|
||||
}
|
||||
|
||||
m_pCurrentState->OnStateChanged( this );
|
||||
}
|
||||
|
||||
void CLayoutSystem::BeginGeneration( CMapLayout *pMapLayout )
|
||||
{
|
||||
if ( m_States.GetStateCount() == 0 )
|
||||
{
|
||||
Log_Warning( LOG_TilegenLayoutSystem, "No states in layout system!\n" );
|
||||
OnError();
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset random generator
|
||||
int nSeed;
|
||||
m_Random = CUniformRandomStream();
|
||||
if ( m_nRandomSeed != 0 )
|
||||
{
|
||||
nSeed = m_nRandomSeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
// @TODO: this is rather unscientific, but it gets us desired randomness for now.
|
||||
nSeed = RandomInt( 1, 1000000000 );
|
||||
}
|
||||
|
||||
m_Random.SetSeed( nSeed );
|
||||
Log_Msg( LOG_TilegenLayoutSystem, "Beginning generation with random seed " );
|
||||
Log_Msg( LOG_TilegenLayoutSystem, Color( 255, 255, 0, 255 ), "%d.\n", nSeed );
|
||||
|
||||
m_bLayoutError = false;
|
||||
m_bGenerating = true;
|
||||
m_nIterations = 0;
|
||||
m_pMapLayout = pMapLayout;
|
||||
m_pCurrentState = m_States.GetState( 0 );
|
||||
m_OpenExits.RemoveAll();
|
||||
m_ActionData.RemoveAll();
|
||||
m_FreeVariables.RemoveAll();
|
||||
|
||||
// Initialize with sentinel value
|
||||
ActionData_t nullActionData = { 0 };
|
||||
m_ActionData.Insert( NULL, nullActionData );
|
||||
|
||||
for ( int i = 0; i < m_TilegenListeners.Count(); ++ i )
|
||||
{
|
||||
m_TilegenListeners[i]->OnBeginGeneration( this, GetFreeVariables() );
|
||||
}
|
||||
|
||||
if ( m_pGlobalActionState != NULL )
|
||||
{
|
||||
m_pGlobalActionState->OnBeginGeneration( this );
|
||||
}
|
||||
|
||||
m_States.OnBeginGeneration();
|
||||
|
||||
GetFreeVariables()->SetOrCreateFreeVariable( "LayoutSystem", this );
|
||||
GetFreeVariables()->SetOrCreateFreeVariable( "MapLayout", GetMapLayout() );
|
||||
}
|
||||
|
||||
void CLayoutSystem::ExecuteIteration()
|
||||
{
|
||||
Assert( m_pCurrentState != NULL && m_pMapLayout != NULL );
|
||||
|
||||
const int nMaxIterations = 200;
|
||||
if ( m_nIterations > nMaxIterations )
|
||||
{
|
||||
Log_Warning( LOG_TilegenLayoutSystem, "Exceeded %d iterations, may be in an infinite loop.\n", nMaxIterations );
|
||||
OnError();
|
||||
return;
|
||||
}
|
||||
|
||||
// Debugging assistant
|
||||
int nBreakOnIteration = tilegen_break_on_iteration.GetInt();
|
||||
if ( nBreakOnIteration >= 0 )
|
||||
{
|
||||
Log_Msg( LOG_TilegenLayoutSystem, "Iteration #%d\n", m_nIterations );
|
||||
}
|
||||
if ( m_nIterations == nBreakOnIteration )
|
||||
{
|
||||
DebuggerBreakIfDebugging();
|
||||
}
|
||||
|
||||
// Initialize state scoped to the current iteration
|
||||
m_CurrentIterationState.m_bStopIteration = false;
|
||||
CUtlVector< CRoomCandidate > roomCandidateList;
|
||||
m_CurrentIterationState.m_pRoomCandidateList = &roomCandidateList;
|
||||
|
||||
// Execute global actions first, if any are present.
|
||||
if ( m_pGlobalActionState != NULL )
|
||||
{
|
||||
m_pGlobalActionState->ExecuteIteration( this );
|
||||
}
|
||||
|
||||
// Now process actions in the current state
|
||||
if ( !ShouldStopProcessingActions() )
|
||||
{
|
||||
// Executing an iteration may have a number of side-effects on the layout system,
|
||||
// such as placing rooms, changing state, etc.
|
||||
m_pCurrentState->ExecuteIteration( this );
|
||||
}
|
||||
|
||||
++ m_nIterations;
|
||||
|
||||
m_CurrentIterationState.m_pRoomCandidateList = NULL;
|
||||
}
|
||||
|
||||
// This function adds an open exit for every un-mated exit in the given room.
|
||||
// The exits are added to the grid tile where a new room would connect
|
||||
// (e.g., a north exit from a room tile at (x, y) is actually added to location (x, y+1)
|
||||
void CLayoutSystem::AddOpenExitsFromRoom( CRoom *pRoom )
|
||||
{
|
||||
const CRoomTemplate *pTemplate = pRoom->m_pRoomTemplate;
|
||||
|
||||
// Go through each exit in the room.
|
||||
for ( int i = 0; i < pTemplate->m_Exits.Count(); ++ i )
|
||||
{
|
||||
const CRoomTemplateExit *pExit = pTemplate->m_Exits[i];
|
||||
int nExitX, nExitY;
|
||||
if ( GetExitPosition( pTemplate, pRoom->m_iPosX, pRoom->m_iPosY, i, &nExitX, &nExitY ) )
|
||||
{
|
||||
// If no room exists where the exit interface goes, then consider the exit open.
|
||||
// NOTE: this function assumes that the caller has already verified that the template fits, which means
|
||||
// that if a room exists where this exit goes, it must be a matching exit (and thus not open).
|
||||
if ( !m_pMapLayout->GetRoom( nExitX, nExitY ) )
|
||||
{
|
||||
AddOpenExit( pRoom, nExitX, nExitY, pExit->m_ExitDirection, pExit->m_szExitTag, pExit->m_bChokepointGrowSource );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CLayoutSystem::AddOpenExit( CRoom *pSourceRoom, int nX, int nY, ExitDirection_t exitDirection, const char *pExitTag, bool bChokepointGrowSource )
|
||||
{
|
||||
// Check to make sure exit isn't already in the list.
|
||||
for ( int i = 0; i < m_OpenExits.Count(); ++ i )
|
||||
{
|
||||
const CExit *pExit = &m_OpenExits[i];
|
||||
if ( pExit->X == nX && pExit->Y == nY && pExit->ExitDirection == exitDirection )
|
||||
{
|
||||
// Exit already exists.
|
||||
Assert( pExit->pSourceRoom == pSourceRoom && Q_stricmp( pExitTag, pExit->m_szExitTag ) == 0 );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_OpenExits.AddToTail( CExit( nX, nY, exitDirection, pExitTag, pSourceRoom, bChokepointGrowSource ) );
|
||||
}
|
||||
Reference in New Issue
Block a user