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.
443 lines
14 KiB
443 lines
14 KiB
//========= 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 );
|
|
}
|
|
|
|
|