Counter Strike : Global Offensive Source Code
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.

422 lines
12 KiB

  1. //===== Copyright � 1996-2009, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "matchext_swarm.h"
  7. #include "swarm.spa.h"
  8. #include "utlvector.h"
  9. #include "utlstringmap.h"
  10. #include "fmtstr.h"
  11. #include "filesystem.h"
  12. #define g_pFileSystem g_pFullFileSystem
  13. // memdbgon must be the last include file in a .cpp file!!!
  14. #include "tier0/memdbgon.h"
  15. CMatchExtSwarm::CMatchExtSwarm()
  16. {
  17. m_pKeyValues = NULL;
  18. }
  19. CMatchExtSwarm::~CMatchExtSwarm()
  20. {
  21. if ( m_pKeyValues )
  22. {
  23. m_pKeyValues->deleteThis();
  24. m_pKeyValues = NULL;
  25. }
  26. }
  27. static CMatchExtSwarm g_MatchExtSwarm;
  28. CMatchExtSwarm *g_pMatchExtSwarm = &g_MatchExtSwarm;
  29. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CMatchExtSwarm, IMatchExtSwarm,
  30. IMATCHEXT_SWARM_INTERFACE, g_MatchExtSwarm );
  31. //
  32. // Implementation
  33. //
  34. void CMatchExtSwarm::ParseMissionFromFile( char const *szFile, bool bBuiltIn )
  35. {
  36. KeyValues *pMissionModes = m_pKeyValues->FindKey( "GameModes" );
  37. KeyValues *pMissionsRoot = m_pKeyValues->FindKey( "Missions" );
  38. if ( !pMissionsRoot || !pMissionModes )
  39. return;
  40. // See if we already have this mission
  41. if ( m_mapFilesLoaded.Find( szFile ) != m_mapFilesLoaded.InvalidIndex() )
  42. return;
  43. m_mapFilesLoaded[ szFile ] = NULL;
  44. KeyValues *missionKeys = new KeyValues( "mission" );
  45. KeyValues::AutoDelete autodelete_missionKeys( missionKeys ); // allows for early error-return
  46. bool bLoadResult = missionKeys->LoadFromFile( g_pFileSystem, szFile );
  47. if ( !bLoadResult )
  48. {
  49. Warning( "MissionManager: Mission file \"%s\" is malformed, failed to parse.\n", szFile );
  50. return;
  51. }
  52. const char *name = missionKeys->GetString( "name", NULL );
  53. const char *campaignVersion = missionKeys->GetString( "version", NULL );
  54. if ( !name || !campaignVersion )
  55. {
  56. Warning( "MissionManager: Mission file \"%s\" is missing name and version\n", szFile );
  57. return;
  58. }
  59. // Check invalid characters
  60. for ( char const *pCharCheck = name; *pCharCheck; ++ pCharCheck )
  61. {
  62. char const c = *pCharCheck;
  63. if ( !( ( c >= 'a' && c <= 'z' ) ||
  64. ( c >= 'A' && c <= 'Z' ) ||
  65. ( c >= '0' && c <= '9' ) ) )
  66. {
  67. Warning( "MissionManager: Only alphanumeric characters allowed in mission name: \"%s\" in \"%s\"\n", name, szFile );
  68. return;
  69. }
  70. }
  71. for ( char const *pCharCheck = campaignVersion; *pCharCheck; ++ pCharCheck )
  72. {
  73. char const c = *pCharCheck;
  74. if ( !( c >= '0' && c <= '9' ) )
  75. {
  76. Warning( "MissionManager: Only numeric characters allowed in mission version: \"%s\" in \"%s\"\n", campaignVersion, szFile );
  77. return;
  78. }
  79. }
  80. // Now go through the game modes
  81. KeyValues *modes = missionKeys->FindKey( "modes" );
  82. if ( !modes )
  83. {
  84. Warning( "MissionManager: Mission file \"%s\" is missing data for any game modes\n", szFile );
  85. return;
  86. }
  87. CFmtStr sNameVersion( "%s_%s", name, campaignVersion );
  88. // Don't allow duplicates
  89. if ( m_mapMissionsLoaded.Find( name ) != m_mapMissionsLoaded.InvalidIndex() )
  90. {
  91. Warning( "MissionManager: Duplicate mission \"%s\" in file \"%s\", already loaded from \"%s\".\n",
  92. name, szFile, m_mapMissionsLoaded[ name ]->GetString( "cfgfile" ) );
  93. return;
  94. }
  95. DevMsg( "\tMission %s ver %s loading...\n", name, campaignVersion );
  96. // Validate DisplayTitle and Description
  97. char const *szDisplayTitle = missionKeys->GetString( "displaytitle" );
  98. if ( !szDisplayTitle || !*szDisplayTitle )
  99. missionKeys->SetString( "displaytitle", name );
  100. // Set auto-generated fields
  101. missionKeys->SetName( name );
  102. missionKeys->SetInt( "builtin", bBuiltIn ? 1 : 0 );
  103. missionKeys->SetString( "cfgfile", szFile );
  104. missionKeys->SetString( "cfgtag", sNameVersion );
  105. // Load game modes
  106. int numGameModes = 0;
  107. for ( KeyValues *modeName = pMissionModes->GetFirstTrueSubKey(); modeName; modeName = modeName->GetNextTrueSubKey() )
  108. {
  109. KeyValues *mode = modes->FindKey( modeName->GetName() );
  110. if ( !mode )
  111. continue;
  112. int numChapters = 0;
  113. while ( KeyValues *pChapterKey = mode->FindKey( CFmtStr( "%d", numChapters + 1 ) ) )
  114. {
  115. // Check required fields
  116. char const *szMap = pChapterKey->GetString( "map" );
  117. if ( !szMap || !*szMap )
  118. {
  119. Warning( "MissionManager: Mission file \"%s\" has invalid map specified for modes/%s/%s\n",
  120. szFile, modeName->GetName(), pChapterKey->GetName() );
  121. numChapters = 0;
  122. break;
  123. }
  124. // DisplayName
  125. char const *szDisplayName = pChapterKey->GetString( "displayname" );
  126. if ( !szDisplayName || !*szDisplayName )
  127. {
  128. pChapterKey->SetString( "displayname", CFmtStr( "%s-%s", name, pChapterKey->GetName() ) );
  129. }
  130. // Image
  131. char const *szImage = pChapterKey->GetString( "image" );
  132. if ( !szImage || !*szImage )
  133. {
  134. pChapterKey->SetString( "image", "maps/unknown" );
  135. }
  136. // Set the automatic fields
  137. pChapterKey->SetInt( "chapter", numChapters + 1 );
  138. // This chapter was valid
  139. ++ numChapters;
  140. }
  141. if ( !numChapters )
  142. {
  143. modes->RemoveSubKey( mode );
  144. mode->deleteThis();
  145. mode = NULL;
  146. Warning( "MissionManager: Mission file \"%s\" has invalid settings for game mode %s\n",
  147. szFile, modeName->GetName() );
  148. continue;
  149. }
  150. mode->SetInt( "chapters", numChapters );
  151. DevMsg( "\t\tloaded %d %s chapters.\n", numChapters, modeName->GetName() );
  152. ++ numGameModes;
  153. }
  154. if ( !numGameModes )
  155. {
  156. Warning( "MissionManager: Mission file \"%s\" does not have valid data for any supported game mode\n", szFile );
  157. return;
  158. }
  159. //
  160. // Bind the loaded mission keys into the system
  161. //
  162. m_mapFilesLoaded[ szFile ] = missionKeys;
  163. m_mapMissionsLoaded[ name ] = missionKeys;
  164. pMissionsRoot->AddSubKey( missionKeys );
  165. autodelete_missionKeys.Assign( NULL ); // prevent automatic deletion
  166. // Register all the loaded game modes
  167. for ( KeyValues *modeName = pMissionModes->GetFirstTrueSubKey(); modeName; modeName = modeName->GetNextTrueSubKey() )
  168. {
  169. KeyValues *mode = modes->FindKey( modeName->GetName() );
  170. if ( !mode )
  171. continue;
  172. modeName->SetPtr( name, missionKeys );
  173. }
  174. DevMsg( "\tMission %s ver %s loaded %d game modes.\n", name, campaignVersion, numGameModes );
  175. }
  176. void CMatchExtSwarm::MakeGameModeCopy( char const *szGameMode, char const *szCopyName )
  177. {
  178. // Fix the GameModes key
  179. if ( KeyValues *pKeyMode = m_pKeyValues->FindKey( CFmtStr( "GameModes/%s", szGameMode ) ) )
  180. {
  181. pKeyMode = pKeyMode->MakeCopy();
  182. pKeyMode->SetName( szCopyName );
  183. m_pKeyValues->FindKey( "GameModes" )->AddSubKey( pKeyMode );
  184. }
  185. // Fix all missions
  186. KeyValues *pMission = GetAllMissions();
  187. for ( pMission = pMission ? pMission->GetFirstTrueSubKey() : NULL;
  188. pMission; pMission = pMission->GetNextTrueSubKey() )
  189. {
  190. if ( KeyValues *pKeyMode = pMission->FindKey( CFmtStr( "modes/%s", szGameMode ) ) )
  191. {
  192. pKeyMode = pKeyMode->MakeCopy();
  193. pKeyMode->SetName( szCopyName );
  194. pMission->FindKey( "modes" )->AddSubKey( pKeyMode );
  195. }
  196. }
  197. }
  198. void CMatchExtSwarm::Initialize()
  199. {
  200. DevMsg( "Loading Mission Data\n" );
  201. MEM_ALLOC_CREDIT();
  202. if ( m_pKeyValues )
  203. {
  204. m_pKeyValues->deleteThis();
  205. m_pKeyValues = NULL;
  206. }
  207. m_mapFilesLoaded.Purge();
  208. m_mapMissionsLoaded.Purge();
  209. m_pKeyValues = KeyValues::FromString(
  210. "AlienSwarm",
  211. " GameModes { "
  212. " coop { } "
  213. #ifndef _DEMO
  214. " versus { } "
  215. " survival { } "
  216. " scavenge { } "
  217. #endif
  218. " } "
  219. " Missions { "
  220. // read from mission files
  221. " } "
  222. );
  223. //
  224. // Parse built-in missions
  225. //
  226. #ifndef _DEMO
  227. ParseMissionFromFile( "missions/campaign1.txt", true );
  228. ParseMissionFromFile( "missions/campaign2.txt", true );
  229. ParseMissionFromFile( "missions/campaign3.txt", true );
  230. ParseMissionFromFile( "missions/campaign4.txt", true );
  231. ParseMissionFromFile( "missions/campaign5.txt", true );
  232. ParseMissionFromFile( "missions/credits.txt", true );
  233. //
  234. // Search missions using the wildcards
  235. //
  236. char szMissionPath[_MAX_PATH];
  237. Q_snprintf( szMissionPath, sizeof( szMissionPath ), "missions/*.txt" );
  238. Q_FixSlashes( szMissionPath );
  239. FileFindHandle_t handle;
  240. const char *pFoundFile = g_pFileSystem->FindFirst( szMissionPath, &handle );
  241. while ( pFoundFile )
  242. {
  243. char pFilename[ MAX_PATH ];
  244. V_snprintf( pFilename, ARRAYSIZE(pFilename), "missions/%s", pFoundFile );
  245. pFoundFile = g_pFileSystem->FindNext( handle );
  246. ParseMissionFromFile( pFilename, false );
  247. }
  248. #else
  249. ParseMissionFromFile( "missions/demo.txt", true );
  250. #endif
  251. #ifndef _DEMO
  252. // Make game mode copies
  253. MakeGameModeCopy( "versus", "teamversus" );
  254. MakeGameModeCopy( "scavenge", "teamscavenge" );
  255. MakeGameModeCopy( "coop", "realism" );
  256. #endif
  257. DevMsg( "Loading Mission Data Finished\n" );
  258. }
  259. void CMatchExtSwarm::DebugPrint()
  260. {
  261. KeyValuesDumpAsDevMsg( m_pKeyValues, 1, 0 );
  262. }
  263. //--------------------------------------------------------------------------------------------------------
  264. CON_COMMAND( mission_reload, "Reload mission metadata" )
  265. {
  266. g_MatchExtSwarm.Initialize();
  267. }
  268. CON_COMMAND_F( mission_debug_print, "Print all mission metadata", FCVAR_DEVELOPMENTONLY )
  269. {
  270. g_MatchExtSwarm.DebugPrint();
  271. }
  272. KeyValues * CMatchExtSwarm::GetAllMissions()
  273. {
  274. if ( !m_pKeyValues )
  275. return NULL;
  276. return m_pKeyValues->FindKey( "Missions" );
  277. }
  278. // Get server map information for the session settings
  279. KeyValues * CMatchExtSwarm::GetMapInfo( KeyValues *pSettings, KeyValues **ppMissionInfo )
  280. {
  281. if ( !m_pKeyValues )
  282. return NULL;
  283. char const *szGameMode = pSettings->GetString( "game/mode", NULL );
  284. if ( !szGameMode || !*szGameMode )
  285. return NULL;
  286. char const *szCampaign = pSettings->GetString( "game/campaign", NULL );
  287. if ( !szCampaign || !*szCampaign )
  288. return NULL;
  289. int nMapNumber = pSettings->GetInt( "game/chapter", 0 );
  290. if ( nMapNumber <= 0 )
  291. return NULL;
  292. // Find the campaign key
  293. KeyValues *pMissionKey = ( KeyValues * ) m_pKeyValues->GetPtr( CFmtStr( "GameModes/%s/%s", szGameMode, szCampaign ), NULL );
  294. if ( !pMissionKey )
  295. return NULL;
  296. // Find the total number of chapters in that mission's game mode
  297. int numChapters = pMissionKey->GetInt( CFmtStr( "modes/%s/chapters", szGameMode ), 0 );
  298. if ( nMapNumber > numChapters )
  299. return NULL;
  300. KeyValues *pChapterKey = pMissionKey->FindKey( CFmtStr( "modes/%s/%d", szGameMode, nMapNumber ) );
  301. if ( !pChapterKey )
  302. return NULL;
  303. if ( ppMissionInfo )
  304. *ppMissionInfo = pMissionKey;
  305. return pChapterKey;
  306. }
  307. KeyValues * CMatchExtSwarm::GetMapInfoByBspName( KeyValues *pSettings, char const *szBspMapName, KeyValues **ppMissionInfo )
  308. {
  309. if ( !m_pKeyValues )
  310. return NULL;
  311. Assert( szBspMapName );
  312. if ( !szBspMapName || !*szBspMapName )
  313. return NULL;
  314. char const *szGameMode = pSettings->GetString( "game/mode", NULL );
  315. if ( !szGameMode || !*szGameMode )
  316. return NULL;
  317. // Walk all the missions in that game mode
  318. KeyValues *pModeMissions = m_pKeyValues->FindKey( CFmtStr( "GameModes/%s", szGameMode ) );
  319. if ( !pModeMissions )
  320. return NULL;
  321. for ( KeyValues *pMissionName = pModeMissions->GetFirstValue(); pMissionName; pMissionName = pMissionName->GetNextValue() )
  322. {
  323. KeyValues *pMission = ( KeyValues * ) pMissionName->GetPtr();
  324. if ( !pMission )
  325. continue;
  326. KeyValues *pChapters = pMission->FindKey( CFmtStr( "modes/%s", szGameMode ) );
  327. if ( !pChapters )
  328. continue;
  329. int numChapters = pChapters->GetInt( "chapters" );
  330. for ( int k = 1; k <= numChapters; ++ k )
  331. {
  332. KeyValues *pMap = pChapters->FindKey( CFmtStr( "%d", k ) );
  333. if ( !pMap )
  334. break;
  335. char const *szBspName = pMap->GetString( "map" );
  336. if ( !Q_stricmp( szBspName, szBspMapName ) )
  337. {
  338. if ( ppMissionInfo )
  339. *ppMissionInfo = pMission;
  340. return pMap;
  341. }
  342. }
  343. }
  344. return NULL;
  345. }