source-engine/tools/commedit/commeditdoc.cpp

444 lines
14 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include "commeditdoc.h"
#include "tier1/KeyValues.h"
#include "tier1/utlbuffer.h"
#include "toolutils/enginetools_int.h"
#include "filesystem.h"
#include "commedittool.h"
#include "toolframework/ienginetool.h"
#include "dmecommentarynodeentity.h"
#include "datamodel/idatamodel.h"
#include "toolutils/attributeelementchoicelist.h"
#include "commentarynodebrowserpanel.h"
#include "vgui_controls/messagebox.h"
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CCommEditDoc::CCommEditDoc( ICommEditDocCallback *pCallback ) : m_pCallback( pCallback )
{
m_hRoot = NULL;
m_pTXTFileName[0] = 0;
m_bDirty = false;
g_pDataModel->InstallNotificationCallback( this );
}
CCommEditDoc::~CCommEditDoc()
{
g_pDataModel->RemoveNotificationCallback( this );
}
//-----------------------------------------------------------------------------
// Inherited from INotifyUI
//-----------------------------------------------------------------------------
void CCommEditDoc::NotifyDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags )
{
OnDataChanged( pReason, nNotifySource, nNotifyFlags );
}
//-----------------------------------------------------------------------------
// Gets the file name
//-----------------------------------------------------------------------------
const char *CCommEditDoc::GetTXTFileName()
{
return m_pTXTFileName;
}
void CCommEditDoc::SetTXTFileName( const char *pFileName )
{
Q_strncpy( m_pTXTFileName, pFileName, sizeof( m_pTXTFileName ) );
Q_FixSlashes( m_pTXTFileName );
SetDirty( true );
}
//-----------------------------------------------------------------------------
// Dirty bits
//-----------------------------------------------------------------------------
void CCommEditDoc::SetDirty( bool bDirty )
{
m_bDirty = bDirty;
}
bool CCommEditDoc::IsDirty() const
{
return m_bDirty;
}
//-----------------------------------------------------------------------------
// Handles creation of the right element for a keyvalue
//-----------------------------------------------------------------------------
class CElementForKeyValueCallback : public IElementForKeyValueCallback
{
public:
const char *GetElementForKeyValue( const char *pszKeyName, int iNestingLevel )
{
if ( iNestingLevel == 1 && !Q_strncmp(pszKeyName, "entity", 6) )
return "DmeCommentaryNodeEntity";
return NULL;
}
};
//-----------------------------------------------------------------------------
// Saves/loads from file
//-----------------------------------------------------------------------------
bool CCommEditDoc::LoadFromFile( const char *pFileName )
{
Assert( !m_hRoot.Get() );
CAppDisableUndoScopeGuard guard( "CCommEditDoc::LoadFromFile", 0 );
SetDirty( false );
if ( !pFileName[0] )
return false;
char mapname[ 256 ];
// Compute the map name
const char *pMaps = Q_stristr( pFileName, "\\maps\\" );
if ( !pMaps )
return false;
// Build map name
//int nNameLen = (int)( (size_t)pComm - (size_t)pMaps ) - 5;
Q_StripExtension( pFileName, mapname, sizeof(mapname) );
char *pszFileName = (char*)Q_UnqualifiedFileName(mapname);
// Set the txt file name.
// If we loaded an existing commentary file, keep the same filename.
// If we loaded a .bsp, change the name & the extension.
if ( !V_stricmp( Q_GetFileExtension( pFileName ), "bsp" ) )
{
const char *pCommentaryAppend = "_commentary.txt";
Q_StripExtension( pFileName, m_pTXTFileName, sizeof(m_pTXTFileName)- strlen(pCommentaryAppend) - 1 );
Q_strcat( m_pTXTFileName, pCommentaryAppend, sizeof( m_pTXTFileName ) );
if ( g_pFileSystem->FileExists( m_pTXTFileName ) )
{
char pBuf[1024];
Q_snprintf( pBuf, sizeof(pBuf), "File %s already exists!\n", m_pTXTFileName );
m_pTXTFileName[0] = 0;
vgui::MessageBox *pMessageBox = new vgui::MessageBox( "Unable to overwrite file!\n", pBuf, g_pCommEditTool );
pMessageBox->DoModal( );
return false;
}
DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( m_pTXTFileName );
m_hRoot = CreateElement<CDmElement>( "root", fileid );
CDmrElementArray<> subkeys( m_hRoot->AddAttribute( "subkeys", AT_ELEMENT_ARRAY ) );
CDmElement *pRoot2 = CreateElement<CDmElement>( "Entities", fileid );
pRoot2->AddAttribute( "subkeys", AT_ELEMENT_ARRAY );
subkeys.AddToTail( pRoot2 );
g_pDataModel->SetFileRoot( fileid, m_hRoot );
}
else
{
char *pComm = Q_stristr( pszFileName, "_commentary" );
if ( !pComm )
{
char pBuf[1024];
Q_snprintf( pBuf, sizeof(pBuf), "File %s is not a commentary file!\nThe file name must end in _commentary.txt.\n", m_pTXTFileName );
m_pTXTFileName[0] = 0;
vgui::MessageBox *pMessageBox = new vgui::MessageBox( "Bad file name!\n", pBuf, g_pCommEditTool );
pMessageBox->DoModal( );
return false;
}
// Clip off the "_commentary" at the end of the filename
*pComm = '\0';
// This is not undoable
CDisableUndoScopeGuard guardFile;
CDmElement *pTXT = NULL;
CElementForKeyValueCallback KeyValuesCallback;
g_pDataModel->SetKeyValuesElementCallback( &KeyValuesCallback );
DmFileId_t fileid = g_pDataModel->RestoreFromFile( pFileName, NULL, "keyvalues", &pTXT );
g_pDataModel->SetKeyValuesElementCallback( NULL );
if ( fileid == DMFILEID_INVALID )
{
m_pTXTFileName[0] = 0;
return false;
}
SetTXTFileName( pFileName );
m_hRoot = pTXT;
}
guard.Release();
SetDirty( false );
char cmd[ 256 ];
Q_snprintf( cmd, sizeof( cmd ), "disconnect; map %s\n", pszFileName );
enginetools->Command( cmd );
enginetools->Execute( );
return true;
}
void CCommEditDoc::SaveToFile( )
{
if ( m_hRoot.Get() && m_pTXTFileName && m_pTXTFileName[0] )
{
g_pDataModel->SaveToFile( m_pTXTFileName, NULL, "keyvalues", "keyvalues", m_hRoot );
}
SetDirty( false );
}
//-----------------------------------------------------------------------------
// Returns the root object
//-----------------------------------------------------------------------------
CDmElement *CCommEditDoc::GetRootObject()
{
return m_hRoot;
}
//-----------------------------------------------------------------------------
// Returns the entity list
//-----------------------------------------------------------------------------
CDmAttribute *CCommEditDoc::GetEntityList()
{
CDmrElementArray<> mainKeys( m_hRoot, "subkeys" );
if ( !mainKeys.IsValid() || mainKeys.Count() == 0 )
return NULL;
CDmeHandle<CDmElement> hEntityList;
hEntityList = mainKeys[ 0 ];
return hEntityList ? hEntityList->GetAttribute( "subkeys", AT_ELEMENT_ARRAY ) : NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCommEditDoc::AddNewInfoTarget( const Vector &vecOrigin, const QAngle &angAngles )
{
CDmrCommentaryNodeEntityList entities( GetEntityList() );
if ( !entities.IsValid() )
return;
CDmeCommentaryNodeEntity *pTarget;
{
CAppUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Add Info Target", "Add Info Target" );
pTarget = CreateElement<CDmeCommentaryNodeEntity>( "target", entities.GetOwner()->GetFileId() );
pTarget->SetName( "entity" );
pTarget->SetValue( "classname", "info_target" );
pTarget->SetRenderOrigin( vecOrigin );
pTarget->SetRenderAngles( angAngles );
entities.AddToTail( pTarget );
pTarget->MarkDirty();
pTarget->DrawInEngine( true );
}
g_pCommEditTool->GetCommentaryNodeBrowser()->SelectNode( pTarget );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCommEditDoc::AddNewInfoTarget( void )
{
Vector vecOrigin;
QAngle angAngles;
float flFov;
clienttools->GetLocalPlayerEyePosition( vecOrigin, angAngles, flFov );
AddNewInfoTarget( vecOrigin, vec3_angle );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCommEditDoc::AddNewCommentaryNode( const Vector &vecOrigin, const QAngle &angAngles )
{
CDmrCommentaryNodeEntityList entities = GetEntityList();
CDmeCommentaryNodeEntity *pNode;
{
CAppUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Add Commentary Node", "Add Commentary Node" );
pNode = CreateElement<CDmeCommentaryNodeEntity>( "node", entities.GetOwner()->GetFileId() );
pNode->SetName( "entity" );
pNode->SetValue( "classname", "point_commentary_node" );
pNode->SetRenderOrigin( vecOrigin );
pNode->SetRenderAngles( angAngles );
pNode->SetValue<CUtlString>( "precommands", "" );
pNode->SetValue<CUtlString>( "postcommands", "" );
pNode->SetValue<CUtlString>( "commentaryfile", "" );
pNode->SetValue<CUtlString>( "viewtarget", "" );
pNode->SetValue<CUtlString>( "viewposition", "" );
pNode->SetValue<int>( "prevent_movement", 0 );
pNode->SetValue<CUtlString>( "speakers", "" );
pNode->SetValue<CUtlString>( "synopsis", "" );
entities.AddToTail( pNode );
pNode->MarkDirty();
pNode->DrawInEngine( true );
}
g_pCommEditTool->GetCommentaryNodeBrowser()->SelectNode( pNode );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCommEditDoc::AddNewCommentaryNode( void )
{
Vector vecOrigin;
QAngle angAngles;
float flFov;
clienttools->GetLocalPlayerEyePosition( vecOrigin, angAngles, flFov );
AddNewCommentaryNode( vecOrigin, vec3_angle );
}
//-----------------------------------------------------------------------------
// Deletes a commentary node
//-----------------------------------------------------------------------------
void CCommEditDoc::DeleteCommentaryNode( CDmElement *pRemoveNode )
{
CDmrCommentaryNodeEntityList entities = GetEntityList();
int nCount = entities.Count();
for ( int i = 0; i < nCount; ++i )
{
if ( pRemoveNode == entities[i] )
{
CAppUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Delete Commentary Node", "Delete Commentary Node" );
CDmeCommentaryNodeEntity *pNode = entities[ i ];
pNode->DrawInEngine( false );
entities.FastRemove( i );
return;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &vecOrigin -
// &angAbsAngles -
// Output : CDmeCommentaryNodeEntity
//-----------------------------------------------------------------------------
CDmeCommentaryNodeEntity *CCommEditDoc::GetCommentaryNodeForLocation( Vector &vecOrigin, QAngle &angAbsAngles )
{
CDmrCommentaryNodeEntityList entities = GetEntityList();
int nCount = entities.Count();
for ( int i = 0; i < nCount; ++i )
{
CDmeCommentaryNodeEntity *pNode = entities[ i ];
if ( !pNode )
continue;
Vector &vecAngles = *(Vector*)(&pNode->GetRenderAngles());
if ( pNode->GetRenderOrigin().DistTo( vecOrigin ) < 1e-3 && vecAngles.DistTo( *(Vector*)&angAbsAngles ) < 1e-1 )
return pNode;
}
return NULL;
}
//-----------------------------------------------------------------------------
// Populate string choice lists
//-----------------------------------------------------------------------------
bool CCommEditDoc::GetStringChoiceList( const char *pChoiceListType, CDmElement *pElement,
const char *pAttributeName, bool bArrayElement, StringChoiceList_t &list )
{
if ( !Q_stricmp( pChoiceListType, "info_targets" ) )
{
CDmrCommentaryNodeEntityList entities = GetEntityList();
StringChoice_t sChoice;
sChoice.m_pValue = "";
sChoice.m_pChoiceString = "";
list.AddToTail( sChoice );
int nCount = entities.Count();
for ( int i = 0; i < nCount; ++i )
{
CDmeCommentaryNodeEntity *pNode = entities[ i ];
if ( !pNode )
continue;
if ( !V_stricmp( pNode->GetClassName(), "info_target" ) )
{
sChoice.m_pValue = pNode->GetTargetName();
sChoice.m_pChoiceString = pNode->GetTargetName();
list.AddToTail( sChoice );
}
}
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Populate element choice lists
//-----------------------------------------------------------------------------
bool CCommEditDoc::GetElementChoiceList( const char *pChoiceListType, CDmElement *pElement,
const char *pAttributeName, bool bArrayElement, ElementChoiceList_t &list )
{
if ( !Q_stricmp( pChoiceListType, "allelements" ) )
{
AddElementsRecursively( m_hRoot, list );
return true;
}
if ( !Q_stricmp( pChoiceListType, "info_targets" ) )
{
CDmrCommentaryNodeEntityList entities = GetEntityList();
bool bFound = false;
int nCount = entities.Count();
for ( int i = 0; i < nCount; ++i )
{
CDmeCommentaryNodeEntity *pNode = entities[ i ];
if ( pNode && !V_stricmp( pNode->GetClassName(), "info_target" ) )
{
bFound = true;
ElementChoice_t sChoice;
sChoice.m_pValue = pNode;
sChoice.m_pChoiceString = pNode->GetTargetName();
list.AddToTail( sChoice );
}
}
return bFound;
}
// by default, try to treat the choice list type as a Dme element type
AddElementsRecursively( m_hRoot, list, pChoiceListType );
return list.Count() > 0;
}
//-----------------------------------------------------------------------------
// Called when data changes
//-----------------------------------------------------------------------------
void CCommEditDoc::OnDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags )
{
SetDirty( nNotifyFlags & NOTIFY_SETDIRTYFLAG ? true : false );
m_pCallback->OnDocChanged( pReason, nNotifySource, nNotifyFlags );
}