CampaignManager.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: CampaignManager.cpp /////////////////////////////////////////////////
  24. //-----------------------------------------------------------------------------
  25. //
  26. // Electronic Arts Pacific.
  27. //
  28. // Confidential Information
  29. // Copyright (C) 2002 - All Rights Reserved
  30. //
  31. //-----------------------------------------------------------------------------
  32. //
  33. // created: Jul 2002
  34. //
  35. // Filename: CampaignManager.cpp
  36. //
  37. // author: Chris Huybregts
  38. //
  39. // purpose: The flow of the campaigns are stored up in here!
  40. //
  41. //-----------------------------------------------------------------------------
  42. ///////////////////////////////////////////////////////////////////////////////
  43. //-----------------------------------------------------------------------------
  44. // SYSTEM INCLUDES ////////////////////////////////////////////////////////////
  45. //-----------------------------------------------------------------------------
  46. //-----------------------------------------------------------------------------
  47. // USER INCLUDES //////////////////////////////////////////////////////////////
  48. //-----------------------------------------------------------------------------
  49. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  50. #include "GameClient/CampaignManager.h"
  51. #include "Common/INI.h"
  52. #include "Common/Xfer.h"
  53. #include "GameClient/ChallengeGenerals.h"//For TheChallengeGenerals, so I can save it too.
  54. #include "GameClient/GameClient.h"
  55. #include "GameNetwork/GameInfo.h" //For Challenge Info. It and Skirmish info are in the wrong place it seems.
  56. //-----------------------------------------------------------------------------
  57. // DEFINES ////////////////////////////////////////////////////////////////////
  58. //-----------------------------------------------------------------------------
  59. CampaignManager *TheCampaignManager = NULL;
  60. #ifdef _INTERNAL
  61. // for occasional debugging...
  62. //#pragma optimize("", off)
  63. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  64. #endif
  65. const FieldParse CampaignManager::m_campaignFieldParseTable[] =
  66. {
  67. { "Mission", CampaignManager::parseMissionPart, NULL, NULL },
  68. { "FirstMission", INI::parseAsciiString, NULL, offsetof( Campaign, m_firstMission ) },
  69. { "CampaignNameLabel", INI::parseAsciiString, NULL, offsetof( Campaign, m_campaignNameLabel ) },
  70. { "FinalVictoryMovie", INI::parseAsciiString, NULL, offsetof( Campaign, m_finalMovieName ) },
  71. { "IsChallengeCampaign", INI::parseBool, NULL, offsetof( Campaign, m_isChallengeCampaign ) },
  72. { "PlayerFaction", INI::parseAsciiString, NULL, offsetof( Campaign, m_playerFactionName ) },
  73. { NULL, NULL, NULL, 0 } // keep this last
  74. };
  75. //-----------------------------------------------------------------------------
  76. // PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
  77. //-----------------------------------------------------------------------------
  78. //-----------------------------------------------------------------------------
  79. void INI::parseCampaignDefinition( INI *ini )
  80. {
  81. AsciiString name;
  82. Campaign *campaign;
  83. // read the name
  84. const char* c = ini->getNextToken();
  85. name.set( c );
  86. // find existing item if present
  87. DEBUG_ASSERTCRASH( TheCampaignManager, ("parseCampaignDefinition: Unable to Get TheCampaignManager\n") );
  88. if( !TheCampaignManager )
  89. return;
  90. // If we have a previously allocated Campaign
  91. campaign = TheCampaignManager->newCampaign( name );
  92. // sanity
  93. DEBUG_ASSERTCRASH( campaign, ("parseCampaignDefinition: Unable to allocate campaign '%s'\n", name.str()) );
  94. // parse the ini definition
  95. ini->initFromINI( campaign, TheCampaignManager->getFieldParse() );
  96. } // end parseCampaignDefinition
  97. //-----------------------------------------------------------------------------
  98. Campaign::Campaign( void ):
  99. m_isChallengeCampaign(FALSE)
  100. {
  101. m_missions.clear();
  102. m_firstMission.clear();
  103. m_name.clear();
  104. m_finalMovieName.clear();
  105. }
  106. //-----------------------------------------------------------------------------
  107. Campaign::~Campaign( void )
  108. {
  109. MissionListIt it = m_missions.begin();
  110. while(it != m_missions.end())
  111. {
  112. Mission *mission = *it;
  113. it = m_missions.erase( it );
  114. if(mission)
  115. mission->deleteInstance();
  116. }
  117. }
  118. AsciiString Campaign::getFinalVictoryMovie( void )
  119. {
  120. return m_finalMovieName;
  121. }
  122. //-----------------------------------------------------------------------------
  123. Mission *Campaign::newMission( AsciiString name )
  124. {
  125. MissionListIt it;
  126. it = m_missions.begin();
  127. name.toLower();
  128. while(it != m_missions.end())
  129. {
  130. Mission *mission = *it;
  131. if(mission->m_name.compare(name) == 0)
  132. {
  133. m_missions.erase( it );
  134. mission->deleteInstance();
  135. break;
  136. }
  137. else
  138. ++it;
  139. }
  140. Mission *newMission = newInstance(Mission);
  141. newMission->m_name.set(name);
  142. m_missions.push_back(newMission);
  143. return newMission;
  144. }
  145. //-----------------------------------------------------------------------------
  146. Mission *Campaign::getMission( AsciiString missionName )
  147. {
  148. if(missionName.isEmpty())
  149. return NULL;
  150. MissionListIt it;
  151. it = m_missions.begin();
  152. // we've reached the end of the campaign
  153. while(it != m_missions.end())
  154. {
  155. Mission *mission = *it;
  156. if(mission->m_name.compare(missionName) == 0)
  157. return mission;
  158. ++it;
  159. }
  160. DEBUG_ASSERTCRASH(FALSE, ("getMission couldn't find %s", missionName.str()));
  161. return NULL;
  162. }
  163. //-----------------------------------------------------------------------------
  164. Mission *Campaign::getNextMission( Mission *current)
  165. {
  166. AsciiString name;
  167. //if passed a Null pointer, load the first mission
  168. if(!current)
  169. {
  170. name = m_firstMission;
  171. }
  172. else
  173. name = current->m_nextMission;
  174. name.toLower();
  175. MissionListIt it;
  176. it = m_missions.begin();
  177. // we've reached the end of the campaign
  178. if(name.isEmpty())
  179. return NULL;
  180. while(it != m_missions.end())
  181. {
  182. Mission *mission = *it;
  183. if(mission->m_name.compare(name) == 0)
  184. return mission;
  185. ++it;
  186. }
  187. // DEBUG_ASSERTCRASH(FALSE, ("GetNextMission couldn't find %s", current->m_nextMission.str()));
  188. return NULL;
  189. }
  190. //-----------------------------------------------------------------------------
  191. CampaignManager::CampaignManager( void )
  192. {
  193. m_campaignList.clear();
  194. m_currentCampaign = NULL;
  195. m_currentMission = NULL;
  196. m_victorious = FALSE;
  197. m_currentRankPoints = 0;
  198. m_difficulty = DIFFICULTY_NORMAL;
  199. m_xferChallengeGeneralsPlayerTemplateNum = 0;
  200. }
  201. //-----------------------------------------------------------------------------
  202. CampaignManager::~CampaignManager( void )
  203. {
  204. m_currentCampaign = NULL;
  205. m_currentMission = NULL;
  206. CampaignListIt it = m_campaignList.begin();
  207. while(it != m_campaignList.end())
  208. {
  209. Campaign *campaign = *it;
  210. it = m_campaignList.erase( it );
  211. if(campaign)
  212. campaign->deleteInstance();
  213. }
  214. }
  215. //-----------------------------------------------------------------------------
  216. void CampaignManager::init( void )
  217. {
  218. INI ini;
  219. // Read from INI all the CampaignManager
  220. ini.load( AsciiString( "Data\\INI\\Campaign.ini" ), INI_LOAD_OVERWRITE, NULL );
  221. }
  222. //-----------------------------------------------------------------------------
  223. Campaign *CampaignManager::getCurrentCampaign( void )
  224. {
  225. return m_currentCampaign;
  226. }
  227. //-----------------------------------------------------------------------------
  228. Mission *CampaignManager::getCurrentMission( void )
  229. {
  230. return m_currentMission;
  231. }
  232. //-----------------------------------------------------------------------------
  233. Mission *CampaignManager::gotoNextMission( void )
  234. {
  235. if (!m_currentCampaign || !m_currentMission)
  236. return NULL;
  237. m_currentMission = m_currentCampaign->getNextMission(m_currentMission);
  238. return m_currentMission;
  239. }
  240. //-----------------------------------------------------------------------------
  241. void CampaignManager::setCampaignAndMission( AsciiString campaign, AsciiString mission )
  242. {
  243. if(mission.isEmpty())
  244. {
  245. setCampaign(campaign);
  246. return;
  247. }
  248. CampaignListIt it;
  249. it = m_campaignList.begin();
  250. campaign.toLower();
  251. while ( it != m_campaignList.end())
  252. {
  253. Campaign *camp = *it;
  254. if(camp->m_name.compare(campaign) == 0)
  255. {
  256. m_currentCampaign = camp;
  257. m_currentMission = camp->getMission( mission );
  258. return;
  259. }
  260. ++it;
  261. }
  262. }
  263. //-----------------------------------------------------------------------------
  264. void CampaignManager::setCampaign( AsciiString campaign )
  265. {
  266. CampaignListIt it;
  267. it = m_campaignList.begin();
  268. campaign.toLower();
  269. while ( it != m_campaignList.end())
  270. {
  271. Campaign *camp = *it;
  272. if(camp->m_name.compare(campaign) == 0)
  273. {
  274. m_currentCampaign = camp;
  275. m_currentMission = camp->getNextMission( NULL );
  276. return;
  277. }
  278. ++it;
  279. }
  280. // could not find the mission. we are resetting the missions to nothing.
  281. m_currentCampaign = NULL;
  282. m_currentMission = NULL;
  283. m_currentRankPoints = 0;
  284. m_difficulty = DIFFICULTY_NORMAL;
  285. }
  286. //-----------------------------------------------------------------------------
  287. AsciiString CampaignManager::getCurrentMap( void )
  288. {
  289. if(!m_currentMission)
  290. return AsciiString::TheEmptyString;
  291. return m_currentMission->m_mapName;
  292. }
  293. // ------------------------------------------------------------------------------------------------
  294. /** Return the 0 based mission number */
  295. // ------------------------------------------------------------------------------------------------
  296. Int CampaignManager::getCurrentMissionNumber( void )
  297. {
  298. Int number = INVALID_MISSION_NUMBER;
  299. if( m_currentCampaign )
  300. {
  301. Campaign::MissionListIt it;
  302. for( it = m_currentCampaign->m_missions.begin();
  303. it != m_currentCampaign->m_missions.end();
  304. ++it )
  305. {
  306. number++;
  307. if( *it == m_currentMission )
  308. return number;
  309. }
  310. }
  311. return number;
  312. }
  313. //-----------------------------------------------------------------------------
  314. void CampaignManager::parseMissionPart( INI* ini, void *instance, void *store, const void *userData )
  315. {
  316. static const FieldParse myFieldParse[] =
  317. {
  318. { "Map", INI::parseAsciiString, NULL, offsetof( Mission, m_mapName ) },
  319. { "NextMission", INI::parseAsciiString, NULL, offsetof( Mission, m_nextMission ) },
  320. { "IntroMovie", INI::parseAsciiString, NULL, offsetof( Mission, m_movieLabel ) },
  321. { "ObjectiveLine0", INI::parseAsciiString, NULL, offsetof( Mission, m_missionObjectivesLabel[0] ) },
  322. { "ObjectiveLine1", INI::parseAsciiString, NULL, offsetof( Mission, m_missionObjectivesLabel[1] ) },
  323. { "ObjectiveLine2", INI::parseAsciiString, NULL, offsetof( Mission, m_missionObjectivesLabel[2] ) },
  324. { "ObjectiveLine3", INI::parseAsciiString, NULL, offsetof( Mission, m_missionObjectivesLabel[3] ) },
  325. { "ObjectiveLine4", INI::parseAsciiString, NULL, offsetof( Mission, m_missionObjectivesLabel[4] ) },
  326. { "BriefingVoice", INI::parseAudioEventRTS, NULL, offsetof( Mission, m_briefingVoice ) },
  327. { "UnitNames0", INI::parseAsciiString, NULL, offsetof( Mission, m_unitNames[0] ) },
  328. { "UnitNames1", INI::parseAsciiString, NULL, offsetof( Mission, m_unitNames[1] ) },
  329. { "UnitNames2", INI::parseAsciiString, NULL, offsetof( Mission, m_unitNames[2] ) },
  330. { "GeneralName", INI::parseAsciiString, NULL, offsetof( Mission, m_generalName) },
  331. { "LocationNameLabel",INI::parseAsciiString, NULL, offsetof( Mission, m_locationNameLabel ) },
  332. { "VoiceLength", INI::parseInt , NULL, offsetof( Mission, m_voiceLength ) },
  333. { NULL, NULL, NULL, 0 } // keep this last
  334. };
  335. AsciiString name;
  336. const char* c = ini->getNextToken();
  337. name.set( c );
  338. Mission *mission = ((Campaign*)instance)->newMission(name );
  339. ini->initFromINI(mission, myFieldParse);
  340. }
  341. //-----------------------------------------------------------------------------
  342. Campaign *CampaignManager::newCampaign(AsciiString name)
  343. {
  344. CampaignListIt it;
  345. it = m_campaignList.begin();
  346. name.toLower();
  347. while(it != m_campaignList.end())
  348. {
  349. Campaign *campaign = *it;
  350. if(campaign->m_name.compare(name) == 0)
  351. {
  352. m_campaignList.erase( it );
  353. campaign->deleteInstance();
  354. break;
  355. }
  356. else
  357. ++it;
  358. }
  359. Campaign *newCampaign = newInstance(Campaign);
  360. newCampaign->m_name.set(name);
  361. m_campaignList.push_back(newCampaign);
  362. return newCampaign;
  363. }
  364. // ------------------------------------------------------------------------------------------------
  365. /** Xfer method
  366. * Version Info
  367. * 1: Initial version
  368. * 2: Added RankPoints Saving
  369. * 4: Need to have Challenge info in Mission saves as well as normal saves
  370. */
  371. // ------------------------------------------------------------------------------------------------
  372. void CampaignManager::xfer( Xfer *xfer )
  373. {
  374. // version
  375. const XferVersion currentVersion = 5;
  376. XferVersion version = currentVersion;
  377. xfer->xferVersion( &version, currentVersion );
  378. // current campaign
  379. AsciiString currentCampaign;
  380. if( m_currentCampaign )
  381. currentCampaign = m_currentCampaign->m_name;
  382. xfer->xferAsciiString( &currentCampaign );
  383. // current mission
  384. AsciiString currentMission;
  385. if( m_currentMission )
  386. currentMission = m_currentMission->m_name;
  387. xfer->xferAsciiString( &currentMission );
  388. // version 2 and above has rank points!
  389. if(version >= 2)
  390. xfer->xferInt( &m_currentRankPoints );
  391. if(version >= 3)
  392. xfer->xferUser( &m_difficulty, sizeof(m_difficulty) );
  393. // when loading, need to set the current campaign and mission
  394. if( xfer->getXferMode() == XFER_LOAD )
  395. setCampaignAndMission( currentCampaign, currentMission );
  396. if( version >= 4 )
  397. {
  398. // The saving of SkirmishInfo in GameStateMap is in the "wrong" place, but I can't move it.
  399. // We need to ensure this is saved in a mission save as well as a normal save.
  400. // Singletons are bad. THis is here because a) it is one of two blocks in a Mission Save
  401. // b) It is not the block that is loaded for every save to get desc's in populating the saveload window.
  402. // So it is here. <sob> I've got nowhere else to go!
  403. Bool isChallengeCampaign = m_currentCampaign ? m_currentCampaign->m_isChallengeCampaign : FALSE;
  404. xfer->xferBool(&isChallengeCampaign);
  405. if( isChallengeCampaign )
  406. {
  407. if( TheChallengeGameInfo==NULL )
  408. {
  409. TheChallengeGameInfo = NEW SkirmishGameInfo;
  410. TheChallengeGameInfo->init();
  411. TheChallengeGameInfo->clearSlotList();
  412. TheChallengeGameInfo->reset();
  413. }
  414. xfer->xferSnapshot(TheChallengeGameInfo);
  415. }
  416. else
  417. {
  418. if( TheChallengeGameInfo )
  419. {
  420. delete TheChallengeGameInfo;
  421. TheChallengeGameInfo = NULL;
  422. }
  423. }
  424. }
  425. if( version >= 5 )
  426. {
  427. // Even more singleton goodness. TheChallengeGenerals is not a subsystem, nor even a snapshot.
  428. // I need to know what side I am for use by The Continue and Play Again buttons. I can only just
  429. // reach in and save it here.
  430. Int playerTemplateNum = TheChallengeGenerals->getCurrentPlayerTemplateNum();
  431. xfer->xferInt(&playerTemplateNum);
  432. m_xferChallengeGeneralsPlayerTemplateNum = playerTemplateNum;
  433. }
  434. } // end xfer
  435. void CampaignManager::loadPostProcess( void )
  436. {
  437. if(TheChallengeGenerals == NULL)
  438. {
  439. DEBUG_CRASH(("TheChallengeGenerals singleton does not exist. This loaded game will not have a working Continue button for GC mode."));
  440. return;
  441. }
  442. TheChallengeGenerals->setCurrentPlayerTemplateNum(m_xferChallengeGeneralsPlayerTemplateNum);
  443. }
  444. //-----------------------------------------------------------------------------
  445. // PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
  446. //-----------------------------------------------------------------------------
  447. //-----------------------------------------------------------------------------
  448. Mission::Mission( void )
  449. {
  450. m_voiceLength = 0;
  451. }
  452. //-----------------------------------------------------------------------------
  453. Mission::~Mission( void )
  454. {
  455. }