Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

365 lines
12 KiB

#include "tilegen_pages.h"
#include "kv_editor.h"
#include "layout_system/tilegen_mission_preprocessor.h"
#include "layout_system_editor/layout_system_kv_editor.h"
#include "MapLayoutPanel.h"
#include "ScrollingWindow.h"
#include <vgui_controls/PanelListPanel.h>
#include <vgui_controls/Button.h>
#include <vgui_controls/TreeView.h>
#include "asw_mission_chooser.h"
#include "asw_key_values_database.h"
#include "filesystem.h"
#include "asw_system.h"
#include "TileGenDialog.h"
#include <vgui_controls/FileOpenDialog.h>
#include <vgui_controls/MessageBox.h>
#include "p4lib/ip4.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// ==============================================================================================
CTileGenLayoutPage::CTileGenLayoutPage( Panel *parent, const char *name ) : BaseClass( parent, name )
{
m_pScrollingWindow = new CScrollingWindow( this, "ScrollingWindow" );
m_pMapLayoutPanel = new CMapLayoutPanel( this, "MapLayoutPanel" );
m_pScrollingWindow->SetChildPanel( m_pMapLayoutPanel );
m_pScrollingWindow->MoveToLowerCenter();
}
void CTileGenLayoutPage::ApplySchemeSettings( vgui::IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
LoadControlSettings( "tilegen/TileGenLayoutPage.res", "GAME" );
}
// =============================================================================================
static bool MissionTreeSortFunc( KeyValues *pItem1, KeyValues *pItem2 )
{
return Q_stricmp( pItem1->GetString( "Text" ), pItem2->GetString( "Text" ) ) < 0;
}
MessageMapItem_t CTilegenKVEditorPage::m_MessageMap[] =
{
MAP_MESSAGE_CONSTCHARPTR( CTilegenKVEditorPage, "FileSelected", OnFileSelected, "fullpath" ),
};
IMPLEMENT_PANELMAP( CTilegenKVEditorPage, vgui::EditablePanel );
CTilegenKVEditorPage::CTilegenKVEditorPage( Panel *pParent, const char *pName ) :
BaseClass( pParent, pName )
{
m_pTree = new vgui::TreeView( this, "Tree" );
m_pTree->MakeReadyForUse();
m_pTree->SetSortFunc( MissionTreeSortFunc );
m_szFilename[0] = '\0';
m_pFilenameLabel = new vgui::Label( this, "FilenameLabel", "" );
m_pSaveButton = new vgui::Button( this, "SaveButton", "Save", this, "Save" );
m_pNewButton = new vgui::Button( this, "NewButton", "New", this, "New" );
}
void CTilegenKVEditorPage::ApplySchemeSettings(vgui::IScheme *pScheme)
{
BaseClass::ApplySchemeSettings( pScheme );
HFont treeFont = pScheme->GetFont( "DefaultVerySmall" );
m_pTree->SetFont( treeFont );
}
void CTilegenKVEditorPage::OnCommand( const char *command )
{
if ( !Q_stricmp( command, "Save" ) )
{
KeyValues *pKV = GetKeyValues();
if ( pKV != NULL && !pKV->SaveToFile( g_pFullFileSystem, m_szFilename, "GAME" ) )
{
if ( p4 )
{
char fullPath[MAX_PATH];
g_pFullFileSystem->RelativePathToFullPath( m_szFilename, "GAME", fullPath, MAX_PATH );
if ( p4->IsFileInPerforce( fullPath ) )
{
MessageBox *pMessage = new MessageBox( "Check Out?", "File is not writeable. Would you like to check it out from Perforce?", this );
pMessage->SetCancelButtonVisible( true );
pMessage->SetOKButtonText( "#MessageBox_Yes" );
pMessage->SetCancelButtonText( "#MessageBox_No" );
pMessage->SetCommand( new KeyValues( "CheckOutFromP4", "file", fullPath ) );
pMessage->DoModal();
return;
}
}
else
{
VGUIMessageBox( this, "Save Error", "Failed to save %s. Make sure file is checked out from Perforce.", m_szFilename );
}
}
UpdateList();
return;
}
else if ( !Q_stricmp( command, "New" ) )
{
SaveNew();
return;
}
BaseClass::OnCommand( command );
}
void CTilegenKVEditorPage::OnCheckOutFromP4( KeyValues *pKV )
{
const char *pPath = pKV->GetString( "file", NULL );
if ( pPath != NULL )
{
if ( p4->OpenFileForEdit( pPath ) )
{
KeyValues *pKV = GetKeyValues();
if ( !pKV->SaveToFile( g_pFullFileSystem, m_szFilename, "GAME" ) )
{
VGUIMessageBox( this, "Save Error!", "Checked out '%s' from Perforce, but failed to save file.", pPath );
}
}
else
{
VGUIMessageBox( this, "P4 Error!", "Failed to check out '%s' from Perforce.", pPath );
}
}
UpdateList();
}
int CTilegenKVEditorPage::RecursiveCreateFolderNodes( int iParentIndex, char *szFilename )
{
char* szFolder = (char*) Q_strnchr( szFilename, CORRECT_PATH_SEPARATOR, Q_strlen( szFilename ) );
if ( !szFolder )
{
return iParentIndex;
}
*szFolder = 0;
// check if szFilename node exists already
for ( int i = 0; i < m_pTree->GetNumChildren( iParentIndex ); i++ )
{
int iChild = m_pTree->GetChild( iParentIndex, i );
KeyValues *pKV = m_pTree->GetItemData( iChild );
if ( !Q_stricmp( szFilename, pKV->GetString( "Text" ) ) )
{
return iChild;
}
}
KeyValues *pFolderEntry = new KeyValues("FolderEntry");
pFolderEntry->SetString( "Text", szFilename );
int iItem = m_pTree->AddItem( pFolderEntry, iParentIndex );
m_pTree->SetItemFgColor( iItem, Color(128, 128, 128, 255) );
return RecursiveCreateFolderNodes( iItem, szFolder + 1 );
}
void CTilegenKVEditorPage::UpdateListHelper( CASW_KeyValuesDatabase *pMissionDatabase, const char *pFolderName )
{
m_pTree->RemoveAll();
KeyValues *kv = new KeyValues( "TVI" );
kv->SetString( "Text", "Missions" );
int missionIndex = m_pTree->AddItem( kv, -1 );
for ( int i = 0; i < pMissionDatabase->GetFileCount(); i++ )
{
char szMissionFilename[MAX_PATH];
Q_strncpy( szMissionFilename, pMissionDatabase->GetFilename( i ), MAX_PATH );
Q_FixSlashes( szMissionFilename );
int iNode = RecursiveCreateFolderNodes( missionIndex, szMissionFilename + Q_strlen( pFolderName ) );
if ( iNode != missionIndex )
{
m_pTree->ExpandItem( iNode, true );
}
Q_strncpy( szMissionFilename, pMissionDatabase->GetFilename( i ), MAX_PATH );
Q_FileBase( szMissionFilename, szMissionFilename, sizeof( szMissionFilename ) );
KeyValues *pMissionEntry = new KeyValues("MissionEntry");
pMissionEntry->SetString( "Text", szMissionFilename );
pMissionEntry->SetString( "Filename", pMissionDatabase->GetFilename( i ) );
int iItem = m_pTree->AddItem( pMissionEntry, iNode );
m_pTree->SetItemFgColor( iItem, Color(255, 255, 255, 255) );
}
m_pTree->ExpandItem( missionIndex, true );
}
// ==============================================================================================
CTilegenLayoutSystemPage::CTilegenLayoutSystemPage( Panel *pParent, const char *pName ) :
BaseClass( pParent, pName )
{
m_pPreprocessor = new CTilegenMissionPreprocessor();
m_pEditor = new CLayoutSystemKVEditor( this, "Editor" );
m_pEditor->SetPreprocessor( m_pPreprocessor );
m_pGenerateButton = new vgui::Button( this, "GenerateButton", "Generate", this, "Generate" );
m_pReloadRulesButton = new vgui::Button( this, "ReloadRulesButton", "Reload Rules", this, "ReloadRules" );
m_pReloadMissionButton = new vgui::Button( this, "ReloadMissionButton", "Reload", this, "ReloadMission" );
m_pNewMissionDatabase = new CASW_KeyValuesDatabase();
m_pNewMissionDatabase->LoadFiles( "tilegen/new_missions/" );
m_pRulesDatabase = new CASW_KeyValuesDatabase();
m_pRulesDatabase->LoadFiles( "tilegen/rules/" );
for ( int i = 0; i < m_pRulesDatabase->GetFileCount(); ++ i )
{
m_pPreprocessor->ParseAndStripRules( m_pRulesDatabase->GetFile( i ) );
}
}
CTilegenLayoutSystemPage::~CTilegenLayoutSystemPage()
{
delete m_pNewMissionDatabase;
delete m_pRulesDatabase;
delete m_pPreprocessor;
}
void CTilegenLayoutSystemPage::ApplySchemeSettings( vgui::IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
LoadControlSettings( "tilegen/TilegenLayoutSystemPage.res", "GAME" );
bool bItemSelected = ( m_szFilename[0] != '\0' );
m_pReloadMissionButton->SetVisible( bItemSelected );
m_pSaveButton->SetVisible( bItemSelected );
m_pGenerateButton->SetVisible( bItemSelected );
}
void CTilegenLayoutSystemPage::OnCommand( const char *pCommand )
{
if ( Q_stricmp( pCommand, "Generate" ) == 0 )
{
if ( g_pTileGenDialog )
{
g_pTileGenDialog->GenerateMission( m_szFilename );
}
return;
}
else if ( Q_stricmp( pCommand, "ReloadMission" ) == 0 )
{
KeyValues *pNewKeyValues = m_pNewMissionDatabase->ReloadFile( m_szFilename );
if ( pNewKeyValues != NULL )
{
m_pEditor->SetMissionData( m_pNewMissionDatabase->GetFileByName( m_szFilename ) );
}
return;
}
else if ( Q_stricmp( pCommand, "ReloadRules" ) == 0 )
{
delete m_pPreprocessor;
m_pPreprocessor = new CTilegenMissionPreprocessor();
m_pRulesDatabase->LoadFiles( "tilegen/rules/" );
for ( int i = 0; i < m_pRulesDatabase->GetFileCount(); ++ i )
{
m_pPreprocessor->ParseAndStripRules( m_pRulesDatabase->GetFile( i ) );
}
m_pEditor->SetPreprocessor( m_pPreprocessor );
m_pEditor->RecreateMissionPanel();
return;
}
BaseClass::OnCommand( pCommand );
}
void CTilegenLayoutSystemPage::OnTreeViewItemSelected( int nItemIndex )
{
KeyValues *pKV = m_pTree->GetItemData( nItemIndex );
if ( pKV && pKV->FindKey( "Filename" ) )
{
const char *pFilename = pKV->GetString( "Filename" );
KeyValues *pExistingKV = m_pEditor->GetMissionData();
KeyValues *pNewKV = m_pNewMissionDatabase->GetFileByName( pFilename );
// @TODO: this is lame, this function gets called twice when the user selects a new node.
if ( pExistingKV != pNewKV )
{
m_pEditor->SetMissionData( pNewKV );
m_pFilenameLabel->SetText( pFilename + Q_strlen( "tilegen/" ) );
m_pReloadMissionButton->SetVisible( true );
m_pSaveButton->SetVisible( true );
m_pGenerateButton->SetVisible( true );
Q_snprintf( m_szFilename, MAX_PATH, "%s", pFilename );
}
}
}
void CTilegenLayoutSystemPage::OnFileSelected( const char *pFullPath )
{
if ( g_pFullFileSystem->FileExists( pFullPath ) )
{
VGUIMessageBox( this, "Save Error", "A layout system already exists with filename %s.", pFullPath );
return;
}
// create new keys
KeyValues *pNewKeys = new KeyValues( "Mission" );
// save them at the full path
if ( !pNewKeys->SaveToFile( g_pFullFileSystem, pFullPath ) )
{
VGUIMessageBox( this, "Save Error", "Failed to save new file %s.", pFullPath );
return;
}
m_pEditor->SetMissionData( pNewKeys );
m_pReloadMissionButton->SetVisible( true );
m_pSaveButton->SetVisible( true );
m_pGenerateButton->SetVisible( true );
bool bConverted = g_pFullFileSystem->FullPathToRelativePath( pFullPath, m_szFilename, MAX_PATH );
if ( !bConverted )
{
Warning( "Failed to convert this to a relative path: %s\n", pFullPath );
return;
}
m_pFilenameLabel->SetText( m_szFilename + Q_strlen( "tilegen/" ) );
m_pNewMissionDatabase->AddFile( pNewKeys, m_szFilename );
UpdateList();
if ( p4 )
{
MessageBox *pMessage = new MessageBox( "Add to P4?", "Would you like to add this mission to perforce?", this );
pMessage->SetCancelButtonVisible( true );
pMessage->SetOKButtonText( "#MessageBox_Yes" );
pMessage->SetCancelButtonText( "#MessageBox_No" );
pMessage->SetCommand( new KeyValues( "AddToP4", "file", pFullPath ) );
pMessage->DoModal();
}
}
void CTilegenLayoutSystemPage::OnAddToP4( KeyValues *pKV )
{
const char *pPath = pKV->GetString( "file", NULL );
if ( pPath != NULL )
{
if ( !p4->OpenFileForAdd( pPath ) )
{
VGUIMessageBox( this, "P4 Error!", "Failed to add '%s' to Perforce.", pPath );
}
}
}
void CTilegenLayoutSystemPage::UpdateList()
{
UpdateListHelper( m_pNewMissionDatabase, "tilegen/new_missions/" );
}
void CTilegenLayoutSystemPage::SaveNew()
{
// Pop up the dialog
FileOpenDialog *pFileDialog = new FileOpenDialog (NULL, "Save New Layout System As...", false);
char buffer[MAX_PATH];
Q_snprintf( buffer, sizeof( buffer ), "%s/tilegen/new_missions", g_gamedir );
pFileDialog->SetStartDirectory( buffer );
pFileDialog->AddFilter("*.txt", "Layout System file (*.txt)", true);
pFileDialog->AddActionSignalTarget(this);
pFileDialog->DoModal(true);
}
KeyValues *CTilegenLayoutSystemPage::GetKeyValues()
{
return m_pEditor->GetMissionData();
}