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.

568 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. #include "vbsp.h"
  3. #include "map_shared.h"
  4. #include "fgdlib/fgdlib.h"
  5. #include "manifest.h"
  6. #include "windows.h"
  7. //-----------------------------------------------------------------------------
  8. // Purpose: default constructor
  9. //-----------------------------------------------------------------------------
  10. CManifestMap::CManifestMap( void )
  11. {
  12. m_RelativeMapFileName[ 0 ] = 0;
  13. m_bTopLevelMap = false;
  14. }
  15. //-----------------------------------------------------------------------------
  16. // Purpose: default constructor
  17. //-----------------------------------------------------------------------------
  18. CManifest::CManifest( void )
  19. {
  20. m_InstancePath[ 0 ] = 0;
  21. m_bIsCordoning = false;
  22. m_CordoningMapEnt = NULL;
  23. }
  24. //-----------------------------------------------------------------------------
  25. // Purpose: this function will parse through the known keys for the manifest map entry
  26. // Input : szKey - the key name
  27. // szValue - the value
  28. // pManifestMap - the manifest map this belongs to
  29. // Output : ChunkFileResult_t - result of the parsing
  30. //-----------------------------------------------------------------------------
  31. ChunkFileResult_t CManifest::LoadManifestMapKeyCallback( const char *szKey, const char *szValue, CManifestMap *pManifestMap )
  32. {
  33. if ( !stricmp( szKey, "Name" ) )
  34. {
  35. // pManifestMap->m_FriendlyName = szValue;
  36. }
  37. else if ( !stricmp( szKey, "File" ) )
  38. {
  39. strcpy( pManifestMap->m_RelativeMapFileName, szValue );
  40. }
  41. else if ( !stricmp( szKey, "IsPrimary" ) )
  42. {
  43. // pManifestMap->m_bPrimaryMap = ( atoi( szValue ) == 1 );
  44. }
  45. else if ( !stricmp( szKey, "IsProtected" ) )
  46. {
  47. // pManifestMap->m_bCanBeModified = ( atoi( szValue ) != 1 );
  48. }
  49. else if ( !stricmp( szKey, "TopLevel" ) )
  50. {
  51. pManifestMap->m_bTopLevelMap = ( atoi( szValue ) == 1 );
  52. }
  53. return ChunkFile_Ok;
  54. }
  55. //-----------------------------------------------------------------------------
  56. // Purpose: this function is responsible for setting up the manifest map about to be read in
  57. // Input : pFile - the chunk file being read
  58. // pDoc - the owning manifest document
  59. // Output : ChunkFileResult_t - result of the parsing
  60. //-----------------------------------------------------------------------------
  61. ChunkFileResult_t CManifest::LoadManifestVMFCallback( CChunkFile *pFile, CManifest *pManifest )
  62. {
  63. CManifestMap *pManifestMap = new CManifestMap();
  64. pManifest->m_Maps.AddToTail( pManifestMap );
  65. ChunkFileResult_t eResult = pFile->ReadChunk( ( KeyHandler_t )LoadManifestMapKeyCallback, pManifestMap );
  66. return( eResult );
  67. }
  68. //-----------------------------------------------------------------------------
  69. // Purpose: this function will load the VMF chunk
  70. // Input : pFile - the chunk file being read
  71. // pDoc - the owning manifest document
  72. // Output : ChunkFileResult_t - result of the parsing
  73. //-----------------------------------------------------------------------------
  74. ChunkFileResult_t CManifest::LoadManifestMapsCallback( CChunkFile *pFile, CManifest *pManifest )
  75. {
  76. CChunkHandlerMap Handlers;
  77. Handlers.AddHandler( "VMF", ( ChunkHandler_t )LoadManifestVMFCallback, pManifest );
  78. pFile->PushHandlers(&Handlers);
  79. ChunkFileResult_t eResult = ChunkFile_Ok;
  80. eResult = pFile->ReadChunk();
  81. pFile->PopHandlers();
  82. return( eResult );
  83. }
  84. //-----------------------------------------------------------------------------
  85. // Purpose:
  86. // Input :
  87. // Output :
  88. //-----------------------------------------------------------------------------
  89. ChunkFileResult_t CManifest::LoadCordonBoxCallback( CChunkFile *pFile, Cordon_t *pCordon )
  90. {
  91. // Add a box to this cordon.
  92. pCordon->m_Boxes.AddToTail();
  93. BoundBox &box = pCordon->m_Boxes.Tail();
  94. // Fill it in with the data from the VMF.
  95. return pFile->ReadChunk( (KeyHandler_t)LoadCordonBoxKeyCallback, (void *)&box );
  96. }
  97. //-----------------------------------------------------------------------------
  98. // Purpose:
  99. // Input :
  100. // Output :
  101. //-----------------------------------------------------------------------------
  102. ChunkFileResult_t CManifest::LoadCordonBoxKeyCallback( const char *szKey, const char *szValue, BoundBox *pBox )
  103. {
  104. if (!stricmp(szKey, "mins"))
  105. {
  106. CChunkFile::ReadKeyValuePoint(szValue, pBox->bmins);
  107. }
  108. else if (!stricmp(szKey, "maxs"))
  109. {
  110. CChunkFile::ReadKeyValuePoint(szValue, pBox->bmaxs);
  111. }
  112. return ChunkFile_Ok;
  113. }
  114. //-----------------------------------------------------------------------------
  115. // Purpose:
  116. // Input :
  117. // Output :
  118. //-----------------------------------------------------------------------------
  119. ChunkFileResult_t CManifest::LoadCordonKeyCallback( const char *szKey, const char *szValue, Cordon_t *pCordon )
  120. {
  121. if (!stricmp(szKey, "name"))
  122. {
  123. pCordon->m_szName.Set( szValue );
  124. }
  125. // Whether this particular cordon volume is active.
  126. else if (!stricmp(szKey, "active"))
  127. {
  128. CChunkFile::ReadKeyValueBool(szValue, pCordon->m_bActive);
  129. }
  130. return ChunkFile_Ok;
  131. }
  132. //-----------------------------------------------------------------------------
  133. // Purpose:
  134. // Input :
  135. // Output :
  136. //-----------------------------------------------------------------------------
  137. ChunkFileResult_t CManifest::LoadCordonCallback( CChunkFile *pFile, CManifest *pManifest )
  138. {
  139. // Add a new cordon which will be filled in by the key callback
  140. pManifest->m_Cordons.AddToTail();
  141. Cordon_t &cordon = pManifest->m_Cordons.Tail();
  142. CChunkHandlerMap Handlers;
  143. Handlers.AddHandler( "box", (ChunkHandler_t)CManifest::LoadCordonBoxCallback, (void *)&cordon );
  144. pFile->PushHandlers(&Handlers);
  145. ChunkFileResult_t eResult = pFile->ReadChunk( (KeyHandler_t)LoadCordonKeyCallback, (void *)&cordon );
  146. pFile->PopHandlers();
  147. return(eResult);
  148. }
  149. //-----------------------------------------------------------------------------------------------------------
  150. // Parses keys that are applicable to all cordons in the map.
  151. //-----------------------------------------------------------------------------
  152. ChunkFileResult_t CManifest::LoadCordonsKeyCallback( const char *szKey, const char *szValue, CManifest *pManifest )
  153. {
  154. // Whether the cordoning system is enabled or disabled.
  155. if ( !stricmp( szKey, "active" ) )
  156. {
  157. CChunkFile::ReadKeyValueBool( szValue, pManifest->m_bIsCordoning );
  158. }
  159. return ChunkFile_Ok;
  160. }
  161. //-----------------------------------------------------------------------------
  162. // Parses the VMF chunk that pertains to all the cordons in the map:
  163. //
  164. // cordons
  165. // {
  166. // "active" "true"
  167. // cordon
  168. // {
  169. // "active" "true"
  170. // "box"
  171. // {
  172. // "mins" "-1024, -1024, -1024"
  173. // "maxs" "1024, 1024, 1024"
  174. // }
  175. // ...may be more boxes...
  176. // }
  177. // ...may be more cordons...
  178. // }
  179. //
  180. //-----------------------------------------------------------------------------
  181. ChunkFileResult_t CManifest::LoadCordonsCallback( CChunkFile *pFile, CManifest *pManifest )
  182. {
  183. CChunkHandlerMap Handlers;
  184. Handlers.AddHandler( "cordon", (ChunkHandler_t)CManifest::LoadCordonCallback, pManifest );
  185. pFile->PushHandlers(&Handlers);
  186. ChunkFileResult_t eResult = pFile->ReadChunk( (KeyHandler_t)LoadCordonsKeyCallback, pManifest );
  187. pFile->PopHandlers();
  188. return(eResult);
  189. }
  190. extern ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity);
  191. ChunkFileResult_t CManifest::LoadManifestCordoningPrefsCallback( CChunkFile *pFile, CManifest *pDoc )
  192. {
  193. pDoc->m_CordoningMapEnt = &g_MainMap->entities[g_MainMap->num_entities];
  194. g_MainMap->num_entities++;
  195. memset( pDoc->m_CordoningMapEnt, 0, sizeof( *pDoc->m_CordoningMapEnt ) );
  196. pDoc->m_CordoningMapEnt->firstbrush = g_MainMap->nummapbrushes;
  197. pDoc->m_CordoningMapEnt->numbrushes = 0;
  198. LoadEntity_t LoadEntity;
  199. LoadEntity.pEntity = pDoc->m_CordoningMapEnt;
  200. // No default flags/contents
  201. LoadEntity.nBaseFlags = 0;
  202. LoadEntity.nBaseContents = 0;
  203. //
  204. // Set up handlers for the subchunks that we are interested in.
  205. //
  206. CChunkHandlerMap Handlers;
  207. Handlers.AddHandler( "cordons", ( ChunkHandler_t )CManifest::LoadCordonsCallback, pDoc );
  208. Handlers.AddHandler("solid", (ChunkHandler_t)::LoadSolidCallback, &LoadEntity);
  209. pFile->PushHandlers(&Handlers);
  210. ChunkFileResult_t eResult = ChunkFile_Ok;
  211. eResult = pFile->ReadChunk();
  212. pFile->PopHandlers();
  213. return( eResult );
  214. }
  215. //-----------------------------------------------------------------------------
  216. // Purpose: this function will create a new entity pair
  217. // Input : pKey - the key of the pair
  218. // pValue - the value of the pair
  219. // Output : returns a newly created epair structure
  220. //-----------------------------------------------------------------------------
  221. epair_t *CManifest::CreateEPair( char *pKey, char *pValue )
  222. {
  223. epair_t *pEPair = new epair_t;
  224. pEPair->key = new char[ strlen( pKey ) + 1 ];
  225. pEPair->value = new char[ strlen( pValue ) + 1 ];
  226. strcpy( pEPair->key, pKey );
  227. strcpy( pEPair->value, pValue );
  228. return pEPair;
  229. }
  230. //-----------------------------------------------------------------------------
  231. // Purpose: this function will load in all of the submaps belonging to this manifest,
  232. // except for the top level map, which is loaded separately.
  233. // Input : pMapFile - the top level map that was previously loaded
  234. // pszFileName - the absolute file name of the top level map file
  235. // Output : returns true if all submaps were loaded
  236. //-----------------------------------------------------------------------------
  237. bool CManifest::LoadSubMaps( CMapFile *pMapFile, const char *pszFileName )
  238. {
  239. entity_t *InstanceEntity;
  240. epair_t *pEPair;
  241. InstanceEntity = &pMapFile->entities[ pMapFile->num_entities ];
  242. pMapFile->num_entities++;
  243. memset( InstanceEntity, 0, sizeof( *InstanceEntity ) );
  244. InstanceEntity->origin.Init( 0.0f, 0.0f, 0.0f );
  245. pEPair = CreateEPair( "classname", "worldspawn" );
  246. pEPair->next = InstanceEntity->epairs;
  247. InstanceEntity->epairs = pEPair;
  248. for( int i = 0; i < m_Maps.Count(); i++ )
  249. {
  250. // if ( m_Maps[ i ]->m_bTopLevelMap == false )
  251. {
  252. char FileName[ MAX_PATH ];
  253. sprintf( FileName, "%s%s", m_InstancePath, m_Maps[ i ]->m_RelativeMapFileName );
  254. InstanceEntity = &pMapFile->entities[ pMapFile->num_entities ];
  255. pMapFile->num_entities++;
  256. memset( InstanceEntity, 0, sizeof( *InstanceEntity ) );
  257. InstanceEntity->origin.Init( 0.0f, 0.0f, 0.0f );
  258. pEPair = CreateEPair( "angles", "0 0 0" );
  259. pEPair->next = InstanceEntity->epairs;
  260. InstanceEntity->epairs = pEPair;
  261. char temp[ 128 ];
  262. sprintf( temp, "%d", GameData::NAME_FIXUP_NONE );
  263. pEPair = CreateEPair( "fixup_style", temp );
  264. pEPair->next = InstanceEntity->epairs;
  265. InstanceEntity->epairs = pEPair;
  266. pEPair = CreateEPair( "classname", "func_instance" );
  267. pEPair->next = InstanceEntity->epairs;
  268. InstanceEntity->epairs = pEPair;
  269. pEPair = CreateEPair( "file", m_Maps[ i ]->m_RelativeMapFileName );
  270. pEPair->next = InstanceEntity->epairs;
  271. InstanceEntity->epairs = pEPair;
  272. if ( m_Maps[ i ]->m_bTopLevelMap == true )
  273. {
  274. pEPair = CreateEPair( "toplevel", "1" );
  275. pEPair->next = InstanceEntity->epairs;
  276. InstanceEntity->epairs = pEPair;
  277. }
  278. }
  279. }
  280. return true;
  281. }
  282. //-----------------------------------------------------------------------------
  283. // Purpose:
  284. // Input :
  285. // Output :
  286. //-----------------------------------------------------------------------------
  287. bool CManifest::LoadVMFManifestUserPrefs( const char *pszFileName )
  288. {
  289. char UserName[ MAX_PATH ], FileName[ MAX_PATH ], UserPrefsFileName[ MAX_PATH ];
  290. DWORD UserNameSize;
  291. UserNameSize = sizeof( UserName );
  292. if ( GetUserName( UserName, &UserNameSize ) == 0 )
  293. {
  294. strcpy( UserPrefsFileName, "default" );
  295. }
  296. sprintf( UserPrefsFileName, "\\%s.vmm_prefs", UserName );
  297. V_StripExtension( pszFileName, FileName, sizeof( FileName ) );
  298. strcat( FileName, UserPrefsFileName );
  299. FILE *fp = fopen( FileName, "rb" );
  300. if ( !fp )
  301. {
  302. return false;
  303. }
  304. CChunkFile File;
  305. ChunkFileResult_t eResult = File.Open( FileName, ChunkFile_Read );
  306. if ( eResult == ChunkFile_Ok )
  307. {
  308. //
  309. // Set up handlers for the subchunks that we are interested in.
  310. //
  311. CChunkHandlerMap Handlers;
  312. Handlers.AddHandler( "cordoning", ( ChunkHandler_t )CManifest::LoadManifestCordoningPrefsCallback, this );
  313. // Handlers.SetErrorHandler( ( ChunkErrorHandler_t )CMapDoc::HandleLoadError, this);
  314. File.PushHandlers(&Handlers);
  315. while( eResult == ChunkFile_Ok )
  316. {
  317. eResult = File.ReadChunk();
  318. }
  319. if ( eResult == ChunkFile_EOF )
  320. {
  321. eResult = ChunkFile_Ok;
  322. }
  323. File.PopHandlers();
  324. }
  325. if ( eResult == ChunkFile_Ok )
  326. {
  327. }
  328. else
  329. {
  330. // no pref message for now
  331. // GetMainWnd()->MessageBox( File.GetErrorText( eResult ), "Error loading manifest!", MB_OK | MB_ICONEXCLAMATION );
  332. }
  333. return true;
  334. }
  335. //-----------------------------------------------------------------------------
  336. // Purpose: Loads a .VMM file.
  337. // Input : pszFileName - Full path of the map file to load.
  338. //-----------------------------------------------------------------------------
  339. bool CManifest::LoadVMFManifest( const char *pszFileName )
  340. {
  341. V_StripExtension( pszFileName, m_InstancePath, sizeof( m_InstancePath ) );
  342. strcat( m_InstancePath, "\\" );
  343. CChunkFile File;
  344. ChunkFileResult_t eResult = File.Open( pszFileName, ChunkFile_Read );
  345. if ( eResult != ChunkFile_Ok )
  346. {
  347. g_MapError.ReportError( File.GetErrorText( eResult ) );
  348. return false;
  349. }
  350. CChunkHandlerMap Handlers;
  351. Handlers.AddHandler( "Maps", ( ChunkHandler_t )LoadManifestMapsCallback, this );
  352. File.PushHandlers(&Handlers);
  353. while (eResult == ChunkFile_Ok)
  354. {
  355. eResult = File.ReadChunk();
  356. }
  357. if (eResult == ChunkFile_EOF)
  358. {
  359. eResult = ChunkFile_Ok;
  360. }
  361. File.PopHandlers();
  362. if ( eResult == ChunkFile_Ok )
  363. {
  364. int index = g_Maps.AddToTail( new CMapFile() );
  365. g_LoadingMap = g_Maps[ index ];
  366. if ( g_MainMap == NULL )
  367. {
  368. g_MainMap = g_LoadingMap;
  369. }
  370. LoadSubMaps( g_LoadingMap, pszFileName );
  371. LoadVMFManifestUserPrefs( pszFileName );
  372. }
  373. return ( eResult == ChunkFile_Ok );
  374. }
  375. //-----------------------------------------------------------------------------
  376. // Purpose:
  377. // Input :
  378. // Output :
  379. //-----------------------------------------------------------------------------
  380. void CManifest::CordonWorld( )
  381. {
  382. if ( m_bIsCordoning == false )
  383. {
  384. return;
  385. }
  386. for ( int i = 0; i < g_MainMap->num_entities; i++ )
  387. {
  388. if ( i == 0 )
  389. { // for world spawn, we look at brushes
  390. for( int nBrushNum = 0; nBrushNum < g_MainMap->entities[ i ].numbrushes; nBrushNum++ )
  391. {
  392. int nIndex = g_MainMap->entities[ i ].firstbrush + nBrushNum;
  393. bool bRemove = true;
  394. for( int nCordon = 0; nCordon < m_Cordons.Count(); nCordon++ )
  395. {
  396. if ( m_Cordons[ nCordon ].m_bActive == false )
  397. {
  398. continue;
  399. }
  400. for( int nBox = 0; nBox < m_Cordons[ nCordon ].m_Boxes.Count(); nBox++ )
  401. {
  402. if ( m_Cordons[ nCordon ].m_Boxes[ nBox ].IsIntersectingBox( g_MainMap->mapbrushes[ nIndex ].mins, g_MainMap->mapbrushes[ nIndex ].maxs ) == true )
  403. {
  404. bRemove = false;
  405. break;
  406. }
  407. }
  408. if ( bRemove == false )
  409. {
  410. break;
  411. }
  412. }
  413. if ( bRemove )
  414. {
  415. int nSize = ( g_MainMap->entities[ i ].numbrushes - nBrushNum - 1 ) * sizeof( g_MainMap->mapbrushes[ 0 ] );
  416. memmove( &g_MainMap->mapbrushes[ nIndex ], &g_MainMap->mapbrushes[ nIndex + 1 ], nSize );
  417. g_MainMap->entities[ i ].numbrushes--;
  418. nBrushNum--;
  419. }
  420. }
  421. }
  422. else if ( &g_MainMap->entities[ i ] != m_CordoningMapEnt )
  423. { // for all other entities, even if they include brushes, we look at origin
  424. if ( g_MainMap->entities[ i ].numbrushes == 0 && g_MainMap->entities[ i ].epairs == NULL )
  425. {
  426. continue;
  427. }
  428. bool bRemove = true;
  429. for( int nCordon = 0; nCordon < m_Cordons.Count(); nCordon++ )
  430. {
  431. if ( m_Cordons[ nCordon ].m_bActive == false )
  432. {
  433. continue;
  434. }
  435. for( int nBox = 0; nBox < m_Cordons[ nCordon ].m_Boxes.Count(); nBox++ )
  436. {
  437. if ( m_Cordons[ nCordon ].m_Boxes[ nBox ].ContainsPoint( g_MainMap->entities[ i ].origin ) == true )
  438. {
  439. bRemove = false;
  440. break;
  441. }
  442. }
  443. if ( bRemove == false )
  444. {
  445. break;
  446. }
  447. }
  448. if ( bRemove )
  449. {
  450. g_MainMap->entities[ i ].numbrushes = 0;
  451. g_MainMap->entities[ i ].epairs = NULL;
  452. }
  453. }
  454. }
  455. if ( m_CordoningMapEnt )
  456. {
  457. g_MainMap->MoveBrushesToWorldGeneral( m_CordoningMapEnt );
  458. m_CordoningMapEnt->numbrushes = 0;
  459. m_CordoningMapEnt->epairs = NULL;
  460. }
  461. }