#include #include #include #include #include #include #include #include #include #include #include #include #include "vgui_controls/AnimationController.h" #include #include #include #include "vgui/ISurface.h" #include #include #include "TileGenDialog.h" #include "tilegen_pages.h" #include "ThemesDialog.h" #include "RoomTemplateEditDialog.h" #include "RoomTemplateListPanel.h" #include "RoomTemplatePanel.h" #include "PlacedRoomTemplatePanel.h" #include "MapLayoutPanel.h" #include "ScrollingWindow.h" #include "VMFExporter.h" #include "TileSource/LevelTheme.h" #include "TileSource/RoomTemplate.h" #include "TileSource/MapLayout.h" #include "TileSource/Room.h" #include "cdll_int.h" #include "filesystem.h" #include "kv_editor_frame.h" #include "asw_mission_chooser.h" #include "location_editor/location_editor_frame.h" #include "tile_check.h" #include "layout_system/tilegen_layout_system.h" #include "layout_system/tilegen_mission_preprocessor.h" #include "layout_system_editor/layout_system_kv_editor.h" #include "asw_npcs.h" // memdbgon must be the last include file in a .cpp file!!! #include using namespace vgui; ConVar tilegen_preprocess_mission( "tilegen_preprocess_mission", "0", FCVAR_CHEAT, "If set to 1, tilegen will dump the pre-processed mission file to preprocess_mission.txt." ); CTileGenDialog *g_pTileGenDialog = NULL; bool g_bProcessGenerator = false; bool g_bAddedTileGenLocalization = false; extern IVEngineClient *engine; extern char g_layoutsdir[1024]; class CModalPreserveMessageBox : public vgui::MessageBox { public: CModalPreserveMessageBox(const char *title, const char *text, vgui::Panel *parent) : vgui::MessageBox( title, text, parent ) { m_PrevAppFocusPanel = vgui::input()->GetAppModalSurface(); } ~CModalPreserveMessageBox() { vgui::input()->SetAppModalSurface( m_PrevAppFocusPanel ); } public: vgui::VPANEL m_PrevAppFocusPanel; }; //----------------------------------------------------------------------------- // Purpose: Utility function to pop up a VGUI message box //----------------------------------------------------------------------------- void VGUIMessageBox( vgui::Panel *pParent, const char *pTitle, const char *pMsg, ... ) { char msg[4096]; va_list marker; va_start( marker, pMsg ); Q_vsnprintf( msg, sizeof( msg ), pMsg, marker ); va_end( marker ); vgui::MessageBox *dlg = new CModalPreserveMessageBox( pTitle, msg, pParent ); dlg->DoModal(); dlg->Activate(); dlg->RequestFocus(); } //----------------------------------------------------------------------------- // Constructor //----------------------------------------------------------------------------- CTileGenDialog::CTileGenDialog( Panel *parent, const char *name ) : BaseClass( parent, name ) { Assert( !g_pTileGenDialog ); g_pTileGenDialog = this; m_pMapLayout = new CMapLayout(); m_pLayoutSystem = NULL; m_VMFExporter = new VMFExporter(); m_pGenerationOptions = new KeyValues( "EmptyOptions" ); vgui::HScheme scheme = vgui::scheme()->LoadSchemeFromFile( "tilegen/tilegen_scheme.res", "GAME" ); SetScheme( scheme ); if ( !g_bAddedTileGenLocalization ) { g_pVGuiLocalize->AddFile( "tilegen/tilegen_english.txt" ); g_bAddedTileGenLocalization = true; } m_fTileSize = 30.0f; m_bShowExits = true; m_bShowTileSquares = true; m_iStartDragX = -1; m_iStartDragY = -1; m_iStartRubberBandX = -1; SetSize(384, 420); SetMinimumSize(384, 420); m_pCurrentThemeDetails = new CThemeDetails(this, "CurrentThemeDetails", NULL); m_pCurrentThemeDetails->m_iDesiredWidth = 356; m_pTemplateListContainer = new CScrollingWindow(this, "TemplateListWindow"); m_pTemplateListPanel = new CRoomTemplateListPanel(m_pTemplateListContainer, "TemplateListPanel"); m_pTemplateFilter = new TextEntry( this, "TemplateFilter" ); m_pShowExitsCheck = new vgui::CheckButton(this, "ShowExitsCheck", ""); m_pShowExitsCheck->SetSelected(m_bShowExits); m_pShowTileSquaresCheck = new vgui::CheckButton(this, "ShowTileSquaresCheck", ""); m_pShowTileSquaresCheck->SetSelected(m_bShowTileSquares); m_pPropertySheet = new vgui::PropertySheet( this, "PropertySheet" ); m_pLayoutPage = new CTileGenLayoutPage( m_pPropertySheet, "TileGenLayoutPage" ); m_pLayoutSystemPage = new CTilegenLayoutSystemPage( m_pPropertySheet, "TilegenLayoutSystemPage" ); m_pLayoutSystemPage->UpdateList(); m_pPropertySheet->AddPage( m_pLayoutPage, "Layout" ); m_pPropertySheet->AddPage( m_pLayoutSystemPage, "Missions" ); m_pCursorPanel = new CRoomTemplatePanel(this, "CursorRoomTemplatePanel"); m_pCursorPanel->SetMouseInputEnabled(false); m_pCursorPanel->SetVisible(false); m_pCursorTemplate = NULL; m_pTemplateListContainer->SetChildPanel( m_pTemplateListPanel ); CLevelTheme::LoadLevelThemes(); OnUpdateCurrentTheme(NULL); // setup the menu m_pMenuBar = new MenuBar(this, "TileGenMenuBar"); MenuButton *pFileMenuButton = new MenuButton(this, "FileMenuButton", "&File"); Menu *pFileMenu = new Menu(pFileMenuButton, "TileGenFileMenu"); pFileMenu->AddMenuItem("&New Map Layout", "NewMapLayout", this); pFileMenu->AddMenuItem("&Open Map Layout...", "OpenMapLayout", this); pFileMenu->AddMenuItem("&Open Current Map Layout...", "OpenCurrentMapLayout", this); pFileMenu->AddMenuItem("&Save Map Layout", "SaveMapLayout", this); pFileMenu->AddMenuItem("&Save Map Layout As...", "SaveMapLayoutAs", this); pFileMenu->AddSeparator(); pFileMenu->AddMenuItem("&Export VMF", "ExportVMF", this); pFileMenu->AddMenuItem("&Export VMF and Play Map", "ExportAndPlay", this); pFileMenu->AddMenuItem("&Export VMF and Play Without Aliens", "ExportAndPlayClean", this); pFileMenuButton->SetMenu(pFileMenu); m_pMenuBar->AddButton(pFileMenuButton); MenuButton *pToolsMenuButton = new MenuButton(this, "ToolsMenuButton", "&Tools"); m_pToolsMenu = new Menu(pToolsMenuButton, "TileGenToolsMenu"); pToolsMenuButton->SetMenu(m_pToolsMenu); m_pMenuBar->AddButton(pToolsMenuButton); //m_pToolsMenu->AddMenuItem("Push Encounters Apart", "PushEncountersApart", this); // used to debug encounter push apart m_pToolsMenu->AddMenuItem("Generate &Thumbnails", "GenerateThumbnails", this); //m_pToolsMenu->AddMenuItem("&Location Layout Editor", "LocationLayoutEditor", this); m_pToolsMenu->AddMenuItem("&Check Room Templates for Errors", "TileCheck", this); m_pToolsMenu->AddSeparator(); m_nShowDefaultParametersMenuItemID = m_pToolsMenu->AddCheckableMenuItem( "&Show Optional Parameters", "ShowOptionalParameters", this ); m_nShowAddButtonsMenuItemID = m_pToolsMenu->AddCheckableMenuItem( "&Show 'Add' Buttons", "ShowAddButtons", this ); // Get the checked menu items in-sync with editor settings. m_pToolsMenu->SetMenuItemChecked( m_nShowDefaultParametersMenuItemID, true ); m_pToolsMenu->SetMenuItemChecked( m_nShowAddButtonsMenuItemID, true ); m_pLayoutSystemPage->GetEditor()->ShowOptionalValues( true ); m_pLayoutSystemPage->GetEditor()->ShowAddButtons( true ); // disable keyboard input on the menu, otherwise once you click there, this dialog never gets key input again (is there a better way to do this and maintain keyboard shortcuts for the menu?) pFileMenuButton->SetKeyBoardInputEnabled(false); pFileMenu->SetKeyBoardInputEnabled(false); m_pMenuBar->SetKeyBoardInputEnabled(false); m_pZoomInButton = new vgui::Button( this, "ZoomInButton", "+", this, "ZoomIn" ); m_pZoomOutButton = new vgui::Button( this, "ZoomOutButton", "-", this, "ZoomOut" ); m_pCoordinateLabel = new vgui::Label( this, "CoordinateLabel", "" ); // make sure the window get a tick all the time (could change this to only tick on necessary states?) vgui::ivgui()->AddTickSignal( GetVPanel(), 0 ); } //----------------------------------------------------------------------------- // Destructor //----------------------------------------------------------------------------- CTileGenDialog::~CTileGenDialog() { g_pTileGenDialog = NULL; delete m_VMFExporter; delete m_pMapLayout; m_pGenerationOptions->deleteThis(); delete m_pLayoutSystem; } //----------------------------------------------------------------------------- // Purpose: Kills the whole app on close //----------------------------------------------------------------------------- void CTileGenDialog::OnClose( void ) { BaseClass::OnClose(); delete this; } //----------------------------------------------------------------------------- // Purpose: Parse commands coming in from the VGUI dialog //----------------------------------------------------------------------------- void CTileGenDialog::OnCommand( const char *command ) { if ( Q_stricmp( command, "Themes" ) == 0 ) { // Launch the theme editing window CThemesDialog *pDialog = new CThemesDialog( this, "ThemesDialog", true ); pDialog->AddActionSignalTarget( this ); pDialog->DoModal(); } else if ( Q_stricmp( command, "NewRoomTemplate" ) == 0 ) { if (!CLevelTheme::s_pCurrentTheme) { MessageBox *pMessage = new MessageBox ("No theme selected", "You must select a theme before creating a new room template.", this); pMessage->DoModal(); return; } CRoomTemplate *pRoomTemplate = new CRoomTemplate(CLevelTheme::s_pCurrentTheme); if (pRoomTemplate) { // Launch the room template editing window CRoomTemplateEditDialog *pDialog = new CRoomTemplateEditDialog( this, "RoomTemplateEditDialog", pRoomTemplate, true ); pDialog->AddActionSignalTarget( this ); pDialog->DoModal(); } } else if ( Q_stricmp( command, "ExportVMF" ) == 0 ) { if (!m_VMFExporter->ExportVMF( GetMapLayout(), "output.vmf", true ) ) { MessageBox *pMessage = new MessageBox ("VMF Export Error", m_VMFExporter->m_szLastExporterError, this); pMessage->DoModal(); return; } if ( !m_VMFExporter->ShowExportErrors() ) { MessageBox *pMessage = new MessageBox ("VMF Export", "Output.vmf exported okay!", this); pMessage->DoModal(); } return; } else if ( Q_stricmp( command, "ExportAndPlay" ) == 0 || Q_stricmp( command, "ExportAndPlayClean" ) == 0 ) { m_pMapLayout->SaveMapLayout( "maps/output.layout" ); if ( engine ) { char buffer[512]; if ( Q_stricmp( command, "ExportAndPlayClean" ) == 0 ) { Q_snprintf(buffer, sizeof(buffer), "asw_spawning_enabled 0; asw_build_map %s", "output.layout" ); } else { Q_snprintf(buffer, sizeof(buffer), "asw_spawning_enabled 1; asw_build_map %s", "output.layout" ); } engine->ClientCmd_Unrestricted( buffer ); } else { MessageBox *pMessage = new MessageBox ("Map Exported", "To build and play map, enter into the console:\n\nasw_build_map output", this); pMessage->DoModal(); } } else if ( Q_stricmp( command, "NewMapLayout" ) == 0 ) { // todo: check the current map is saved, print a warning if not? delete m_pMapLayout; m_pMapLayout = new CMapLayout(); return; } else if ( Q_stricmp( command, "OpenMapLayout" ) == 0 ) { // todo: check the current map is saved, print a warning if not? DoFileOpen(); return; } else if ( Q_stricmp( command, "OpenCurrentMapLayout" ) == 0 ) { // todo: check the current map is saved, print a warning if not? // clear the current map layout, if any delete m_pMapLayout; m_pMapLayout = new CMapLayout(); char mapname[ 255 ]; Q_snprintf( mapname, sizeof( mapname ), "maps/%s.layout", engine->GetLevelNameShort() ); // load in the new one if ( !m_pMapLayout->LoadMapLayout( mapname ) ) { MessageBox *pMessage = new MessageBox ("Error", "Error loading map layout", this); pMessage->DoModal(); return; } // make sure each placed CRoom has a UI panel GetMapLayoutPanel()->CreateAllUIPanels(); return; } else if ( Q_stricmp( command, "SaveMapLayout" ) == 0 ) { if (Q_strlen(m_pMapLayout->m_szFilename) <= 0) { DoFileSaveAs(); } else { m_pMapLayout->SaveMapLayout(m_pMapLayout->m_szFilename); } return; } else if ( Q_stricmp( command, "SaveMapLayoutAs" ) == 0 ) { DoFileSaveAs(); return; } else if ( Q_stricmp( command, "GenerateThumbnails" ) == 0 ) { if ( p4 ) { MessageBox *pMessage = new MessageBox( "Add to P4?", "Would you like to add the thumbnails to perforce?", this ); pMessage->SetCancelButtonVisible( true ); pMessage->SetOKButtonText( "#MessageBox_Yes" ); pMessage->SetCancelButtonText( "#MessageBox_No" ); pMessage->SetCommand( new KeyValues( "AddToP4", "add", "1" ) ); pMessage->SetCancelCommand( new KeyValues( "AddToP4", "add", "0" ) ); pMessage->DoModal(); return; } GenerateRoomThumbnails(); return; } else if ( !Q_stricmp( command, "PushEncountersApart" ) ) { CASWMissionChooserNPCs::PushEncountersApart( m_pMapLayout ); } else if ( Q_stricmp( command, "LocationLayoutEditor" ) == 0 ) { vgui::Frame *pFrame = new CLocation_Editor_Frame( this, "LocationEditorFrame" ); pFrame->MoveToCenterOfScreen(); pFrame->Activate(); return; } else if ( Q_stricmp( command, "ZoomIn" ) == 0 ) { Zoom( 10 ); return; } else if ( Q_stricmp( command, "ZoomOut" ) == 0 ) { Zoom( -10 ); return; } else if ( !Q_stricmp( command, "TileCheck" ) ) { vgui::Frame *pFrame = new CTile_Check_Frame( this, "TileCheckFrame" ); pFrame->MoveToCenterOfScreen(); pFrame->Activate(); return; } else if ( !Q_stricmp( command, "ShowOptionalParameters" ) ) { m_pLayoutSystemPage->GetEditor()->ShowOptionalValues( m_pToolsMenu->IsChecked( m_nShowDefaultParametersMenuItemID ) ); return; } else if ( !Q_stricmp( command, "ShowAddButtons" ) ) { m_pLayoutSystemPage->GetEditor()->ShowAddButtons( m_pToolsMenu->IsChecked( m_nShowAddButtonsMenuItemID ) ); return; } BaseClass::OnCommand( command ); } void CTileGenDialog::OnUpdateCurrentTheme(KeyValues *params) { m_pCurrentThemeDetails->SetTheme(CLevelTheme::s_pCurrentTheme); m_pCurrentThemeDetails->InvalidateLayout(); m_pTemplateListPanel->UpdateRoomList(); m_pTemplateListContainer->MoveToTopLeft(); m_pTemplateListContainer->InvalidateLayout(); m_pTemplateListContainer->Repaint(); } void CTileGenDialog::OnAddToP4( KeyValues *pKV ) { GenerateRoomThumbnails( pKV->GetBool( "add" ) ); } void CTileGenDialog::PerformLayout() { BaseClass::PerformLayout(); if ( m_bFirstPerformLayout ) { int screenWide, screenTall; surface()->GetScreenSize(screenWide, screenTall); m_bFirstPerformLayout = false; int inset = screenWide * 0.02f; SetBounds( inset, inset, screenWide - inset * 3, screenTall - inset * 2 ); } } void CTileGenDialog::ApplySchemeSettings(vgui::IScheme *pScheme) { BaseClass::ApplySchemeSettings(pScheme); LoadControlSettings( "tilegen/TileGenDialog.res", "GAME" ); SetSizeable(true); SetMinimizeButtonVisible( false ); SetMaximizeButtonVisible( false ); SetCloseButtonVisible( true ); SetMenuButtonVisible( false ); m_bFirstPerformLayout = true; } void CTileGenDialog::SetCursorRoomTemplate( const CRoomTemplate *pRoomTemplate ) { const CRoomTemplate *pOldTemplate = m_pCursorTemplate; m_pCursorTemplate = pRoomTemplate; if (pRoomTemplate) { ClearRoomSelection(); m_pCursorPanel->SetRoomTemplate(pRoomTemplate); m_pCursorPanel->SetVisible(true); //vgui::ivgui()->AddTickSignal( GetVPanel(), 0 ); } else { m_pCursorPanel->SetVisible(false); //vgui::ivgui()->RemoveTickSignal( GetVPanel() ); } // update highlighting of the currently selected panel in the room template browser list if (pOldTemplate) m_pTemplateListPanel->UpdatePanelsWithTemplate(pOldTemplate); if (pRoomTemplate) m_pTemplateListPanel->UpdatePanelsWithTemplate(pRoomTemplate); } void CTileGenDialog::OnTick() { vgui::GetAnimationController()->UpdateAnimations( Plat_FloatTime() ); if (m_pCursorTemplate) { int mx, my; input()->GetCursorPos(mx, my); if (GetScrollingWindow() && GetScrollingWindow()->m_pView && GetScrollingWindow()->m_hChildPanel.Get() && GetScrollingWindow()->m_pView->IsCursorOver()) { m_pCursorPanel->SetVisible(true); // cursor is over the grid area, snap our position to the grid vgui::Panel *pGrid = GetScrollingWindow()->m_hChildPanel; pGrid->ScreenToLocal(mx, my); int iTileX = mx / RoomTemplatePanelTileSize(); int iTileY = (my / RoomTemplatePanelTileSize()) + 1; mx = iTileX * RoomTemplatePanelTileSize(); my = iTileY * RoomTemplatePanelTileSize(); pGrid->LocalToScreen(mx, my); ScreenToLocal(mx, my); my -= m_pCursorPanel->GetTall(); // relative to lower left m_pCursorPanel->SetPos(mx, my); m_pCursorPanel->InvalidateLayout(); Repaint(); } else { if (m_pCursorPanel->IsVisible()) { m_pCursorPanel->SetVisible(false); Repaint(); } } } // check for dragging rooms if (m_iStartDragX != -1) { if (!input()->IsMouseDown( MOUSE_LEFT )) { // stop dragging rooms m_iStartDragX = -1; m_iStartDragY = -1; } else { // if the mouse x/y tile changes, then we need to move rooms int iTileX = 0; int iTileY = 0; GetMapLayoutPanel()->GetCursorTile(iTileX, iTileY); if (iTileX != m_iStartDragX || iTileY != m_iStartDragY) { int xdiff = iTileX - m_iStartDragX; int ydiff = iTileY - m_iStartDragY; // check the rooms fit at the new location // TODO check we're not dragging out of map bounds! bool bCollision = false; int iRooms = m_SelectedRooms.Count(); for (int i=0; im_pRoomTemplate ) { bCollision = true; break; } for ( int x=0;xm_pRoomTemplate->GetTilesX() && !bCollision;x++ ) { for ( int y=0;ym_pRoomTemplate->GetTilesY();y++ ) { if ( m_pMapLayout->m_pRoomGrid[ pRoom->m_iPosX + xdiff + x ][ pRoom->m_iPosY + ydiff + y ] != NULL && !IsASelectedRoom( m_pMapLayout->m_pRoomGrid[ pRoom->m_iPosX + xdiff + x ][ pRoom->m_iPosY + ydiff + y ] ) ) { bCollision = true; break; } } } } if ( !bCollision ) { // shift all selected rooms by that amount for (int i=0; iRemoveRoom( pRoom ); pRoom->m_iPosX += xdiff; pRoom->m_iPosY += ydiff; m_pMapLayout->PlaceRoom( pRoom ); if ( pRoom->m_pPlacedRoomPanel ) { CPlacedRoomTemplatePanel *pPanel = dynamic_cast(pRoom->m_pPlacedRoomPanel); if ( pPanel ) { pPanel->OnDragged(); } } } Repaint(); // restart dragging from the new cursor location OnStartDraggingSelectedRooms(); } } } } // Display the coordinate and room template name beneath the mouse cursor int nMouseX, nMouseY; char coordinateString[200]; input()->GetCursorPos( nMouseX, nMouseY ); if ( GetMapLayoutPanel()->IsWithin( nMouseX, nMouseY ) && m_pPropertySheet->GetActivePage() == static_cast< Panel * >( m_pLayoutPage ) ) { int nTileX = 0, nTileY = 0; GetMapLayoutPanel()->GetCursorTile( nTileX, nTileY ); CRoom *pRoom = NULL; if ( m_pMapLayout && nTileX >= 0 && nTileY >= 0 && nTileX < MAP_LAYOUT_TILES_WIDE && nTileY < MAP_LAYOUT_TILES_WIDE ) { pRoom = m_pMapLayout->GetRoom( nTileX, nTileY ); } if ( pRoom != NULL ) { char roomName[MAX_PATH]; pRoom->GetFullRoomName( roomName, MAX_PATH ); Q_snprintf( coordinateString, 200, "(%d, %d) - %s", nTileX, nTileY, roomName ); } else { Q_snprintf( coordinateString, 200, "(%d, %d)", nTileX, nTileY ); } } else { coordinateString[0] = '\0'; } m_pCoordinateLabel->SetText( coordinateString ); // Give the new layout system time to perform 1 iteration. if ( m_pLayoutSystem != NULL && m_pLayoutSystem->IsGenerating() ) { m_pLayoutSystem->ExecuteIteration(); // Make sure each placed CRoom has a UI panel GetMapLayoutPanel()->CreateAllUIPanels(); } } bool CTileGenDialog::IsASelectedRoom( CRoom *pRoom ) { if ( !pRoom ) return false; return ( m_SelectedRooms.Find( pRoom ) != m_SelectedRooms.InvalidIndex() ); } void CTileGenDialog::OnCheckButtonChecked(Panel *panel) { if ( panel == m_pShowExitsCheck ) { m_bShowExits = m_pShowExitsCheck->IsSelected(); CRoomTemplatePanel::UpdateAllImages(); Repaint(); } else if ( panel == m_pShowTileSquaresCheck ) { m_bShowTileSquares = m_pShowTileSquaresCheck->IsSelected(); CRoomTemplatePanel::UpdateAllImages(); Repaint(); } } void CTileGenDialog::OnTextChanged( Panel *panel ) { if ( panel == m_pTemplateFilter ) { char buf[CRoomTemplateListPanel::m_nFilterTextLength]; m_pTemplateFilter->GetText( buf, CRoomTemplateListPanel::m_nFilterTextLength ); m_pTemplateListPanel->SetFilterText( buf ); m_pTemplateListPanel->InvalidateLayout(); } } void CTileGenDialog::DoFileOpen() { m_FileSelectType = FST_LAYOUT_OPEN; // Pop up the dialog FileOpenDialog *pFileDialog = new FileOpenDialog(NULL, "Open Map Layout", true); pFileDialog->SetStartDirectory( g_layoutsdir ); pFileDialog->AddFilter("*.layout", "Map Layout (*.layout)", true); pFileDialog->AddActionSignalTarget(this); pFileDialog->DoModal(false); } void CTileGenDialog::DoFileSaveAs() { m_FileSelectType = FST_LAYOUT_SAVE_AS; // Pop up the dialog FileOpenDialog *pFileDialog = new FileOpenDialog (NULL, "Save Map Layout", false); pFileDialog->SetStartDirectory( g_layoutsdir ); pFileDialog->AddFilter("*.layout", "Map Layout (*.layout)", true); pFileDialog->AddActionSignalTarget(this); pFileDialog->DoModal(true); } void CTileGenDialog::OnFileSelected( const char *fullpath ) { if ( m_FileSelectType == FST_LAYOUT_SAVE_AS ) { m_pMapLayout->SetCurrentFilename(fullpath); m_pMapLayout->SaveMapLayout(fullpath); } else if ( m_FileSelectType == FST_LAYOUT_OPEN ) { // clear the current map layout, if any delete m_pMapLayout; m_pMapLayout = new CMapLayout(); // load in the new one if ( !m_pMapLayout->LoadMapLayout(fullpath) ) { MessageBox *pMessage = new MessageBox ("Error", "Error loading map layout", this); pMessage->DoModal(); return; } // make sure each placed CRoom has a UI panel GetMapLayoutPanel()->CreateAllUIPanels(); } } void CTileGenDialog::GenerateMission( const char *szMissionFile ) { m_pPropertySheet->SetActivePage( m_pPropertySheet->GetPage( 0 ) ); m_pGenerationOptions->Clear(); m_pGenerationOptions->LoadFromFile( g_pFullFileSystem, szMissionFile, "GAME" ); if ( IsGenerating() ) { MessageBox *pMessage = new MessageBox ("Error", "Already generating a layout. Please wait for this to finish before starting a new one.", this); pMessage->DoModal(); return; } delete m_pLayoutSystem; m_pLayoutSystem = NULL; // Preprocess the mission file (i.e. apply rules) if ( m_pLayoutSystemPage->GetPreprocessor()->SubstituteRules( m_pGenerationOptions ) ) { KeyValues *pMissionSettings = m_pGenerationOptions->FindKey( "mission_settings" ); if ( pMissionSettings == NULL ) { MessageBox *pMessage = new MessageBox( "Error", "Mission is missing a Global Options block.", this ); pMessage->DoModal(); return; } // Copy the filename key over pMissionSettings->SetString( "Filename", m_pGenerationOptions->GetString( "Filename", "invalid_filename" ) ); if ( tilegen_preprocess_mission.GetBool() ) { m_pGenerationOptions->SaveToFile( g_pFullFileSystem, "preprocessed_mission.txt", "GAME" ); } m_pLayoutSystem = new CLayoutSystem(); AddListeners( m_pLayoutSystem ); if ( !m_pLayoutSystem->LoadFromKeyValues( m_pGenerationOptions ) ) { MessageBox *pMessage = new MessageBox( "Error", "Failed to load mission from pre-processed key-values.", this ); pMessage->DoModal(); return; } // Clear the current layout // @TODO: check the current map is saved, print a warning if not? delete m_pMapLayout; m_pMapLayout = new CMapLayout( pMissionSettings->MakeCopy() ); m_pLayoutSystem->BeginGeneration( m_pMapLayout ); } else { MessageBox *pMessage = new MessageBox ("Error", "Failed to pre-process layout system definition.", this); pMessage->DoModal(); return; } } bool CTileGenDialog::IsGenerating() { return m_pLayoutSystem != NULL && m_pLayoutSystem->IsGenerating(); } MessageMapItem_t CTileGenDialog::m_MessageMap[] = { MAP_MESSAGE_CONSTCHARPTR(CTileGenDialog, "FileSelected", OnFileSelected, "fullpath"), }; IMPLEMENT_PANELMAP(CTileGenDialog, Frame); // adds or removes a room from the current selection void CTileGenDialog::ToggleRoomSelection(CRoom* pRoom) { if (m_SelectedRooms.Find(pRoom) != -1) { m_SelectedRooms.FindAndRemove(pRoom); } else { m_SelectedRooms.AddToTail(pRoom); } pRoom->m_pPlacedRoomPanel->InvalidateLayout(true, true); } // sets the room selection to just the specified room void CTileGenDialog::SetRoomSelection(CRoom* pRoom) { ClearRoomSelection(); m_SelectedRooms.AddToTail(pRoom); pRoom->m_pPlacedRoomPanel->InvalidateLayout(true, true); } void CTileGenDialog::ClearRoomSelection() { // store previously selected rooms CUtlVector m_PreviouslySelectedRooms; int iRooms = m_SelectedRooms.Count(); for (int i=0; im_pPlacedRoomPanel ) { m_PreviouslySelectedRooms[i]->m_pPlacedRoomPanel->InvalidateLayout(true, true); } } } void CTileGenDialog::OnKeyCodeTyped( vgui::KeyCode code ) { if ( code == KEY_DELETE ) // delete all selected rooms if you press DEL { if (m_SelectedRooms.Count() > 0) { // note: deleting the CRoom objects causes them to be removed from the CMapLayout and their panels deleted // so we're deleting them and clearing out our selected room list at the same time m_SelectedRooms.PurgeAndDeleteElements(); Repaint(); } } else if ( code == KEY_SPACE ) { g_bProcessGenerator = true; } else { BaseClass::OnKeyCodeTyped( code ); } } void CTileGenDialog::OnStartDraggingSelectedRooms() { GetMapLayoutPanel()->GetCursorTile(m_iStartDragX, m_iStartDragY); } #define THUMBNAILS_FILE "resource/roomthumbnails.txt" void CTileGenDialog::GenerateRoomThumbnails( bool bAddToPerforce ) { // save out a keyvalues file with position/size of each room KeyValues *pKV = new KeyValues( "RoomThumbnails" ); char filename[MAX_PATH]; for ( int i=0;im_PlacedRooms.Count();i++ ) { CRoom *pRoom = m_pMapLayout->m_PlacedRooms[i]; if ( !pRoom ) continue; KeyValues *pkvEntry = new KeyValues( "Thumbnail" ); Q_snprintf( filename, sizeof( filename ), "tilegen/roomtemplates/%s/%s.tga", pRoom->m_pRoomTemplate->m_pLevelTheme->m_szName, pRoom->m_pRoomTemplate->GetFullName() ); pkvEntry->SetString( "Filename", filename ); int half_map_size = MAP_LAYOUT_TILES_WIDE * 0.5f; // shift back so the middle of our grid is the origin float xPos = (pRoom->m_iPosX - half_map_size) * ASW_TILE_SIZE; float yPos = (pRoom->m_iPosY - half_map_size) * ASW_TILE_SIZE; pkvEntry->SetFloat( "RoomX", xPos ); pkvEntry->SetFloat( "RoomY", yPos + pRoom->m_pRoomTemplate->GetTilesY() * ASW_TILE_SIZE ); pkvEntry->SetFloat( "RoomWide", pRoom->m_pRoomTemplate->GetTilesX() * ASW_TILE_SIZE ); pkvEntry->SetFloat( "RoomTall", pRoom->m_pRoomTemplate->GetTilesY() * ASW_TILE_SIZE ); pkvEntry->SetFloat( "OutputWide", pRoom->m_pRoomTemplate->GetTilesX() * RoomTemplatePanelTileSize() ); pkvEntry->SetFloat( "OutputTall", pRoom->m_pRoomTemplate->GetTilesY() * RoomTemplatePanelTileSize() ); pKV->AddSubKey( pkvEntry ); } if ( !pKV->SaveToFile( g_pFullFileSystem, THUMBNAILS_FILE, "GAME" ) ) { Msg( "Error: Couldn't save %s\n", THUMBNAILS_FILE ); pKV->deleteThis(); return; } pKV->deleteThis(); m_pMapLayout->SaveMapLayout( "maps/output.layout" ); if ( engine ) { char buffer[256]; Q_snprintf(buffer, sizeof(buffer), "asw_random_weapons 0; asw_building_room_thumbnails 1; asw_add_room_thumbnails_to_perforce %d; asw_build_map %s", bAddToPerforce ? 1 : 0, "output.layout" ); engine->ClientCmd_Unrestricted( buffer ); } } void CTileGenDialog::Zoom( int iChange ) { int scroll_min = 0, scroll_max = 0, scroll_value; GetScrollingWindow()->m_pHorizScrollbar->GetRange( scroll_min, scroll_max ); scroll_value = GetScrollingWindow()->m_pHorizScrollbar->GetValue(); float flHorizFraction = ( float ) (scroll_value - scroll_min ) / (float) ( scroll_max - scroll_min ); GetScrollingWindow()->m_pVertScrollbar->GetRange( scroll_min, scroll_max ); scroll_value = GetScrollingWindow()->m_pVertScrollbar->GetValue(); float flVertFraction = ( float ) (scroll_value - scroll_min ) / (float) ( scroll_max - scroll_min ); m_fTileSize = clamp( m_fTileSize + iChange, 10, 40 ); InvalidateLayout( true, true ); GetScrollingWindow()->m_pHorizScrollbar->GetRange( scroll_min, scroll_max ); scroll_value = scroll_min + ( flHorizFraction * ( scroll_max - scroll_min ) ); GetScrollingWindow()->m_pHorizScrollbar->SetValue( scroll_value ); GetScrollingWindow()->m_pVertScrollbar->GetRange( scroll_min, scroll_max ); scroll_value = scroll_min + ( flVertFraction * ( scroll_max - scroll_min ) ); GetScrollingWindow()->m_pVertScrollbar->SetValue( scroll_value ); InvalidateLayout( true, true ); Repaint(); } CScrollingWindow* CTileGenDialog::GetScrollingWindow() { return m_pLayoutPage->m_pScrollingWindow; } CMapLayoutPanel* CTileGenDialog::GetMapLayoutPanel() { return m_pLayoutPage->m_pMapLayoutPanel; } void CTileGenDialog::StartRubberBandSelection( int mx, int my ) { m_iStartRubberBandX = mx; m_iStartRubberBandY = my; } void CTileGenDialog::EndRubberBandSelection( int mx, int my ) { ClearRoomSelection(); // now add all rooms between the selection int iStartTileX = m_iStartRubberBandX / RoomTemplatePanelTileSize(); int iStartTileY = (m_iStartRubberBandY / RoomTemplatePanelTileSize()); iStartTileY = MapLayoutTilesWide() - iStartTileY - 1; // reverse the Y axis int iEndTileX = mx / RoomTemplatePanelTileSize(); int iEndTileY = (my / RoomTemplatePanelTileSize()); iEndTileY = MapLayoutTilesWide() - iEndTileY - 1; // reverse the Y axis if ( iStartTileX > iEndTileX ) { int i = iStartTileX; iStartTileX = iEndTileX; iEndTileX = i; } if ( iStartTileY > iEndTileY ) { int i = iStartTileY; iStartTileY = iEndTileY; iEndTileY = i; } int iRooms = m_pMapLayout->m_PlacedRooms.Count(); for ( int i = 0; i < iRooms; i++ ) { CRoom *pRoom = m_pMapLayout->m_PlacedRooms[i]; if ( pRoom->m_iPosX < iEndTileX && ( pRoom->m_iPosX + pRoom->m_pRoomTemplate->GetTilesX() ) > iStartTileX && pRoom->m_iPosY < iEndTileY && ( pRoom->m_iPosY + pRoom->m_pRoomTemplate->GetTilesY() ) > iStartTileY ) { ToggleRoomSelection( pRoom ); } } m_iStartRubberBandX = -1; } bool CTileGenDialog::GetRubberBandStart( int &sx, int &sy ) { if ( m_iStartRubberBandX == -1 ) return false; sx = m_iStartRubberBandX; sy = m_iStartRubberBandY; return true; } void cc_kv_editor( const CCommand &args ) { CKV_Editor_Frame *pFrame = new CKV_Editor_Frame( g_pTileGenDialog->GetParent(), "KVEditor" ); pFrame->MoveToCenterOfScreen(); pFrame->Activate(); } static ConCommand kv_editor( "kv_editor", cc_kv_editor, 0 ); char* TileGenCopyString( const char *szString ) { if ( !szString ) return NULL; int len = Q_strlen( szString ) + 1; if ( len <= 1 ) return NULL; char *text = new char[ len ]; Q_strncpy( text, szString, len ); return text; }