|
|
//===== Copyright � 1996-2009, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//===========================================================================//
#include "matchext_swarm.h"
#include "swarm.spa.h"
#include "utlvector.h"
#include "utlstringmap.h"
#include "fmtstr.h"
#include "filesystem.h"
#define g_pFileSystem g_pFullFileSystem
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
CMatchExtSwarm::CMatchExtSwarm() { m_pKeyValues = NULL; }
CMatchExtSwarm::~CMatchExtSwarm() { if ( m_pKeyValues ) { m_pKeyValues->deleteThis(); m_pKeyValues = NULL; } }
static CMatchExtSwarm g_MatchExtSwarm; CMatchExtSwarm *g_pMatchExtSwarm = &g_MatchExtSwarm; EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CMatchExtSwarm, IMatchExtSwarm, IMATCHEXT_SWARM_INTERFACE, g_MatchExtSwarm );
//
// Implementation
//
void CMatchExtSwarm::ParseMissionFromFile( char const *szFile, bool bBuiltIn ) { KeyValues *pMissionModes = m_pKeyValues->FindKey( "GameModes" ); KeyValues *pMissionsRoot = m_pKeyValues->FindKey( "Missions" ); if ( !pMissionsRoot || !pMissionModes ) return;
// See if we already have this mission
if ( m_mapFilesLoaded.Find( szFile ) != m_mapFilesLoaded.InvalidIndex() ) return; m_mapFilesLoaded[ szFile ] = NULL;
KeyValues *missionKeys = new KeyValues( "mission" ); KeyValues::AutoDelete autodelete_missionKeys( missionKeys ); // allows for early error-return
bool bLoadResult = missionKeys->LoadFromFile( g_pFileSystem, szFile ); if ( !bLoadResult ) { Warning( "MissionManager: Mission file \"%s\" is malformed, failed to parse.\n", szFile ); return; }
const char *name = missionKeys->GetString( "name", NULL ); const char *campaignVersion = missionKeys->GetString( "version", NULL );
if ( !name || !campaignVersion ) { Warning( "MissionManager: Mission file \"%s\" is missing name and version\n", szFile ); return; }
// Check invalid characters
for ( char const *pCharCheck = name; *pCharCheck; ++ pCharCheck ) { char const c = *pCharCheck; if ( !( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || ( c >= '0' && c <= '9' ) ) ) { Warning( "MissionManager: Only alphanumeric characters allowed in mission name: \"%s\" in \"%s\"\n", name, szFile ); return; } } for ( char const *pCharCheck = campaignVersion; *pCharCheck; ++ pCharCheck ) { char const c = *pCharCheck; if ( !( c >= '0' && c <= '9' ) ) { Warning( "MissionManager: Only numeric characters allowed in mission version: \"%s\" in \"%s\"\n", campaignVersion, szFile ); return; } }
// Now go through the game modes
KeyValues *modes = missionKeys->FindKey( "modes" ); if ( !modes ) { Warning( "MissionManager: Mission file \"%s\" is missing data for any game modes\n", szFile ); return; }
CFmtStr sNameVersion( "%s_%s", name, campaignVersion );
// Don't allow duplicates
if ( m_mapMissionsLoaded.Find( name ) != m_mapMissionsLoaded.InvalidIndex() ) { Warning( "MissionManager: Duplicate mission \"%s\" in file \"%s\", already loaded from \"%s\".\n", name, szFile, m_mapMissionsLoaded[ name ]->GetString( "cfgfile" ) ); return; }
DevMsg( "\tMission %s ver %s loading...\n", name, campaignVersion );
// Validate DisplayTitle and Description
char const *szDisplayTitle = missionKeys->GetString( "displaytitle" ); if ( !szDisplayTitle || !*szDisplayTitle ) missionKeys->SetString( "displaytitle", name );
// Set auto-generated fields
missionKeys->SetName( name ); missionKeys->SetInt( "builtin", bBuiltIn ? 1 : 0 ); missionKeys->SetString( "cfgfile", szFile ); missionKeys->SetString( "cfgtag", sNameVersion );
// Load game modes
int numGameModes = 0; for ( KeyValues *modeName = pMissionModes->GetFirstTrueSubKey(); modeName; modeName = modeName->GetNextTrueSubKey() ) { KeyValues *mode = modes->FindKey( modeName->GetName() ); if ( !mode ) continue;
int numChapters = 0; while ( KeyValues *pChapterKey = mode->FindKey( CFmtStr( "%d", numChapters + 1 ) ) ) { // Check required fields
char const *szMap = pChapterKey->GetString( "map" ); if ( !szMap || !*szMap ) { Warning( "MissionManager: Mission file \"%s\" has invalid map specified for modes/%s/%s\n", szFile, modeName->GetName(), pChapterKey->GetName() ); numChapters = 0; break; }
// DisplayName
char const *szDisplayName = pChapterKey->GetString( "displayname" ); if ( !szDisplayName || !*szDisplayName ) { pChapterKey->SetString( "displayname", CFmtStr( "%s-%s", name, pChapterKey->GetName() ) ); }
// Image
char const *szImage = pChapterKey->GetString( "image" ); if ( !szImage || !*szImage ) { pChapterKey->SetString( "image", "maps/unknown" ); }
// Set the automatic fields
pChapterKey->SetInt( "chapter", numChapters + 1 ); // This chapter was valid
++ numChapters; }
if ( !numChapters ) { modes->RemoveSubKey( mode ); mode->deleteThis(); mode = NULL;
Warning( "MissionManager: Mission file \"%s\" has invalid settings for game mode %s\n", szFile, modeName->GetName() ); continue; }
mode->SetInt( "chapters", numChapters ); DevMsg( "\t\tloaded %d %s chapters.\n", numChapters, modeName->GetName() );
++ numGameModes; }
if ( !numGameModes ) { Warning( "MissionManager: Mission file \"%s\" does not have valid data for any supported game mode\n", szFile ); return; }
//
// Bind the loaded mission keys into the system
//
m_mapFilesLoaded[ szFile ] = missionKeys; m_mapMissionsLoaded[ name ] = missionKeys;
pMissionsRoot->AddSubKey( missionKeys ); autodelete_missionKeys.Assign( NULL ); // prevent automatic deletion
// Register all the loaded game modes
for ( KeyValues *modeName = pMissionModes->GetFirstTrueSubKey(); modeName; modeName = modeName->GetNextTrueSubKey() ) { KeyValues *mode = modes->FindKey( modeName->GetName() ); if ( !mode ) continue;
modeName->SetPtr( name, missionKeys ); }
DevMsg( "\tMission %s ver %s loaded %d game modes.\n", name, campaignVersion, numGameModes ); }
void CMatchExtSwarm::MakeGameModeCopy( char const *szGameMode, char const *szCopyName ) { // Fix the GameModes key
if ( KeyValues *pKeyMode = m_pKeyValues->FindKey( CFmtStr( "GameModes/%s", szGameMode ) ) ) { pKeyMode = pKeyMode->MakeCopy(); pKeyMode->SetName( szCopyName ); m_pKeyValues->FindKey( "GameModes" )->AddSubKey( pKeyMode ); }
// Fix all missions
KeyValues *pMission = GetAllMissions(); for ( pMission = pMission ? pMission->GetFirstTrueSubKey() : NULL; pMission; pMission = pMission->GetNextTrueSubKey() ) { if ( KeyValues *pKeyMode = pMission->FindKey( CFmtStr( "modes/%s", szGameMode ) ) ) { pKeyMode = pKeyMode->MakeCopy(); pKeyMode->SetName( szCopyName ); pMission->FindKey( "modes" )->AddSubKey( pKeyMode ); } } }
void CMatchExtSwarm::Initialize() { DevMsg( "Loading Mission Data\n" ); MEM_ALLOC_CREDIT();
if ( m_pKeyValues ) { m_pKeyValues->deleteThis(); m_pKeyValues = NULL; }
m_mapFilesLoaded.Purge(); m_mapMissionsLoaded.Purge();
m_pKeyValues = KeyValues::FromString( "AlienSwarm", " GameModes { " " coop { } " #ifndef _DEMO
" versus { } " " survival { } " " scavenge { } " #endif
" } " " Missions { " // read from mission files
" } " );
//
// Parse built-in missions
//
#ifndef _DEMO
ParseMissionFromFile( "missions/campaign1.txt", true ); ParseMissionFromFile( "missions/campaign2.txt", true ); ParseMissionFromFile( "missions/campaign3.txt", true ); ParseMissionFromFile( "missions/campaign4.txt", true ); ParseMissionFromFile( "missions/campaign5.txt", true ); ParseMissionFromFile( "missions/credits.txt", true );
//
// Search missions using the wildcards
//
char szMissionPath[_MAX_PATH]; Q_snprintf( szMissionPath, sizeof( szMissionPath ), "missions/*.txt" ); Q_FixSlashes( szMissionPath );
FileFindHandle_t handle; const char *pFoundFile = g_pFileSystem->FindFirst( szMissionPath, &handle ); while ( pFoundFile ) { char pFilename[ MAX_PATH ]; V_snprintf( pFilename, ARRAYSIZE(pFilename), "missions/%s", pFoundFile ); pFoundFile = g_pFileSystem->FindNext( handle );
ParseMissionFromFile( pFilename, false ); }
#else
ParseMissionFromFile( "missions/demo.txt", true ); #endif
#ifndef _DEMO
// Make game mode copies
MakeGameModeCopy( "versus", "teamversus" ); MakeGameModeCopy( "scavenge", "teamscavenge" ); MakeGameModeCopy( "coop", "realism" ); #endif
DevMsg( "Loading Mission Data Finished\n" ); }
void CMatchExtSwarm::DebugPrint() { KeyValuesDumpAsDevMsg( m_pKeyValues, 1, 0 ); }
//--------------------------------------------------------------------------------------------------------
CON_COMMAND( mission_reload, "Reload mission metadata" ) { g_MatchExtSwarm.Initialize(); }
CON_COMMAND_F( mission_debug_print, "Print all mission metadata", FCVAR_DEVELOPMENTONLY ) { g_MatchExtSwarm.DebugPrint(); }
KeyValues * CMatchExtSwarm::GetAllMissions() { if ( !m_pKeyValues ) return NULL;
return m_pKeyValues->FindKey( "Missions" ); }
// Get server map information for the session settings
KeyValues * CMatchExtSwarm::GetMapInfo( KeyValues *pSettings, KeyValues **ppMissionInfo ) { if ( !m_pKeyValues ) return NULL;
char const *szGameMode = pSettings->GetString( "game/mode", NULL ); if ( !szGameMode || !*szGameMode ) return NULL;
char const *szCampaign = pSettings->GetString( "game/campaign", NULL ); if ( !szCampaign || !*szCampaign ) return NULL;
int nMapNumber = pSettings->GetInt( "game/chapter", 0 ); if ( nMapNumber <= 0 ) return NULL;
// Find the campaign key
KeyValues *pMissionKey = ( KeyValues * ) m_pKeyValues->GetPtr( CFmtStr( "GameModes/%s/%s", szGameMode, szCampaign ), NULL ); if ( !pMissionKey ) return NULL;
// Find the total number of chapters in that mission's game mode
int numChapters = pMissionKey->GetInt( CFmtStr( "modes/%s/chapters", szGameMode ), 0 ); if ( nMapNumber > numChapters ) return NULL;
KeyValues *pChapterKey = pMissionKey->FindKey( CFmtStr( "modes/%s/%d", szGameMode, nMapNumber ) ); if ( !pChapterKey ) return NULL;
if ( ppMissionInfo ) *ppMissionInfo = pMissionKey;
return pChapterKey; }
KeyValues * CMatchExtSwarm::GetMapInfoByBspName( KeyValues *pSettings, char const *szBspMapName, KeyValues **ppMissionInfo ) { if ( !m_pKeyValues ) return NULL;
Assert( szBspMapName ); if ( !szBspMapName || !*szBspMapName ) return NULL;
char const *szGameMode = pSettings->GetString( "game/mode", NULL ); if ( !szGameMode || !*szGameMode ) return NULL;
// Walk all the missions in that game mode
KeyValues *pModeMissions = m_pKeyValues->FindKey( CFmtStr( "GameModes/%s", szGameMode ) ); if ( !pModeMissions ) return NULL;
for ( KeyValues *pMissionName = pModeMissions->GetFirstValue(); pMissionName; pMissionName = pMissionName->GetNextValue() ) { KeyValues *pMission = ( KeyValues * ) pMissionName->GetPtr(); if ( !pMission ) continue;
KeyValues *pChapters = pMission->FindKey( CFmtStr( "modes/%s", szGameMode ) ); if ( !pChapters ) continue;
int numChapters = pChapters->GetInt( "chapters" ); for ( int k = 1; k <= numChapters; ++ k ) { KeyValues *pMap = pChapters->FindKey( CFmtStr( "%d", k ) ); if ( !pMap ) break;
char const *szBspName = pMap->GetString( "map" ); if ( !Q_stricmp( szBspName, szBspMapName ) ) { if ( ppMissionInfo ) *ppMissionInfo = pMission;
return pMap; } } }
return NULL; }
|