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_nUselessIterations = 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() );
}
Example #2
0
void CTilegenAction_ChooseCandidate::Execute( CLayoutSystem *pLayoutSystem )
{
	CUtlVector< CRoomCandidate > *pRoomCandidateList = pLayoutSystem->GetRoomCandidateList();
	if ( pRoomCandidateList->Count() == 0 )
	{
		Log_Msg( LOG_TilegenLayoutSystem, "No more room candidates to choose from.\n" );
		pLayoutSystem->GetFreeVariables()->SetOrCreateFreeVariable( "ChoseCandidate", ( void * )0 );
		return;
	}

	int i;
	float flChance = 0.0f;
	for ( i = 0; i < pRoomCandidateList->Count(); ++ i )
	{
		flChance += pRoomCandidateList->Element( i ).m_flCandidateChance;
	}

	float flRandom = pLayoutSystem->GetRandomFloat( 0.0f, 1.0f ) * flChance;

	for ( i = 0; i < pRoomCandidateList->Count(); ++ i )
	{
		flRandom -= pRoomCandidateList->Element( i ).m_flCandidateChance;
		if ( flRandom <= 0.0f )
		{
			break;
		}
	}
	
	if ( i == pRoomCandidateList->Count() ) 
	{
		i = pRoomCandidateList->Count() - 1;
	}
	
	const CRoomCandidate *pCandidate = &pRoomCandidateList->Element( i );

	// This should always succeed since it's in the candidate list to begin with
	bool bSuccess = pLayoutSystem->TryPlaceRoom( pCandidate );
	Assert( bSuccess );
	bSuccess;
	Log_Msg( LOG_TilegenLayoutSystem, "Chose room candidate %s at position (%d, %d).\n", pCandidate->m_pRoomTemplate->GetFullName(), pCandidate->m_iXPos, pCandidate->m_iYPos );
	
	// Empty the room candidate list for future actions
	pRoomCandidateList->RemoveAll();

	if ( m_bStopProcessingActionsOnSuccess )
	{
		pLayoutSystem->StopProcessingActions();
	}

	pLayoutSystem->GetFreeVariables()->SetOrCreateFreeVariable( "ChoseCandidate", ( void * )1 );
}
Example #3
0
void CTilegenAction_LoadLayout::Execute( CLayoutSystem *pLayoutSystem )
{
	CMapLayout *pMapLayout = new CMapLayout();
	if ( !pMapLayout->LoadMapLayout( m_LayoutFilename ) )
	{
		Log_Warning( LOG_TilegenLayoutSystem, "Unable to load map layout '%s'.\n", m_LayoutFilename );
		pLayoutSystem->OnError();
		return;
	}

	for ( int i = 0; i < pMapLayout->m_PlacedRooms.Count(); ++ i )
	{
		CRoom *pRoom = pMapLayout->m_PlacedRooms[i];
		CRoomCandidate roomCandidate( pRoom->m_pRoomTemplate, pRoom->m_iPosX, pRoom->m_iPosY, NULL );
		if ( !pLayoutSystem->TryPlaceRoom( &roomCandidate ) )
		{
			Log_Warning( LOG_TilegenLayoutSystem, "Unable to place room template '%s' at position (%d, %d) based on data from layout file '%s'.\n", 
				pRoom->m_pRoomTemplate->GetFullName(), 
				pRoom->m_iPosX,
				pRoom->m_iPosY,
				m_LayoutFilename );
			pLayoutSystem->OnError();
			return;
		}
		Log_Msg( LOG_TilegenLayoutSystem, "Chose room candidate %s at position (%d, %d).\n", pRoom->m_pRoomTemplate->GetFullName(), pRoom->m_iPosX, pRoom->m_iPosY );
	}
}
Example #4
0
void CTilegenAction_PlaceComponent::Execute( CLayoutSystem *pLayoutSystem )
{
	int nTotalArea;
	int nNumTilesPlaced;
	CFreeVariableMap *pFreeVariables = pLayoutSystem->GetFreeVariables();
	bool bInGlobalActionState = ( pFreeVariables->GetFreeVariableDisallowNULL( "CurrentState" ) == pLayoutSystem->GetGlobalActionState() );
	const char *pNumTilesPlacedVariableName;
	const char *pTotalAreaVariableName;
	if ( bInGlobalActionState )
	{
		// Fractions are portions of the entire map's area.  This relies on the "TotalGenerationArea" variable to have been set.
		// The CTilegenListener_NumTilesPlaced listener must also be registered.
		pTotalAreaVariableName = "TotalGenerationArea";
		pNumTilesPlacedVariableName = "NumTilesPlaced";
	}
	else
	{
		// Fractions are portions of this step's total area.  This relies on the "StepGenerationArea" variable to have been set for the current state.
		// The CTilegenListener_NumTilesPlaced listener must also be registered.
		pTotalAreaVariableName = "StepGenerationArea";
		pNumTilesPlacedVariableName = "NumTilesPlacedThisState";
	}

	nTotalArea = ( int )pFreeVariables->GetFreeVariableOrNULL( pTotalAreaVariableName );
	nNumTilesPlaced = ( int )pFreeVariables->GetFreeVariableOrNULL( pNumTilesPlacedVariableName );

	if ( nTotalArea <= 0 )
	{
		// Nothing has been placed yet, do not attempt to place component pieces
		Log_Msg( LOG_TilegenLayoutSystem, "Ignoring PlaceComponent action since no tiles have been placed yet.\n" );
		pLayoutSystem->GetFreeVariables()->SetOrCreateFreeVariable( "PlacedComponent", ( void * )0 );
		return;
	}

	// Go through the sorted list of rooms to place and attempt to place 1 room
	for ( int i = 0; i < m_RoomsToPlace.Count(); ++ i )
	{
		if ( !m_RoomsToPlace[i].m_bPlaced )
		{
			int nTileThreshold = ( int )( m_RoomsToPlace[i].m_flPlacementFraction * ( float )nTotalArea );
			if ( nNumTilesPlaced >= nTileThreshold )
			{
				if ( PlaceRoom( pLayoutSystem, m_RoomsToPlace[i].pInfo->m_pRoomTemplate ) )
				{
					m_RoomsToPlace[i].m_bPlaced = true;
					pLayoutSystem->GetFreeVariables()->SetOrCreateFreeVariable( "PlacedComponent", ( void * )1 );
					return;
				}
				else
				{
					pLayoutSystem->OnError();
					return;
				}
			}
		}
	}
	pLayoutSystem->GetFreeVariables()->SetOrCreateFreeVariable( "PlacedComponent", ( void * )0 );
}
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::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 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 CTilegenAction_PlaceComponent::OnBeginGeneration( CLayoutSystem *pLayoutSystem )
{
	int nNumOptionalRooms = pLayoutSystem->GetRandomInt( m_nMinOptionalRooms, m_nMaxOptionalRooms );
	nNumOptionalRooms = MIN( nNumOptionalRooms, m_OptionalRooms.Count() );
	nNumOptionalRooms = MAX( nNumOptionalRooms, 0 );

	m_RoomsToPlace.RemoveAll();
	for ( int i = 0; i < m_MandatoryRooms.Count(); ++ i )
	{
		AddRoomPlacementInstance( pLayoutSystem, &m_MandatoryRooms[i] );
	}

	bool isRoomChosen[m_nMaxTotalOptionalRooms];
	Q_memset( isRoomChosen, 0, sizeof( isRoomChosen ) );

	// Simplest but probably not the most efficient way to randomly choose N rooms from a list of X rooms.
	int nNumOptionalRoomsChosen = 0;
	Msg("Processing random rooms, need to add [%d] rooms.  Have [%d] options\n",nNumOptionalRooms, m_OptionalRooms.Count());
	Log_Msg(LOG_TilegenLayoutSystem, "Processing random rooms, need to add [%d] rooms.  Have [%d] options\n",nNumOptionalRooms, m_OptionalRooms.Count());
		
	while ( nNumOptionalRoomsChosen < nNumOptionalRooms )
	{

		//BUG: int nRoom = pLayoutSystem->GetRandomInt( 0, nNumOptionalRooms - 1 );
		//Should choose from all available rooms.
		int nRoom = pLayoutSystem->GetRandomInt( 0, m_OptionalRooms.Count() -1);
			
		if ( !isRoomChosen[nRoom] )
		{
			
			isRoomChosen[nRoom] = true;
			AddRoomPlacementInstance( pLayoutSystem, &m_OptionalRooms[nRoom] );
			++ nNumOptionalRoomsChosen;
			Msg("Room [%d] is id [%d]\n", nNumOptionalRoomsChosen, nRoom);
			Log_Msg(LOG_TilegenLayoutSystem,  "Room [%d] is id [%d]\n", nNumOptionalRoomsChosen, nRoom);			
		}
	}
	Msg("Done processing random rooms.\n");
}
void CLayoutSystem::ExecuteIteration()
{
	Assert( m_pCurrentState != NULL && m_pMapLayout != NULL );

	const int nMaxIterations = 200;
	if ( m_nUselessIterations > 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_nUselessIterations;

	m_CurrentIterationState.m_pRoomCandidateList = NULL;
}
Example #10
0
bool CTilegenAction_PlaceComponent::PlaceRoom( CLayoutSystem *pLayoutSystem, const CRoomTemplate *pRoomTemplate )
{
	int nNumTries = 0;

	while ( nNumTries < 10 )
	{
		BuildRoomCandidateList( 
			pLayoutSystem, 
			&pRoomTemplate,
			1,
			m_pExitFilter, 
			m_pRoomCandidateFilter, 
			m_pRoomCandidateFilterAction,
			m_pRoomCandidateFilterCondition,
			m_bExcludeGlobalFilters );

		CUtlVector< CRoomCandidate > *pRoomCandidateList = pLayoutSystem->GetRoomCandidateList();
		if ( pRoomCandidateList->Count() > 0 )
		{
			pLayoutSystem->ExecuteAction( m_pChooseCandidate, NULL );
			return true;
		}
		else
		{
			Log_Msg( LOG_TilegenLayoutSystem, "Unable to place component room %s, attempting to place connector room.\n", pRoomTemplate->GetFullName() );
			if ( !PlaceConnectorRoom( pLayoutSystem, pRoomTemplate ) )
			{
				Log_Warning( LOG_TilegenLayoutSystem, "Unable to place connector piece to connect to component room %s.\n", pRoomTemplate->GetFullName() );
				return false;
			}
		}

		++ nNumTries;
	}

	Log_Warning( LOG_TilegenLayoutSystem, "Unable to place component room %s after %d tries.\n", pRoomTemplate->GetFullName(), nNumTries );
	return false;
}
// Builds a map from a .layout file
void CASW_Map_Builder::BuildMap()
{
	char layoutFilename[MAX_PATH];
	char vmfFilename[MAX_PATH];
	
	Q_snprintf( layoutFilename, MAX_PATH, "maps\\%s", m_szLayoutName );
	Q_strncpy( vmfFilename, m_szLayoutName, MAX_PATH );
	Q_SetExtension( vmfFilename, "vmf", MAX_PATH );

	Log_Msg( LOG_TilegenGeneral, "Building map from layout: %s, emitting map file: %s\n", layoutFilename, vmfFilename );

	// Make sure our themes are loaded
	CLevelTheme::LoadLevelThemes();

	// Load the .layout from disk
	// @TODO: keep this in memory and avoid the round-trip
	delete m_pBuildingMapLayout;
	m_pBuildingMapLayout = new CMapLayout();
	if ( !m_pBuildingMapLayout->LoadMapLayout( layoutFilename ) )
	{
		Log_Warning(LOG_TilegenGeneral, "Couldn't find layoutfile: [%s]", layoutFilename);
		m_iBuildStage = MapBuildStage::STAGE_NONE;
		delete m_pBuildingMapLayout;
		m_pBuildingMapLayout = NULL;
		return;
	}

	// Export it to VMF
	bool bSuccess = false;
	{
		VMFExporter exporter;
		bSuccess = exporter.ExportVMF(m_pBuildingMapLayout, m_szLayoutName);
	}

	if ( !bSuccess )
	{
		Log_Warning( LOG_TilegenGeneral, "Failed to create VMF from layout '%s'.\n", m_szLayoutName );
		delete m_pBuildingMapLayout;
		m_pBuildingMapLayout = NULL;
	}
	
	if ( asw_vbsp2.GetInt() )
	{
		m_iBuildStage = STAGE_VBSP2;
		m_nVBSP2Progress = 0;
		Q_strncpy( m_szVBSP2MapName, vmfFilename, MAX_PATH );
		// Guarantee all reads/writes committed before kicking off the thread.  Probably unnecessary in practice due to lag between operations, but whatever...
		ThreadMemoryBarrier();

		// Call with a 0 ms timeout to return immediately
		m_pWorkerThread->CallWorker( MBC_PROCESS_MAP, 0 );
	}
	else
	{
		// Building map layout is ignored in VBSP1 codepath
		delete m_pBuildingMapLayout;
		m_pBuildingMapLayout = NULL;

		m_iBuildStage = STAGE_VBSP;

		char buffer[512];
		Q_snprintf( buffer, sizeof(buffer), "-game ..\\ %s %s", m_pMapBuilderOptions->GetString( "vbsp", "" ), vmfFilename );
		Execute( "bin/vbsp.exe", buffer );

		m_iCurrentBuildSearch = 0;
		m_iOutputBufferPos = 0;
		Q_memset( &m_szOutputBuffer, 0, sizeof( m_szOutputBuffer ) );
	}
}
void CASW_Map_Builder::Update( float flEngineTime )
{
	if ( m_bRunningProcess )
	{
		ProcessExecution();
	}
	else if ( m_iBuildStage == STAGE_MAP_BUILD_SCHEDULED )
	{
		if ( m_flStartProcessingTime < flEngineTime )
		{
			BuildMap();
		}
	}
	else if ( m_iBuildStage == STAGE_VBSP2 )
	{
		UpdateVBSP2Progress();
	}
	else if ( m_iBuildStage == STAGE_GENERATE )
	{
		if ( m_flStartProcessingTime < flEngineTime )
		{
			if ( !m_bStartedGeneration )
			{
				delete m_pGeneratedMapLayout;
				delete m_pLayoutSystem;
				m_pLayoutSystem = new CLayoutSystem();
				AddListeners( m_pLayoutSystem );
				m_pGeneratedMapLayout = new CMapLayout( m_pMissionSettings->MakeCopy() );

				if ( !m_pLayoutSystem->LoadFromKeyValues( m_pMissionDefinition ) )
				{
					Log_Warning( LOG_TilegenLayoutSystem, "Failed to load mission from key values definition.\n" );
					m_iBuildStage = STAGE_NONE;
					return;
				}
				m_pLayoutSystem->BeginGeneration( m_pGeneratedMapLayout );
				m_bStartedGeneration = true;
			}
			else
			{
				if ( m_pLayoutSystem->IsGenerating() )
				{
					m_pLayoutSystem->ExecuteIteration();

					// If an error occurred and this map is randomly generated, try again and hope we get a successful layout this time.
					if ( m_pLayoutSystem->GenerationErrorOccurred() )
					{
						if ( m_nLevelGenerationRetryCount < tilegen_retry_count.GetInt() && m_pLayoutSystem->IsRandomlyGenerated() )
						{
							// Error generating layout
							Log_Msg( LOG_TilegenGeneral, "Retrying layout generation...\n" );
							m_pGeneratedMapLayout->Clear();
							m_pLayoutSystem->BeginGeneration( m_pGeneratedMapLayout );
							++ m_nLevelGenerationRetryCount;
						}
						else
						{
							Log_Warning( LOG_TilegenGeneral, "Failed to generate valid map layout after %d tries...\n", tilegen_retry_count.GetInt() );
							m_iBuildStage = STAGE_NONE;
						}
					}
				}
				else
				{
					Log_Msg( LOG_TilegenGeneral, "Map layout generated\n" );
					m_iBuildStage = STAGE_NONE;
					
					char layoutFilename[MAX_PATH];
					Q_snprintf( layoutFilename, MAX_PATH, "maps\\%s", m_szLayoutName );
					m_pGeneratedMapLayout->SaveMapLayout( layoutFilename );

					delete m_pGeneratedMapLayout;
					m_pGeneratedMapLayout = NULL;

					BuildMap();
				}
			}
		}
	}
}