ChallengeMenu.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  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. ///////////////////////////////////////////////////////////////////////////////////////
  24. // FILE: ChallengeMenu.cpp
  25. // Author: Steve Copeland, May 2003
  26. // Description: General's Challenge Mode Menu
  27. ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common/FileSystem.h"
  30. #include "Common/GameEngine.h"
  31. //#include "Common/GameLOD.h"
  32. #include "Common/GameState.h"
  33. #include "Common/PlayerTemplate.h"
  34. #include "Common/RandomValue.h"
  35. #include "Common/Recorder.h"
  36. #include "Common/Version.h"
  37. #include "GameClient/CampaignManager.h"
  38. #include "GameClient/ChallengeGenerals.h"
  39. #include "GameClient/Gadget.h"
  40. #include "GameClient/GadgetCheckBox.h"
  41. #include "GameClient/GadgetListbox.h"
  42. #include "GameClient/GadgetStaticText.h"
  43. #include "GameClient/GameText.h"
  44. #include "GameClient/GameWindowManager.h"
  45. #include "GameClient/GameWindowTransitions.h"
  46. #include "GameClient/KeyDefs.h"
  47. #include "GameClient/MessageBox.h"
  48. #include "GameClient/Shell.h"
  49. #include "GameClient/WindowLayout.h"
  50. #include "GameClient/WindowVideoManager.h"
  51. #include "GameLogic/GameLogic.h"
  52. #include "GameLogic/ScriptEngine.h"
  53. #ifdef _INTERNAL
  54. // for occasional debugging...
  55. //#pragma optimize("", off)
  56. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  57. #endif
  58. SkirmishGameInfo *TheChallengeGameInfo = NULL;
  59. // defines
  60. static const Int DEFAULT_GENERAL = 0;
  61. static const Int TELETYPE_SKIP = 2;
  62. // window ids ------------------------------------------------------------------------------
  63. static NameKeyType parentID = NAMEKEY_INVALID;
  64. static NameKeyType buttonPlayID = NAMEKEY_INVALID;
  65. static NameKeyType buttonBackID = NAMEKEY_INVALID;
  66. static NameKeyType bioPortraitID = NAMEKEY_INVALID;
  67. static NameKeyType bioNameEntryID = NAMEKEY_INVALID;
  68. static NameKeyType bioDOBEntryID = NAMEKEY_INVALID;
  69. static NameKeyType bioBirthplaceEntryID = NAMEKEY_INVALID;
  70. static NameKeyType bioStrategyEntryID = NAMEKEY_INVALID;
  71. static NameKeyType buttonGeneralPositionID[NUM_GENERALS] = {NAMEKEY_INVALID};
  72. static NameKeyType backdropID = NAMEKEY_INVALID;
  73. static NameKeyType bioParentID = NAMEKEY_INVALID;
  74. // window pointers --------------------------------------------------------------------------------
  75. static GameWindow *parentMenu = NULL;
  76. static GameWindow *buttonPlay = NULL;
  77. static GameWindow *buttonBack = NULL;
  78. static GameWindow *bioPortrait = NULL;
  79. static GameWindow *bioLine1Entry = NULL;
  80. static GameWindow *bioLine2Entry = NULL;
  81. static GameWindow *bioLine3Entry = NULL;
  82. static GameWindow *bioLine4Entry = NULL;
  83. static GameWindow *buttonGeneralPosition[NUM_GENERALS] = {NULL};
  84. static GameWindow *backdrop = NULL;
  85. static GameWindow *bioParent = NULL;
  86. //static NameKeyType testWinID = NAMEKEY_INVALID;
  87. //static GameWindow *testWin = NULL;
  88. static WindowVideoManager *wndVideoManager = NULL;
  89. //
  90. static Int initialGadgetDelay = 2;
  91. static Bool justEntered = FALSE;
  92. static Bool isShuttingDown = FALSE;
  93. static Int lastButtonIndex = -1;
  94. Int lastHilitedIndex = -1;
  95. Bool isAutoSelecting = FALSE;
  96. // for use by the teletype style bio text display
  97. UnicodeString bioLine1;
  98. UnicodeString bioLine2;
  99. UnicodeString bioLine3;
  100. UnicodeString bioLine4;
  101. UnicodeString bioLine1Readout;
  102. UnicodeString bioLine2Readout;
  103. UnicodeString bioLine3Readout;
  104. UnicodeString bioLine4Readout;
  105. Int bioTextPosition = 0;
  106. Int bioTotalLength = 0;
  107. // for use by the intro animation
  108. static Int buttonSequenceStep = 0;
  109. // audio
  110. AudioHandle lastSelectionSound = NULL;
  111. AudioHandle lastPreviewSound = NULL;
  112. static Int introAudioMagicNumber = 0;
  113. static Bool hasPlayedIntroAudio = FALSE;
  114. //-------------------------------------------------------------------------------------------------
  115. // returns the index of the General Position button selected, or -1 if not found
  116. //-------------------------------------------------------------------------------------------------
  117. Int findPositionButton( Int controlID )
  118. {
  119. for (Int i = 0; i < NUM_GENERALS; i++)
  120. {
  121. if (controlID == buttonGeneralPositionID[i])
  122. return i;
  123. }
  124. return -1;
  125. }
  126. //-------------------------------------------------------------------------------------------------
  127. // enable the appropriate buttons, make sure they aren't hidden, and set the correct images
  128. //-------------------------------------------------------------------------------------------------
  129. void setEnabledButtons()
  130. {
  131. for (Int i = 0; i < NUM_GENERALS; i++)
  132. {
  133. const GeneralPersona* generals = TheChallengeGenerals->getChallengeGenerals();
  134. buttonGeneralPosition[i]->winEnable(generals[i].isStartingEnabled());
  135. buttonGeneralPosition[i]->winHide(! generals[i].isStartingEnabled());
  136. Int templateNum = ThePlayerTemplateStore->getTemplateNumByName(generals[i].getPlayerTemplateName());
  137. const PlayerTemplate *playerTemplate = ThePlayerTemplateStore->getNthPlayerTemplate(templateNum);
  138. if (playerTemplate)
  139. {
  140. const Image *enabledImage = TheMappedImageCollection->findImageByName( playerTemplate->getMedallionNormal() );
  141. GadgetCheckBoxSetEnabledImage( buttonGeneralPosition[i], enabledImage );
  142. if (enabledImage)
  143. // image size keeps changing, so it'll drive the window size directly
  144. buttonGeneralPosition[i]->winSetSize( enabledImage->getImageWidth(), enabledImage->getImageWidth() );
  145. const Image *selectedImage = TheMappedImageCollection->findImageByName( playerTemplate->getMedallionSelected() );
  146. GadgetCheckBoxSetHiliteUncheckedBoxImage( buttonGeneralPosition[i], selectedImage);
  147. GadgetCheckBoxSetDisabledUncheckedBoxImage( buttonGeneralPosition[i], selectedImage);
  148. const Image *hilitedImage = TheMappedImageCollection->findImageByName( playerTemplate->getMedallionHilite() );
  149. GadgetCheckBoxSetHiliteImage( buttonGeneralPosition[i], hilitedImage);
  150. }
  151. }
  152. }
  153. //-------------------------------------------------------------------------------------------------
  154. // sets the appropriate campaign for the chosen general
  155. //-------------------------------------------------------------------------------------------------
  156. void setGeneralCampaign( Int buttonIndex )
  157. {
  158. if (buttonIndex < 0 || buttonIndex >= NUM_GENERALS)
  159. return;
  160. // determine which general and player template is selected and store it
  161. const GeneralPersona* generals = TheChallengeGenerals->getChallengeGenerals();
  162. TheCampaignManager->setCampaign(generals[buttonIndex].getCampaign());
  163. Int templateNum = ThePlayerTemplateStore->getTemplateNumByName(generals[buttonIndex].getPlayerTemplateName());
  164. TheChallengeGenerals->setCurrentPlayerTemplateNum(templateNum);
  165. // set up the skirmish games single player slot
  166. GameSlot slot;
  167. const PlayerTemplate *playerTemplate = ThePlayerTemplateStore->getNthPlayerTemplate(templateNum);
  168. slot.setState(SLOT_PLAYER, playerTemplate->getDisplayName());
  169. slot.setPlayerTemplate(templateNum);
  170. TheChallengeGameInfo->setSlot(0, slot);
  171. }
  172. //-------------------------------------------------------------------------------------------------
  173. // set the appropriate bio for the given general and initialize the bio windows
  174. //-------------------------------------------------------------------------------------------------
  175. void setGeneralBio( Int buttonIndex )
  176. {
  177. if (buttonIndex < 0 || buttonIndex >= NUM_GENERALS)
  178. return;
  179. // this is hidden until the a bio is set
  180. // @todo: use a fancy transition
  181. bioParent->winHide(FALSE);
  182. const GeneralPersona* generals = TheChallengeGenerals->getChallengeGenerals();
  183. const Image *image = generals[buttonIndex].getBioPortraitSmall();
  184. bioPortrait->winSetEnabledImage( 0, image );
  185. bioPortrait->winSetStatus( WIN_STATUS_IMAGE );
  186. bioTextPosition = 0;
  187. bioLine1 = TheGameText->fetch(generals[buttonIndex].getBioName());
  188. bioLine2 = TheGameText->fetch(generals[buttonIndex].getBioRank());
  189. bioLine3 = TheGameText->fetch(generals[buttonIndex].getBioBranch());
  190. bioLine4 = TheGameText->fetch(generals[buttonIndex].getBioStrategy());
  191. bioTotalLength = bioLine1.getLength() + bioLine2.getLength() + bioLine3.getLength() + bioLine4.getLength();
  192. // clear the bio readout text, because updateBio likes it that way
  193. GadgetStaticTextSetText(bioLine1Entry, UnicodeString::TheEmptyString);
  194. GadgetStaticTextSetText(bioLine2Entry, UnicodeString::TheEmptyString);
  195. GadgetStaticTextSetText(bioLine3Entry, UnicodeString::TheEmptyString);
  196. GadgetStaticTextSetText(bioLine4Entry, UnicodeString::TheEmptyString);
  197. }
  198. //-------------------------------------------------------------------------------------------------
  199. // update the intro button sequence UNFINISHED
  200. //-------------------------------------------------------------------------------------------------
  201. void updateButtonSequence(Int stepsPerUpdate)
  202. {
  203. const static Int cleanupStates = 2;
  204. if (buttonSequenceStep > NUM_GENERALS + cleanupStates)
  205. return;
  206. const GeneralPersona* generals = TheChallengeGenerals->getChallengeGenerals();
  207. for (Int i = 0; i < stepsPerUpdate; i++)
  208. {
  209. // selected look
  210. Int pos = buttonSequenceStep;
  211. if (pos < NUM_GENERALS && !buttonGeneralPosition[pos]->winIsHidden())
  212. {
  213. Int templateNum = ThePlayerTemplateStore->getTemplateNumByName(generals[pos].getPlayerTemplateName());
  214. const PlayerTemplate *playerTemplate = ThePlayerTemplateStore->getNthPlayerTemplate(templateNum);
  215. if (playerTemplate)
  216. GadgetCheckBoxSetEnabledImage( buttonGeneralPosition[pos], TheMappedImageCollection->findImageByName( playerTemplate->getMedallionSelected() ) );
  217. }
  218. // mouseover look
  219. if (--pos > 0 && pos < NUM_GENERALS && !buttonGeneralPosition[pos]->winIsHidden())
  220. {
  221. Int templateNum = ThePlayerTemplateStore->getTemplateNumByName(generals[pos].getPlayerTemplateName());
  222. const PlayerTemplate *playerTemplate = ThePlayerTemplateStore->getNthPlayerTemplate(templateNum);
  223. if (playerTemplate)
  224. GadgetCheckBoxSetEnabledImage( buttonGeneralPosition[pos], TheMappedImageCollection->findImageByName( playerTemplate->getMedallionHilite() ) );
  225. }
  226. // regular look
  227. if (--pos > 0 && pos < NUM_GENERALS && !buttonGeneralPosition[pos]->winIsHidden())
  228. {
  229. Int templateNum = ThePlayerTemplateStore->getTemplateNumByName(generals[pos].getPlayerTemplateName());
  230. const PlayerTemplate *playerTemplate = ThePlayerTemplateStore->getNthPlayerTemplate(templateNum);
  231. if (playerTemplate)
  232. GadgetCheckBoxSetEnabledImage( buttonGeneralPosition[pos], TheMappedImageCollection->findImageByName( playerTemplate->getMedallionNormal() ) );
  233. }
  234. buttonSequenceStep++;
  235. }
  236. }
  237. //-------------------------------------------------------------------------------------------------
  238. // update the general's bio, teletype style, call this as often as you want frames advanced
  239. // NOTE: static text windows must be initialized to empty before calling
  240. // accepts the number of frames to advance
  241. // returns TRUE if an update occurred (the update was necessary)
  242. //-------------------------------------------------------------------------------------------------
  243. Bool updateBio(Int frames)
  244. {
  245. Bool ret = FALSE;
  246. for (Int i = 0; i < frames; i++)
  247. {
  248. if (bioTextPosition < bioTotalLength)
  249. {
  250. UnicodeString text;
  251. WideChar wChar;
  252. GameWindow *window;
  253. if (bioTextPosition < bioLine1.getLength())
  254. {
  255. text = GadgetStaticTextGetText(bioLine1Entry);
  256. wChar = bioLine1.getCharAt(bioTextPosition);
  257. window = bioLine1Entry;
  258. }
  259. else if (bioTextPosition < bioLine1.getLength() + bioLine2.getLength())
  260. {
  261. text = GadgetStaticTextGetText(bioLine2Entry);
  262. wChar = bioLine2.getCharAt(bioTextPosition - bioLine1.getLength());
  263. window = bioLine2Entry;
  264. }
  265. else if (bioTextPosition < bioLine1.getLength() + bioLine2.getLength() + bioLine3.getLength())
  266. {
  267. text = GadgetStaticTextGetText(bioLine3Entry);
  268. wChar = bioLine3.getCharAt(bioTextPosition - bioLine1.getLength() - bioLine2.getLength());
  269. window = bioLine3Entry;
  270. }
  271. else
  272. {
  273. text = GadgetStaticTextGetText(bioLine4Entry);
  274. wChar = bioLine4.getCharAt(bioTextPosition - bioLine1.getLength() - bioLine2.getLength() - bioLine3.getLength());
  275. window = bioLine4Entry;
  276. }
  277. text.concat(wChar);
  278. GadgetStaticTextSetText(window, text);
  279. bioTextPosition++;
  280. ret = TRUE;
  281. }
  282. }
  283. return ret;
  284. }
  285. //-------------------------------------------------------------------------------------------------
  286. /** init the challenge mode menu */
  287. //-------------------------------------------------------------------------------------------------
  288. void ChallengeMenuInit( WindowLayout *layout, void *userData )
  289. {
  290. if( !TheChallengeGameInfo )
  291. TheChallengeGameInfo = NEW SkirmishGameInfo;
  292. TheChallengeGameInfo->init();
  293. TheChallengeGameInfo->clearSlotList();
  294. TheChallengeGameInfo->reset();
  295. TheChallengeGameInfo->enterGame();
  296. TheShell->showShellMap(TRUE);
  297. // init window ids and pointers
  298. parentID = TheNameKeyGenerator->nameToKey( AsciiString("ChallengeMenu.wnd:ParentChallengeMenu") );
  299. parentMenu = TheWindowManager->winGetWindowFromId( NULL, parentID );
  300. buttonPlayID = TheNameKeyGenerator->nameToKey( AsciiString("ChallengeMenu.wnd:ButtonPlay") );
  301. buttonPlay = TheWindowManager->winGetWindowFromId( parentMenu, buttonPlayID );
  302. buttonBackID = TheNameKeyGenerator->nameToKey( AsciiString("ChallengeMenu.wnd:ButtonBack") );
  303. buttonBack = TheWindowManager->winGetWindowFromId( parentMenu, buttonBackID );
  304. bioPortraitID = TheNameKeyGenerator->nameToKey( AsciiString("ChallengeMenu.wnd:BioPortrait") );
  305. bioPortrait = TheWindowManager->winGetWindowFromId( parentMenu, bioPortraitID );
  306. bioNameEntryID = TheNameKeyGenerator->nameToKey( AsciiString("ChallengeMenu.wnd:BioNameEntry") );
  307. bioLine1Entry = TheWindowManager->winGetWindowFromId( parentMenu, bioNameEntryID ); // this window has been repurposed
  308. bioDOBEntryID = TheNameKeyGenerator->nameToKey( AsciiString("ChallengeMenu.wnd:BioDOBEntry") );
  309. bioLine2Entry = TheWindowManager->winGetWindowFromId( parentMenu, bioDOBEntryID ); // this window has been repurposed
  310. bioBirthplaceEntryID = TheNameKeyGenerator->nameToKey( AsciiString("ChallengeMenu.wnd:BioBirthplaceEntry") );
  311. bioLine3Entry = TheWindowManager->winGetWindowFromId( parentMenu, bioBirthplaceEntryID ); // this window has been repurposed
  312. bioStrategyEntryID = TheNameKeyGenerator->nameToKey( AsciiString("ChallengeMenu.wnd:BioStrategyEntry") );
  313. bioLine4Entry = TheWindowManager->winGetWindowFromId( parentMenu, bioStrategyEntryID ); // this window has been repurposed
  314. backdropID = TheNameKeyGenerator->nameToKey( AsciiString("ChallengeMenu.wnd:MainBackdrop") );
  315. backdrop = TheWindowManager->winGetWindowFromId( parentMenu, backdropID);
  316. bioParentID = TheNameKeyGenerator->nameToKey( AsciiString("ChallengeMenu.wnd:GeneralsBioParent") );
  317. bioParent = TheWindowManager->winGetWindowFromId( parentMenu, bioParentID);
  318. AsciiString strButtonName;
  319. for (Int i = 0; i < NUM_GENERALS; i++)
  320. {
  321. strButtonName.format("ChallengeMenu.wnd:GeneralPosition%d", i);
  322. buttonGeneralPositionID[i] = TheNameKeyGenerator->nameToKey( strButtonName );
  323. buttonGeneralPosition[i] = TheWindowManager->winGetWindowFromId( parentMenu, buttonGeneralPositionID[i] );
  324. DEBUG_ASSERTCRASH(buttonGeneralPosition[i], ("Could not find the ButtonGeneralPosition[%d]",i ));
  325. // start all buttons hidden, then expose them later if there is a general for this spot
  326. buttonGeneralPosition[i]->winHide( TRUE );
  327. }
  328. // set defaults
  329. bioParent->winHide(TRUE);
  330. buttonPlay->winHide(TRUE);
  331. isAutoSelecting = FALSE;
  332. buttonSequenceStep = 0;
  333. setEnabledButtons();
  334. // show menu
  335. layout->hide( FALSE );
  336. // set keyboard focus to main parent
  337. TheWindowManager->winSetFocus( parentMenu );
  338. justEntered = TRUE;
  339. initialGadgetDelay = 2;
  340. GameWindow *winGadgetParent = TheWindowManager->winGetWindowFromId(NULL, TheNameKeyGenerator->nameToKey("ChallengeMenu.wnd:GadgetParent"));
  341. if(winGadgetParent)
  342. winGadgetParent->winHide(TRUE);
  343. isShuttingDown = FALSE;
  344. if(!wndVideoManager)
  345. wndVideoManager = NEW WindowVideoManager;
  346. wndVideoManager->init();
  347. lastSelectionSound = NULL;
  348. lastPreviewSound = NULL;
  349. hasPlayedIntroAudio = FALSE;
  350. }
  351. //-------------------------------------------------------------------------------------------------
  352. /** update the challenge mode menu */
  353. //-------------------------------------------------------------------------------------------------
  354. void ChallengeMenuUpdate( WindowLayout *layout, void *userData )
  355. {
  356. if(justEntered)
  357. {
  358. if(initialGadgetDelay == 1)
  359. {
  360. TheTransitionHandler->setGroup("ChallengeMenuFade");
  361. // TheTransitionHandler->setGroup("ChallengeButtonsIntro");
  362. initialGadgetDelay = 2;
  363. justEntered = FALSE;
  364. }
  365. else
  366. initialGadgetDelay--;
  367. }
  368. // if (TheTransitionHandler->isFinished())
  369. // updateButtonSequence( 1 );
  370. // delay the voice for N updates after the transition is done
  371. if (!hasPlayedIntroAudio && TheTransitionHandler->isFinished())
  372. {
  373. introAudioMagicNumber++;
  374. if (introAudioMagicNumber == 10)
  375. {
  376. // "Choose your general."
  377. AudioEventRTS event( "Taunts_GCAnnouncer01" );
  378. TheAudio->addAudioEvent( &event );
  379. hasPlayedIntroAudio = TRUE;
  380. }
  381. }
  382. updateBio( TELETYPE_SKIP );
  383. if(isShuttingDown && TheShell->isAnimFinished() && TheTransitionHandler->isFinished())
  384. {
  385. TheShell->shutdownComplete( layout );
  386. }
  387. if(wndVideoManager)
  388. wndVideoManager->update();
  389. }
  390. //-------------------------------------------------------------------------------------------------
  391. /** shutdown the challenge mode menu */
  392. //-------------------------------------------------------------------------------------------------
  393. void ChallengeMenuShutdown( WindowLayout *layout, void *userData )
  394. {
  395. if(wndVideoManager)
  396. delete wndVideoManager;
  397. wndVideoManager = NULL;
  398. lastButtonIndex = -1;
  399. buttonSequenceStep = 0;
  400. Bool popImmediate = *(Bool *)userData;
  401. if( popImmediate )
  402. {
  403. layout->hide( TRUE );
  404. TheShell->shutdownComplete( layout );
  405. return;
  406. }
  407. TheTransitionHandler->reverse("ChallengeMenuFade");
  408. isShuttingDown = TRUE;
  409. if(TheChallengeGameInfo)
  410. delete TheChallengeGameInfo;
  411. TheChallengeGameInfo = NULL;
  412. TheAudio->removeAudioEvent( lastSelectionSound );
  413. TheAudio->removeAudioEvent( lastPreviewSound );
  414. lastSelectionSound = NULL;
  415. lastPreviewSound = NULL;
  416. introAudioMagicNumber = 0;
  417. }
  418. //-------------------------------------------------------------------------------------------------
  419. /** challenge mode menu input callback */
  420. //-------------------------------------------------------------------------------------------------
  421. WindowMsgHandledType ChallengeMenuInput( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 )
  422. {
  423. switch( msg )
  424. {
  425. // --------------------------------------------------------------------------------------------
  426. case GWM_CHAR:
  427. {
  428. UnsignedByte key = mData1;
  429. UnsignedByte state = mData2;
  430. switch( key )
  431. {
  432. // ----------------------------------------------------------------------------------------
  433. case KEY_ESC:
  434. {
  435. //
  436. // send a simulated selected event to the parent window of the
  437. // back/exit button
  438. //
  439. if( BitTest( state, KEY_STATE_UP ) )
  440. {
  441. TheWindowManager->winSendSystemMsg( window, GBM_SELECTED, (WindowMsgData)buttonBack, buttonBackID );
  442. } // end if
  443. // don't let key fall through anywhere else
  444. return MSG_HANDLED;
  445. } // end escape
  446. } // end switch( key )
  447. } // end char
  448. } // end switch( msg )
  449. return MSG_IGNORED;
  450. }
  451. //-------------------------------------------------------------------------------------------------
  452. /** challenge mode menu window system callback */
  453. //-------------------------------------------------------------------------------------------------
  454. WindowMsgHandledType ChallengeMenuSystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 )
  455. {
  456. switch( msg )
  457. {
  458. case GWM_CREATE: break;
  459. case GWM_DESTROY: break;
  460. case GWM_INPUT_FOCUS:
  461. {
  462. // if we're givin the opportunity to take the keyboard focus we must say we want it
  463. if( mData1 == TRUE )
  464. *(Bool *)mData2 = TRUE;
  465. return MSG_HANDLED;
  466. }
  467. case GBM_MOUSE_ENTERING:
  468. {
  469. // preview the bio for this position
  470. GameWindow *control = (GameWindow *)mData1;
  471. Int controlID = control->winGetWindowId();
  472. Int buttonIndex = findPositionButton(controlID);
  473. if( buttonIndex != -1 && buttonIndex != lastButtonIndex )
  474. {
  475. setGeneralBio(buttonIndex);
  476. // special sound for Harvard
  477. AudioEventRTS event( "GUILogoMouseOver" );
  478. TheAudio->addAudioEvent( &event );
  479. lastHilitedIndex = buttonIndex;
  480. }
  481. break;
  482. }
  483. case GBM_MOUSE_LEAVING:
  484. {
  485. // set the bio back to the selected one
  486. GameWindow *control = (GameWindow *)mData1;
  487. Int controlID = control->winGetWindowId();
  488. Int buttonIndex = findPositionButton(controlID);
  489. if( buttonIndex != -1 && buttonIndex != lastButtonIndex )
  490. {
  491. if (lastButtonIndex == -1)
  492. {
  493. // they're just browsing and haven't made a selection yet
  494. // @todo: make this a nice transition
  495. // bioParent->winHide(TRUE);
  496. // buttonPlay->winHide(TRUE);
  497. }
  498. setGeneralBio(lastButtonIndex);
  499. }
  500. break;
  501. }
  502. case GBM_SELECTED:
  503. {
  504. UnicodeString filename;
  505. GameWindow *control = (GameWindow *)mData1;
  506. Int controlID = control->winGetWindowId();
  507. // we don't need to handle these
  508. if ( isAutoSelecting )
  509. {
  510. isAutoSelecting = FALSE;
  511. break;
  512. }
  513. Int buttonIndex = findPositionButton(controlID);
  514. if( buttonIndex != -1)
  515. {
  516. // partial radio button behavior (I'm not using actual radio buttons
  517. // because it limits options on how the interface can work, which is a
  518. // bad thing when the design is constantly in flux.)
  519. // ...basically this just makes you have exactly one button selected
  520. // once the first choice has been made.
  521. if (lastButtonIndex != -1)
  522. {
  523. isAutoSelecting = TRUE;
  524. GameWindow *lastControl = TheWindowManager->winGetWindowFromId( NULL, buttonGeneralPositionID[lastButtonIndex]);
  525. GadgetCheckBoxToggle(lastControl);
  526. }
  527. // play audio to indicate selection
  528. TheAudio->removeAudioEvent(lastSelectionSound);
  529. TheAudio->removeAudioEvent(lastPreviewSound);
  530. const GeneralPersona* generals = TheChallengeGenerals->getChallengeGenerals();
  531. AudioEventRTS event( generals[buttonIndex].getPreviewSound() );
  532. lastPreviewSound = TheAudio->addAudioEvent( &event );
  533. /*
  534. // play audio to indicate selection
  535. TheAudio->removeAudioEvent(lastSelectionSound);
  536. TheAudio->removeAudioEvent(lastPreviewSound);
  537. const GeneralPersona* generals = TheChallengeGenerals->getChallengeGenerals();
  538. AudioEventRTS event( generals[buttonIndex].getSelectionSound() );
  539. lastSelectionSound = TheAudio->addAudioEvent( &event );
  540. */
  541. lastButtonIndex = buttonIndex;
  542. buttonPlay->winHide(FALSE);
  543. //@todo: outro transitions
  544. }
  545. else if( controlID == buttonPlayID )
  546. {
  547. if( TheChallengeGameInfo == NULL )
  548. {
  549. // If this is NULL, then we must be on the way back out of this menu.
  550. // Don't crash, just eat the button click message.
  551. return MSG_HANDLED;
  552. }
  553. setGeneralCampaign(lastButtonIndex);
  554. TheWritableGlobalData->m_pendingFile = TheCampaignManager->getCurrentMap();
  555. TheChallengeGameInfo->setMap(TheCampaignManager->getCurrentMap());
  556. // turn off the last button so the screen will be pristine when the user returns
  557. isAutoSelecting = TRUE;
  558. GameWindow *lastControl = TheWindowManager->winGetWindowFromId( NULL, buttonGeneralPositionID[lastButtonIndex]);
  559. GadgetCheckBoxSetChecked(lastControl, FALSE);
  560. lastButtonIndex = -1;
  561. // introAudioHasPlayed = FALSE;
  562. buttonSequenceStep = 0;
  563. if (TheGameLogic->isInGame())
  564. TheGameLogic->clearGameData();
  565. // If the campaign has been reset, so has the campaign difficulty. Restore it, just in case.
  566. DEBUG_ASSERTCRASH(TheChallengeGenerals, ("TheChallengeGenerals are not initialized."));
  567. if (TheChallengeGenerals)
  568. {
  569. TheCampaignManager->setGameDifficulty(TheChallengeGenerals->getCurrentDifficulty());
  570. TheScriptEngine->setGlobalDifficulty(TheChallengeGenerals->getCurrentDifficulty());
  571. }
  572. // put a request in the message stream for a new game
  573. GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_NEW_GAME );
  574. msg->appendIntegerArgument(GAME_SINGLE_PLAYER);
  575. msg->appendIntegerArgument(TheCampaignManager->getGameDifficulty());
  576. msg->appendIntegerArgument(TheCampaignManager->getRankPoints());
  577. // Added so that, even though a ChallengeGame is really a SkirmishGame in SinglePlayerGame's clothing,
  578. // GameEngine will still apply the default "FRAME CAP" as it does during "Solo Missions."
  579. msg->appendIntegerArgument(LOGICFRAMES_PER_SECOND); // FPS limit
  580. InitRandom(0);
  581. }
  582. else if( controlID == buttonBackID )
  583. {
  584. TheShell->pop();
  585. }
  586. break;
  587. }
  588. default: return MSG_IGNORED;
  589. }
  590. return MSG_HANDLED;
  591. }