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() ); }
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 ); }
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 ); } }
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; }
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(); } } } } }