Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "commeditdoc.h"
  9. #include "tier1/KeyValues.h"
  10. #include "tier1/utlbuffer.h"
  11. #include "toolutils/enginetools_int.h"
  12. #include "filesystem.h"
  13. #include "commedittool.h"
  14. #include "toolframework/ienginetool.h"
  15. #include "dmecommentarynodeentity.h"
  16. #include "datamodel/idatamodel.h"
  17. #include "toolutils/attributeelementchoicelist.h"
  18. #include "commentarynodebrowserpanel.h"
  19. #include "vgui_controls/messagebox.h"
  20. //-----------------------------------------------------------------------------
  21. // Constructor
  22. //-----------------------------------------------------------------------------
  23. CCommEditDoc::CCommEditDoc( ICommEditDocCallback *pCallback ) : m_pCallback( pCallback )
  24. {
  25. m_hRoot = NULL;
  26. m_pTXTFileName[0] = 0;
  27. m_bDirty = false;
  28. g_pDataModel->InstallNotificationCallback( this );
  29. }
  30. CCommEditDoc::~CCommEditDoc()
  31. {
  32. g_pDataModel->RemoveNotificationCallback( this );
  33. }
  34. //-----------------------------------------------------------------------------
  35. // Inherited from INotifyUI
  36. //-----------------------------------------------------------------------------
  37. void CCommEditDoc::NotifyDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags )
  38. {
  39. OnDataChanged( pReason, nNotifySource, nNotifyFlags );
  40. }
  41. //-----------------------------------------------------------------------------
  42. // Gets the file name
  43. //-----------------------------------------------------------------------------
  44. const char *CCommEditDoc::GetTXTFileName()
  45. {
  46. return m_pTXTFileName;
  47. }
  48. void CCommEditDoc::SetTXTFileName( const char *pFileName )
  49. {
  50. Q_strncpy( m_pTXTFileName, pFileName, sizeof( m_pTXTFileName ) );
  51. Q_FixSlashes( m_pTXTFileName );
  52. SetDirty( true );
  53. }
  54. //-----------------------------------------------------------------------------
  55. // Dirty bits
  56. //-----------------------------------------------------------------------------
  57. void CCommEditDoc::SetDirty( bool bDirty )
  58. {
  59. m_bDirty = bDirty;
  60. }
  61. bool CCommEditDoc::IsDirty() const
  62. {
  63. return m_bDirty;
  64. }
  65. //-----------------------------------------------------------------------------
  66. // Handles creation of the right element for a keyvalue
  67. //-----------------------------------------------------------------------------
  68. class CElementForKeyValueCallback : public IElementForKeyValueCallback
  69. {
  70. public:
  71. const char *GetElementForKeyValue( const char *pszKeyName, int iNestingLevel )
  72. {
  73. if ( iNestingLevel == 1 && !Q_strncmp(pszKeyName, "entity", 6) )
  74. return "DmeCommentaryNodeEntity";
  75. return NULL;
  76. }
  77. };
  78. //-----------------------------------------------------------------------------
  79. // Saves/loads from file
  80. //-----------------------------------------------------------------------------
  81. bool CCommEditDoc::LoadFromFile( const char *pFileName )
  82. {
  83. Assert( !m_hRoot.Get() );
  84. CAppDisableUndoScopeGuard guard( "CCommEditDoc::LoadFromFile", 0 );
  85. SetDirty( false );
  86. if ( !pFileName[0] )
  87. return false;
  88. char mapname[ 256 ];
  89. // Compute the map name
  90. const char *pMaps = Q_stristr( pFileName, "\\maps\\" );
  91. if ( !pMaps )
  92. return false;
  93. // Build map name
  94. //int nNameLen = (int)( (size_t)pComm - (size_t)pMaps ) - 5;
  95. Q_StripExtension( pFileName, mapname, sizeof(mapname) );
  96. char *pszFileName = (char*)Q_UnqualifiedFileName(mapname);
  97. // Set the txt file name.
  98. // If we loaded an existing commentary file, keep the same filename.
  99. // If we loaded a .bsp, change the name & the extension.
  100. if ( !V_stricmp( Q_GetFileExtension( pFileName ), "bsp" ) )
  101. {
  102. const char *pCommentaryAppend = "_commentary.txt";
  103. Q_StripExtension( pFileName, m_pTXTFileName, sizeof(m_pTXTFileName)- strlen(pCommentaryAppend) - 1 );
  104. Q_strcat( m_pTXTFileName, pCommentaryAppend, sizeof( m_pTXTFileName ) );
  105. if ( g_pFileSystem->FileExists( m_pTXTFileName ) )
  106. {
  107. char pBuf[1024];
  108. Q_snprintf( pBuf, sizeof(pBuf), "File %s already exists!\n", m_pTXTFileName );
  109. m_pTXTFileName[0] = 0;
  110. vgui::MessageBox *pMessageBox = new vgui::MessageBox( "Unable to overwrite file!\n", pBuf, g_pCommEditTool );
  111. pMessageBox->DoModal( );
  112. return false;
  113. }
  114. DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( m_pTXTFileName );
  115. m_hRoot = CreateElement<CDmElement>( "root", fileid );
  116. CDmrElementArray<> subkeys( m_hRoot->AddAttribute( "subkeys", AT_ELEMENT_ARRAY ) );
  117. CDmElement *pRoot2 = CreateElement<CDmElement>( "Entities", fileid );
  118. pRoot2->AddAttribute( "subkeys", AT_ELEMENT_ARRAY );
  119. subkeys.AddToTail( pRoot2 );
  120. g_pDataModel->SetFileRoot( fileid, m_hRoot );
  121. }
  122. else
  123. {
  124. char *pComm = Q_stristr( pszFileName, "_commentary" );
  125. if ( !pComm )
  126. {
  127. char pBuf[1024];
  128. Q_snprintf( pBuf, sizeof(pBuf), "File %s is not a commentary file!\nThe file name must end in _commentary.txt.\n", m_pTXTFileName );
  129. m_pTXTFileName[0] = 0;
  130. vgui::MessageBox *pMessageBox = new vgui::MessageBox( "Bad file name!\n", pBuf, g_pCommEditTool );
  131. pMessageBox->DoModal( );
  132. return false;
  133. }
  134. // Clip off the "_commentary" at the end of the filename
  135. *pComm = '\0';
  136. // This is not undoable
  137. CDisableUndoScopeGuard guardFile;
  138. CDmElement *pTXT = NULL;
  139. CElementForKeyValueCallback KeyValuesCallback;
  140. g_pDataModel->SetKeyValuesElementCallback( &KeyValuesCallback );
  141. DmFileId_t fileid = g_pDataModel->RestoreFromFile( pFileName, NULL, "keyvalues", &pTXT );
  142. g_pDataModel->SetKeyValuesElementCallback( NULL );
  143. if ( fileid == DMFILEID_INVALID )
  144. {
  145. m_pTXTFileName[0] = 0;
  146. return false;
  147. }
  148. SetTXTFileName( pFileName );
  149. m_hRoot = pTXT;
  150. }
  151. guard.Release();
  152. SetDirty( false );
  153. char cmd[ 256 ];
  154. Q_snprintf( cmd, sizeof( cmd ), "disconnect; map %s\n", pszFileName );
  155. enginetools->Command( cmd );
  156. enginetools->Execute( );
  157. return true;
  158. }
  159. void CCommEditDoc::SaveToFile( )
  160. {
  161. if ( m_hRoot.Get() && m_pTXTFileName && m_pTXTFileName[0] )
  162. {
  163. g_pDataModel->SaveToFile( m_pTXTFileName, NULL, "keyvalues", "keyvalues", m_hRoot );
  164. }
  165. SetDirty( false );
  166. }
  167. //-----------------------------------------------------------------------------
  168. // Returns the root object
  169. //-----------------------------------------------------------------------------
  170. CDmElement *CCommEditDoc::GetRootObject()
  171. {
  172. return m_hRoot;
  173. }
  174. //-----------------------------------------------------------------------------
  175. // Returns the entity list
  176. //-----------------------------------------------------------------------------
  177. CDmAttribute *CCommEditDoc::GetEntityList()
  178. {
  179. CDmrElementArray<> mainKeys( m_hRoot, "subkeys" );
  180. if ( !mainKeys.IsValid() || mainKeys.Count() == 0 )
  181. return NULL;
  182. CDmeHandle<CDmElement> hEntityList;
  183. hEntityList = mainKeys[ 0 ];
  184. return hEntityList ? hEntityList->GetAttribute( "subkeys", AT_ELEMENT_ARRAY ) : NULL;
  185. }
  186. //-----------------------------------------------------------------------------
  187. // Purpose:
  188. //-----------------------------------------------------------------------------
  189. void CCommEditDoc::AddNewInfoTarget( const Vector &vecOrigin, const QAngle &angAngles )
  190. {
  191. CDmrCommentaryNodeEntityList entities( GetEntityList() );
  192. if ( !entities.IsValid() )
  193. return;
  194. CDmeCommentaryNodeEntity *pTarget;
  195. {
  196. CAppUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Add Info Target", "Add Info Target" );
  197. pTarget = CreateElement<CDmeCommentaryNodeEntity>( "target", entities.GetOwner()->GetFileId() );
  198. pTarget->SetName( "entity" );
  199. pTarget->SetValue( "classname", "info_target" );
  200. pTarget->SetRenderOrigin( vecOrigin );
  201. pTarget->SetRenderAngles( angAngles );
  202. entities.AddToTail( pTarget );
  203. pTarget->MarkDirty();
  204. pTarget->DrawInEngine( true );
  205. }
  206. g_pCommEditTool->GetCommentaryNodeBrowser()->SelectNode( pTarget );
  207. }
  208. //-----------------------------------------------------------------------------
  209. // Purpose:
  210. //-----------------------------------------------------------------------------
  211. void CCommEditDoc::AddNewInfoTarget( void )
  212. {
  213. Vector vecOrigin;
  214. QAngle angAngles;
  215. float flFov;
  216. clienttools->GetLocalPlayerEyePosition( vecOrigin, angAngles, flFov );
  217. AddNewInfoTarget( vecOrigin, vec3_angle );
  218. }
  219. //-----------------------------------------------------------------------------
  220. // Purpose:
  221. //-----------------------------------------------------------------------------
  222. void CCommEditDoc::AddNewCommentaryNode( const Vector &vecOrigin, const QAngle &angAngles )
  223. {
  224. CDmrCommentaryNodeEntityList entities = GetEntityList();
  225. CDmeCommentaryNodeEntity *pNode;
  226. {
  227. CAppUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Add Commentary Node", "Add Commentary Node" );
  228. pNode = CreateElement<CDmeCommentaryNodeEntity>( "node", entities.GetOwner()->GetFileId() );
  229. pNode->SetName( "entity" );
  230. pNode->SetValue( "classname", "point_commentary_node" );
  231. pNode->SetRenderOrigin( vecOrigin );
  232. pNode->SetRenderAngles( angAngles );
  233. pNode->SetValue<CUtlString>( "precommands", "" );
  234. pNode->SetValue<CUtlString>( "postcommands", "" );
  235. pNode->SetValue<CUtlString>( "commentaryfile", "" );
  236. pNode->SetValue<CUtlString>( "viewtarget", "" );
  237. pNode->SetValue<CUtlString>( "viewposition", "" );
  238. pNode->SetValue<int>( "prevent_movement", 0 );
  239. pNode->SetValue<CUtlString>( "speakers", "" );
  240. pNode->SetValue<CUtlString>( "synopsis", "" );
  241. entities.AddToTail( pNode );
  242. pNode->MarkDirty();
  243. pNode->DrawInEngine( true );
  244. }
  245. g_pCommEditTool->GetCommentaryNodeBrowser()->SelectNode( pNode );
  246. }
  247. //-----------------------------------------------------------------------------
  248. // Purpose:
  249. //-----------------------------------------------------------------------------
  250. void CCommEditDoc::AddNewCommentaryNode( void )
  251. {
  252. Vector vecOrigin;
  253. QAngle angAngles;
  254. float flFov;
  255. clienttools->GetLocalPlayerEyePosition( vecOrigin, angAngles, flFov );
  256. AddNewCommentaryNode( vecOrigin, vec3_angle );
  257. }
  258. //-----------------------------------------------------------------------------
  259. // Deletes a commentary node
  260. //-----------------------------------------------------------------------------
  261. void CCommEditDoc::DeleteCommentaryNode( CDmElement *pRemoveNode )
  262. {
  263. CDmrCommentaryNodeEntityList entities = GetEntityList();
  264. int nCount = entities.Count();
  265. for ( int i = 0; i < nCount; ++i )
  266. {
  267. if ( pRemoveNode == entities[i] )
  268. {
  269. CAppUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Delete Commentary Node", "Delete Commentary Node" );
  270. CDmeCommentaryNodeEntity *pNode = entities[ i ];
  271. pNode->DrawInEngine( false );
  272. entities.FastRemove( i );
  273. return;
  274. }
  275. }
  276. }
  277. //-----------------------------------------------------------------------------
  278. // Purpose:
  279. // Input : &vecOrigin -
  280. // &angAbsAngles -
  281. // Output : CDmeCommentaryNodeEntity
  282. //-----------------------------------------------------------------------------
  283. CDmeCommentaryNodeEntity *CCommEditDoc::GetCommentaryNodeForLocation( Vector &vecOrigin, QAngle &angAbsAngles )
  284. {
  285. CDmrCommentaryNodeEntityList entities = GetEntityList();
  286. int nCount = entities.Count();
  287. for ( int i = 0; i < nCount; ++i )
  288. {
  289. CDmeCommentaryNodeEntity *pNode = entities[ i ];
  290. if ( !pNode )
  291. continue;
  292. Vector &vecAngles = *(Vector*)(&pNode->GetRenderAngles());
  293. if ( pNode->GetRenderOrigin().DistTo( vecOrigin ) < 1e-3 && vecAngles.DistTo( *(Vector*)&angAbsAngles ) < 1e-1 )
  294. return pNode;
  295. }
  296. return NULL;
  297. }
  298. //-----------------------------------------------------------------------------
  299. // Populate string choice lists
  300. //-----------------------------------------------------------------------------
  301. bool CCommEditDoc::GetStringChoiceList( const char *pChoiceListType, CDmElement *pElement,
  302. const char *pAttributeName, bool bArrayElement, StringChoiceList_t &list )
  303. {
  304. if ( !Q_stricmp( pChoiceListType, "info_targets" ) )
  305. {
  306. CDmrCommentaryNodeEntityList entities = GetEntityList();
  307. StringChoice_t sChoice;
  308. sChoice.m_pValue = "";
  309. sChoice.m_pChoiceString = "";
  310. list.AddToTail( sChoice );
  311. int nCount = entities.Count();
  312. for ( int i = 0; i < nCount; ++i )
  313. {
  314. CDmeCommentaryNodeEntity *pNode = entities[ i ];
  315. if ( !pNode )
  316. continue;
  317. if ( !V_stricmp( pNode->GetClassName(), "info_target" ) )
  318. {
  319. sChoice.m_pValue = pNode->GetTargetName();
  320. sChoice.m_pChoiceString = pNode->GetTargetName();
  321. list.AddToTail( sChoice );
  322. }
  323. }
  324. return true;
  325. }
  326. return false;
  327. }
  328. //-----------------------------------------------------------------------------
  329. // Populate element choice lists
  330. //-----------------------------------------------------------------------------
  331. bool CCommEditDoc::GetElementChoiceList( const char *pChoiceListType, CDmElement *pElement,
  332. const char *pAttributeName, bool bArrayElement, ElementChoiceList_t &list )
  333. {
  334. if ( !Q_stricmp( pChoiceListType, "allelements" ) )
  335. {
  336. AddElementsRecursively( m_hRoot, list );
  337. return true;
  338. }
  339. if ( !Q_stricmp( pChoiceListType, "info_targets" ) )
  340. {
  341. CDmrCommentaryNodeEntityList entities = GetEntityList();
  342. bool bFound = false;
  343. int nCount = entities.Count();
  344. for ( int i = 0; i < nCount; ++i )
  345. {
  346. CDmeCommentaryNodeEntity *pNode = entities[ i ];
  347. if ( pNode && !V_stricmp( pNode->GetClassName(), "info_target" ) )
  348. {
  349. bFound = true;
  350. ElementChoice_t sChoice;
  351. sChoice.m_pValue = pNode;
  352. sChoice.m_pChoiceString = pNode->GetTargetName();
  353. list.AddToTail( sChoice );
  354. }
  355. }
  356. return bFound;
  357. }
  358. // by default, try to treat the choice list type as a Dme element type
  359. AddElementsRecursively( m_hRoot, list, pChoiceListType );
  360. return list.Count() > 0;
  361. }
  362. //-----------------------------------------------------------------------------
  363. // Called when data changes
  364. //-----------------------------------------------------------------------------
  365. void CCommEditDoc::OnDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags )
  366. {
  367. SetDirty( nNotifyFlags & NOTIFY_SETDIRTYFLAG ? true : false );
  368. m_pCallback->OnDocChanged( pReason, nNotifySource, nNotifyFlags );
  369. }