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
568 lines
16 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
#include "vbsp.h" |
|
#include "map_shared.h" |
|
#include "fgdlib/fgdlib.h" |
|
#include "manifest.h" |
|
#include "windows.h" |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: default constructor |
|
//----------------------------------------------------------------------------- |
|
CManifestMap::CManifestMap( void ) |
|
{ |
|
m_RelativeMapFileName[ 0 ] = 0; |
|
m_bTopLevelMap = false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: default constructor |
|
//----------------------------------------------------------------------------- |
|
CManifest::CManifest( void ) |
|
{ |
|
m_InstancePath[ 0 ] = 0; |
|
m_bIsCordoning = false; |
|
m_CordoningMapEnt = NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: this function will parse through the known keys for the manifest map entry |
|
// Input : szKey - the key name |
|
// szValue - the value |
|
// pManifestMap - the manifest map this belongs to |
|
// Output : ChunkFileResult_t - result of the parsing |
|
//----------------------------------------------------------------------------- |
|
ChunkFileResult_t CManifest::LoadManifestMapKeyCallback( const char *szKey, const char *szValue, CManifestMap *pManifestMap ) |
|
{ |
|
if ( !stricmp( szKey, "Name" ) ) |
|
{ |
|
// pManifestMap->m_FriendlyName = szValue; |
|
} |
|
else if ( !stricmp( szKey, "File" ) ) |
|
{ |
|
strcpy( pManifestMap->m_RelativeMapFileName, szValue ); |
|
} |
|
else if ( !stricmp( szKey, "IsPrimary" ) ) |
|
{ |
|
// pManifestMap->m_bPrimaryMap = ( atoi( szValue ) == 1 ); |
|
} |
|
else if ( !stricmp( szKey, "IsProtected" ) ) |
|
{ |
|
// pManifestMap->m_bCanBeModified = ( atoi( szValue ) != 1 ); |
|
} |
|
else if ( !stricmp( szKey, "TopLevel" ) ) |
|
{ |
|
pManifestMap->m_bTopLevelMap = ( atoi( szValue ) == 1 ); |
|
} |
|
|
|
return ChunkFile_Ok; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: this function is responsible for setting up the manifest map about to be read in |
|
// Input : pFile - the chunk file being read |
|
// pDoc - the owning manifest document |
|
// Output : ChunkFileResult_t - result of the parsing |
|
//----------------------------------------------------------------------------- |
|
ChunkFileResult_t CManifest::LoadManifestVMFCallback( CChunkFile *pFile, CManifest *pManifest ) |
|
{ |
|
CManifestMap *pManifestMap = new CManifestMap(); |
|
|
|
pManifest->m_Maps.AddToTail( pManifestMap ); |
|
|
|
ChunkFileResult_t eResult = pFile->ReadChunk( ( KeyHandler_t )LoadManifestMapKeyCallback, pManifestMap ); |
|
|
|
return( eResult ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: this function will load the VMF chunk |
|
// Input : pFile - the chunk file being read |
|
// pDoc - the owning manifest document |
|
// Output : ChunkFileResult_t - result of the parsing |
|
//----------------------------------------------------------------------------- |
|
ChunkFileResult_t CManifest::LoadManifestMapsCallback( CChunkFile *pFile, CManifest *pManifest ) |
|
{ |
|
CChunkHandlerMap Handlers; |
|
Handlers.AddHandler( "VMF", ( ChunkHandler_t )LoadManifestVMFCallback, pManifest ); |
|
pFile->PushHandlers(&Handlers); |
|
|
|
ChunkFileResult_t eResult = ChunkFile_Ok; |
|
|
|
eResult = pFile->ReadChunk(); |
|
|
|
pFile->PopHandlers(); |
|
|
|
return( eResult ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
ChunkFileResult_t CManifest::LoadCordonBoxCallback( CChunkFile *pFile, Cordon_t *pCordon ) |
|
{ |
|
// Add a box to this cordon. |
|
pCordon->m_Boxes.AddToTail(); |
|
BoundBox &box = pCordon->m_Boxes.Tail(); |
|
|
|
// Fill it in with the data from the VMF. |
|
return pFile->ReadChunk( (KeyHandler_t)LoadCordonBoxKeyCallback, (void *)&box ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
ChunkFileResult_t CManifest::LoadCordonBoxKeyCallback( const char *szKey, const char *szValue, BoundBox *pBox ) |
|
{ |
|
if (!stricmp(szKey, "mins")) |
|
{ |
|
CChunkFile::ReadKeyValuePoint(szValue, pBox->bmins); |
|
} |
|
else if (!stricmp(szKey, "maxs")) |
|
{ |
|
CChunkFile::ReadKeyValuePoint(szValue, pBox->bmaxs); |
|
} |
|
|
|
return ChunkFile_Ok; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
ChunkFileResult_t CManifest::LoadCordonKeyCallback( const char *szKey, const char *szValue, Cordon_t *pCordon ) |
|
{ |
|
if (!stricmp(szKey, "name")) |
|
{ |
|
pCordon->m_szName.Set( szValue ); |
|
} |
|
// Whether this particular cordon volume is active. |
|
else if (!stricmp(szKey, "active")) |
|
{ |
|
CChunkFile::ReadKeyValueBool(szValue, pCordon->m_bActive); |
|
} |
|
|
|
return ChunkFile_Ok; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
ChunkFileResult_t CManifest::LoadCordonCallback( CChunkFile *pFile, CManifest *pManifest ) |
|
{ |
|
// Add a new cordon which will be filled in by the key callback |
|
pManifest->m_Cordons.AddToTail(); |
|
Cordon_t &cordon = pManifest->m_Cordons.Tail(); |
|
|
|
CChunkHandlerMap Handlers; |
|
Handlers.AddHandler( "box", (ChunkHandler_t)CManifest::LoadCordonBoxCallback, (void *)&cordon ); |
|
|
|
pFile->PushHandlers(&Handlers); |
|
ChunkFileResult_t eResult = pFile->ReadChunk( (KeyHandler_t)LoadCordonKeyCallback, (void *)&cordon ); |
|
pFile->PopHandlers(); |
|
|
|
return(eResult); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------- |
|
// Parses keys that are applicable to all cordons in the map. |
|
//----------------------------------------------------------------------------- |
|
ChunkFileResult_t CManifest::LoadCordonsKeyCallback( const char *szKey, const char *szValue, CManifest *pManifest ) |
|
{ |
|
// Whether the cordoning system is enabled or disabled. |
|
if ( !stricmp( szKey, "active" ) ) |
|
{ |
|
CChunkFile::ReadKeyValueBool( szValue, pManifest->m_bIsCordoning ); |
|
} |
|
|
|
return ChunkFile_Ok; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Parses the VMF chunk that pertains to all the cordons in the map: |
|
// |
|
// cordons |
|
// { |
|
// "active" "true" |
|
// cordon |
|
// { |
|
// "active" "true" |
|
// "box" |
|
// { |
|
// "mins" "-1024, -1024, -1024" |
|
// "maxs" "1024, 1024, 1024" |
|
// } |
|
// ...may be more boxes... |
|
// } |
|
// ...may be more cordons... |
|
// } |
|
// |
|
//----------------------------------------------------------------------------- |
|
ChunkFileResult_t CManifest::LoadCordonsCallback( CChunkFile *pFile, CManifest *pManifest ) |
|
{ |
|
CChunkHandlerMap Handlers; |
|
Handlers.AddHandler( "cordon", (ChunkHandler_t)CManifest::LoadCordonCallback, pManifest ); |
|
|
|
pFile->PushHandlers(&Handlers); |
|
ChunkFileResult_t eResult = pFile->ReadChunk( (KeyHandler_t)LoadCordonsKeyCallback, pManifest ); |
|
pFile->PopHandlers(); |
|
|
|
return(eResult); |
|
} |
|
|
|
extern ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity); |
|
|
|
ChunkFileResult_t CManifest::LoadManifestCordoningPrefsCallback( CChunkFile *pFile, CManifest *pDoc ) |
|
{ |
|
pDoc->m_CordoningMapEnt = &g_MainMap->entities[g_MainMap->num_entities]; |
|
g_MainMap->num_entities++; |
|
memset( pDoc->m_CordoningMapEnt, 0, sizeof( *pDoc->m_CordoningMapEnt ) ); |
|
pDoc->m_CordoningMapEnt->firstbrush = g_MainMap->nummapbrushes; |
|
pDoc->m_CordoningMapEnt->numbrushes = 0; |
|
|
|
LoadEntity_t LoadEntity; |
|
LoadEntity.pEntity = pDoc->m_CordoningMapEnt; |
|
|
|
// No default flags/contents |
|
LoadEntity.nBaseFlags = 0; |
|
LoadEntity.nBaseContents = 0; |
|
|
|
// |
|
// Set up handlers for the subchunks that we are interested in. |
|
// |
|
CChunkHandlerMap Handlers; |
|
Handlers.AddHandler( "cordons", ( ChunkHandler_t )CManifest::LoadCordonsCallback, pDoc ); |
|
Handlers.AddHandler("solid", (ChunkHandler_t)::LoadSolidCallback, &LoadEntity); |
|
pFile->PushHandlers(&Handlers); |
|
|
|
ChunkFileResult_t eResult = ChunkFile_Ok; |
|
|
|
eResult = pFile->ReadChunk(); |
|
|
|
pFile->PopHandlers(); |
|
|
|
return( eResult ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: this function will create a new entity pair |
|
// Input : pKey - the key of the pair |
|
// pValue - the value of the pair |
|
// Output : returns a newly created epair structure |
|
//----------------------------------------------------------------------------- |
|
epair_t *CManifest::CreateEPair( char *pKey, char *pValue ) |
|
{ |
|
epair_t *pEPair = new epair_t; |
|
|
|
pEPair->key = new char[ strlen( pKey ) + 1 ]; |
|
pEPair->value = new char[ strlen( pValue ) + 1 ]; |
|
|
|
strcpy( pEPair->key, pKey ); |
|
strcpy( pEPair->value, pValue ); |
|
|
|
return pEPair; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: this function will load in all of the submaps belonging to this manifest, |
|
// except for the top level map, which is loaded separately. |
|
// Input : pMapFile - the top level map that was previously loaded |
|
// pszFileName - the absolute file name of the top level map file |
|
// Output : returns true if all submaps were loaded |
|
//----------------------------------------------------------------------------- |
|
bool CManifest::LoadSubMaps( CMapFile *pMapFile, const char *pszFileName ) |
|
{ |
|
entity_t *InstanceEntity; |
|
epair_t *pEPair; |
|
|
|
InstanceEntity = &pMapFile->entities[ pMapFile->num_entities ]; |
|
pMapFile->num_entities++; |
|
memset( InstanceEntity, 0, sizeof( *InstanceEntity ) ); |
|
|
|
InstanceEntity->origin.Init( 0.0f, 0.0f, 0.0f ); |
|
pEPair = CreateEPair( "classname", "worldspawn" ); |
|
pEPair->next = InstanceEntity->epairs; |
|
InstanceEntity->epairs = pEPair; |
|
|
|
for( int i = 0; i < m_Maps.Count(); i++ ) |
|
{ |
|
// if ( m_Maps[ i ]->m_bTopLevelMap == false ) |
|
{ |
|
char FileName[ MAX_PATH ]; |
|
|
|
sprintf( FileName, "%s%s", m_InstancePath, m_Maps[ i ]->m_RelativeMapFileName ); |
|
|
|
InstanceEntity = &pMapFile->entities[ pMapFile->num_entities ]; |
|
pMapFile->num_entities++; |
|
|
|
memset( InstanceEntity, 0, sizeof( *InstanceEntity ) ); |
|
InstanceEntity->origin.Init( 0.0f, 0.0f, 0.0f ); |
|
|
|
pEPair = CreateEPair( "angles", "0 0 0" ); |
|
pEPair->next = InstanceEntity->epairs; |
|
InstanceEntity->epairs = pEPair; |
|
|
|
char temp[ 128 ]; |
|
sprintf( temp, "%d", GameData::NAME_FIXUP_NONE ); |
|
|
|
pEPair = CreateEPair( "fixup_style", temp ); |
|
pEPair->next = InstanceEntity->epairs; |
|
InstanceEntity->epairs = pEPair; |
|
|
|
pEPair = CreateEPair( "classname", "func_instance" ); |
|
pEPair->next = InstanceEntity->epairs; |
|
InstanceEntity->epairs = pEPair; |
|
|
|
pEPair = CreateEPair( "file", m_Maps[ i ]->m_RelativeMapFileName ); |
|
pEPair->next = InstanceEntity->epairs; |
|
InstanceEntity->epairs = pEPair; |
|
|
|
if ( m_Maps[ i ]->m_bTopLevelMap == true ) |
|
{ |
|
pEPair = CreateEPair( "toplevel", "1" ); |
|
pEPair->next = InstanceEntity->epairs; |
|
InstanceEntity->epairs = pEPair; |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
bool CManifest::LoadVMFManifestUserPrefs( const char *pszFileName ) |
|
{ |
|
char UserName[ MAX_PATH ], FileName[ MAX_PATH ], UserPrefsFileName[ MAX_PATH ]; |
|
DWORD UserNameSize; |
|
|
|
UserNameSize = sizeof( UserName ); |
|
if ( GetUserName( UserName, &UserNameSize ) == 0 ) |
|
{ |
|
strcpy( UserPrefsFileName, "default" ); |
|
} |
|
|
|
sprintf( UserPrefsFileName, "\\%s.vmm_prefs", UserName ); |
|
V_StripExtension( pszFileName, FileName, sizeof( FileName ) ); |
|
strcat( FileName, UserPrefsFileName ); |
|
|
|
FILE *fp = fopen( FileName, "rb" ); |
|
if ( !fp ) |
|
{ |
|
return false; |
|
} |
|
|
|
CChunkFile File; |
|
ChunkFileResult_t eResult = File.Open( FileName, ChunkFile_Read ); |
|
|
|
if ( eResult == ChunkFile_Ok ) |
|
{ |
|
// |
|
// Set up handlers for the subchunks that we are interested in. |
|
// |
|
CChunkHandlerMap Handlers; |
|
Handlers.AddHandler( "cordoning", ( ChunkHandler_t )CManifest::LoadManifestCordoningPrefsCallback, this ); |
|
|
|
// Handlers.SetErrorHandler( ( ChunkErrorHandler_t )CMapDoc::HandleLoadError, this); |
|
|
|
File.PushHandlers(&Handlers); |
|
|
|
while( eResult == ChunkFile_Ok ) |
|
{ |
|
eResult = File.ReadChunk(); |
|
} |
|
|
|
if ( eResult == ChunkFile_EOF ) |
|
{ |
|
eResult = ChunkFile_Ok; |
|
} |
|
|
|
File.PopHandlers(); |
|
} |
|
|
|
if ( eResult == ChunkFile_Ok ) |
|
{ |
|
} |
|
else |
|
{ |
|
// no pref message for now |
|
// GetMainWnd()->MessageBox( File.GetErrorText( eResult ), "Error loading manifest!", MB_OK | MB_ICONEXCLAMATION ); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Loads a .VMM file. |
|
// Input : pszFileName - Full path of the map file to load. |
|
//----------------------------------------------------------------------------- |
|
bool CManifest::LoadVMFManifest( const char *pszFileName ) |
|
{ |
|
V_StripExtension( pszFileName, m_InstancePath, sizeof( m_InstancePath ) ); |
|
strcat( m_InstancePath, "\\" ); |
|
|
|
CChunkFile File; |
|
ChunkFileResult_t eResult = File.Open( pszFileName, ChunkFile_Read ); |
|
if ( eResult != ChunkFile_Ok ) |
|
{ |
|
g_MapError.ReportError( File.GetErrorText( eResult ) ); |
|
return false; |
|
} |
|
|
|
CChunkHandlerMap Handlers; |
|
Handlers.AddHandler( "Maps", ( ChunkHandler_t )LoadManifestMapsCallback, this ); |
|
|
|
File.PushHandlers(&Handlers); |
|
|
|
while (eResult == ChunkFile_Ok) |
|
{ |
|
eResult = File.ReadChunk(); |
|
} |
|
|
|
if (eResult == ChunkFile_EOF) |
|
{ |
|
eResult = ChunkFile_Ok; |
|
} |
|
|
|
File.PopHandlers(); |
|
|
|
if ( eResult == ChunkFile_Ok ) |
|
{ |
|
int index = g_Maps.AddToTail( new CMapFile() ); |
|
g_LoadingMap = g_Maps[ index ]; |
|
if ( g_MainMap == NULL ) |
|
{ |
|
g_MainMap = g_LoadingMap; |
|
} |
|
|
|
LoadSubMaps( g_LoadingMap, pszFileName ); |
|
|
|
LoadVMFManifestUserPrefs( pszFileName ); |
|
} |
|
|
|
return ( eResult == ChunkFile_Ok ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
void CManifest::CordonWorld( ) |
|
{ |
|
if ( m_bIsCordoning == false ) |
|
{ |
|
return; |
|
} |
|
|
|
for ( int i = 0; i < g_MainMap->num_entities; i++ ) |
|
{ |
|
if ( i == 0 ) |
|
{ // for world spawn, we look at brushes |
|
for( int nBrushNum = 0; nBrushNum < g_MainMap->entities[ i ].numbrushes; nBrushNum++ ) |
|
{ |
|
int nIndex = g_MainMap->entities[ i ].firstbrush + nBrushNum; |
|
|
|
bool bRemove = true; |
|
|
|
for( int nCordon = 0; nCordon < m_Cordons.Count(); nCordon++ ) |
|
{ |
|
if ( m_Cordons[ nCordon ].m_bActive == false ) |
|
{ |
|
continue; |
|
} |
|
|
|
for( int nBox = 0; nBox < m_Cordons[ nCordon ].m_Boxes.Count(); nBox++ ) |
|
{ |
|
if ( m_Cordons[ nCordon ].m_Boxes[ nBox ].IsIntersectingBox( g_MainMap->mapbrushes[ nIndex ].mins, g_MainMap->mapbrushes[ nIndex ].maxs ) == true ) |
|
{ |
|
bRemove = false; |
|
break; |
|
} |
|
} |
|
|
|
if ( bRemove == false ) |
|
{ |
|
break; |
|
} |
|
} |
|
|
|
if ( bRemove ) |
|
{ |
|
int nSize = ( g_MainMap->entities[ i ].numbrushes - nBrushNum - 1 ) * sizeof( g_MainMap->mapbrushes[ 0 ] ); |
|
memmove( &g_MainMap->mapbrushes[ nIndex ], &g_MainMap->mapbrushes[ nIndex + 1 ], nSize ); |
|
g_MainMap->entities[ i ].numbrushes--; |
|
nBrushNum--; |
|
} |
|
} |
|
} |
|
else if ( &g_MainMap->entities[ i ] != m_CordoningMapEnt ) |
|
{ // for all other entities, even if they include brushes, we look at origin |
|
if ( g_MainMap->entities[ i ].numbrushes == 0 && g_MainMap->entities[ i ].epairs == NULL ) |
|
{ |
|
continue; |
|
} |
|
|
|
bool bRemove = true; |
|
|
|
for( int nCordon = 0; nCordon < m_Cordons.Count(); nCordon++ ) |
|
{ |
|
if ( m_Cordons[ nCordon ].m_bActive == false ) |
|
{ |
|
continue; |
|
} |
|
|
|
for( int nBox = 0; nBox < m_Cordons[ nCordon ].m_Boxes.Count(); nBox++ ) |
|
{ |
|
if ( m_Cordons[ nCordon ].m_Boxes[ nBox ].ContainsPoint( g_MainMap->entities[ i ].origin ) == true ) |
|
{ |
|
bRemove = false; |
|
break; |
|
} |
|
} |
|
|
|
if ( bRemove == false ) |
|
{ |
|
break; |
|
} |
|
} |
|
|
|
if ( bRemove ) |
|
{ |
|
g_MainMap->entities[ i ].numbrushes = 0; |
|
g_MainMap->entities[ i ].epairs = NULL; |
|
} |
|
} |
|
} |
|
|
|
if ( m_CordoningMapEnt ) |
|
{ |
|
g_MainMap->MoveBrushesToWorldGeneral( m_CordoningMapEnt ); |
|
m_CordoningMapEnt->numbrushes = 0; |
|
m_CordoningMapEnt->epairs = NULL; |
|
} |
|
}
|
|
|