WOLQuickMatchMenu.cpp 63 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897
  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: WOLQuickMatchMenu.cpp
  25. // Author: Chris Huybregts, November 2001
  26. // Description: Lan Lobby Menu
  27. ///////////////////////////////////////////////////////////////////////////////////////
  28. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  29. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  30. #include "Common/GameEngine.h"
  31. #include "Common/QuickmatchPreferences.h"
  32. #include "Common/LadderPreferences.h"
  33. #include "Common/MultiplayerSettings.h"
  34. #include "Common/PlayerTemplate.h"
  35. #include "GameClient/AnimateWindowManager.h"
  36. #include "GameClient/WindowLayout.h"
  37. #include "GameClient/Gadget.h"
  38. #include "GameClient/GameText.h"
  39. #include "GameClient/InGameUI.h"
  40. #include "GameClient/Shell.h"
  41. #include "GameClient/ShellHooks.h"
  42. #include "GameClient/KeyDefs.h"
  43. #include "GameClient/GameWindowManager.h"
  44. #include "GameClient/GadgetComboBox.h"
  45. #include "GameClient/GadgetPushButton.h"
  46. #include "GameClient/GadgetListBox.h"
  47. #include "GameClient/GadgetTextEntry.h"
  48. #include "GameClient/GadgetStaticText.h"
  49. #include "GameClient/MapUtil.h"
  50. #include "GameClient/GameWindowTransitions.h"
  51. #include "GameClient/ChallengeGenerals.h"
  52. #include "GameLogic/GameLogic.h"
  53. #include "GameNetwork/NAT.h"
  54. #include "GameNetwork/GameSpyOverlay.h"
  55. #include "GameNetwork/GameSpy/BuddyDefs.h"
  56. #include "GameNetwork/GameSpy/GSConfig.h"
  57. #include "GameNetwork/GameSpy/PeerDefs.h"
  58. #include "GameNetwork/GameSpy/PeerThread.h"
  59. #include "GameNetwork/GameSpy/PersistentStorageDefs.h"
  60. #include "GameNetwork/GameSpy/PersistentStorageThread.h"
  61. #include "GameNetwork/RankPointValue.h"
  62. #include "GameNetwork/GameSpy/LadderDefs.h"
  63. #ifdef _INTERNAL
  64. // for occasional debugging...
  65. //#pragma optimize("", off)
  66. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  67. #endif
  68. #ifdef DEBUG_LOGGING
  69. #include "Common/MiniLog.h"
  70. //#define PERF_TEST
  71. static LogClass s_perfLog("QMPerf.txt");
  72. static Bool s_inQM = FALSE;
  73. #define PERF_LOG(x) s_perfLog.log x
  74. #else // DEBUG_LOGGING
  75. #define PERF_LOG(x) {}
  76. #endif // DEBUG_LOGGING
  77. // PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
  78. // window ids ------------------------------------------------------------------------------
  79. static NameKeyType parentWOLQuickMatchID = NAMEKEY_INVALID;
  80. static NameKeyType buttonBackID = NAMEKEY_INVALID;
  81. static NameKeyType buttonStartID = NAMEKEY_INVALID;
  82. static NameKeyType buttonStopID = NAMEKEY_INVALID;
  83. static NameKeyType buttonWidenID = NAMEKEY_INVALID;
  84. static NameKeyType buttonBuddiesID = NAMEKEY_INVALID;
  85. static NameKeyType listboxQuickMatchID = NAMEKEY_INVALID;
  86. static NameKeyType listboxMapSelectID = NAMEKEY_INVALID;
  87. static NameKeyType buttonSelectAllMapsID = NAMEKEY_INVALID;
  88. static NameKeyType buttonSelectNoMapsID = NAMEKEY_INVALID;
  89. //static NameKeyType textEntryMaxDisconnectsID = NAMEKEY_INVALID;
  90. //static NameKeyType textEntryMaxPointsID = NAMEKEY_INVALID;
  91. //static NameKeyType textEntryMinPointsID = NAMEKEY_INVALID;
  92. static NameKeyType textEntryWaitTimeID = NAMEKEY_INVALID;
  93. static NameKeyType comboBoxNumPlayersID = NAMEKEY_INVALID;
  94. static NameKeyType comboBoxMaxPingID = NAMEKEY_INVALID;
  95. static NameKeyType comboBoxLadderID = NAMEKEY_INVALID;
  96. static NameKeyType comboBoxMaxDisconnectsID = NAMEKEY_INVALID;
  97. static NameKeyType staticTextNumPlayersID = NAMEKEY_INVALID;
  98. static NameKeyType comboBoxSideID = NAMEKEY_INVALID;
  99. static NameKeyType comboBoxColorID = NAMEKEY_INVALID;
  100. // Window Pointers ------------------------------------------------------------------------
  101. static GameWindow *parentWOLQuickMatch = NULL;
  102. static GameWindow *buttonBack = NULL;
  103. static GameWindow *buttonStart = NULL;
  104. static GameWindow *buttonStop = NULL;
  105. static GameWindow *buttonWiden = NULL;
  106. GameWindow *quickmatchTextWindow = NULL;
  107. static GameWindow *listboxMapSelect = NULL;
  108. //static GameWindow *textEntryMaxDisconnects = NULL;
  109. //static GameWindow *textEntryMaxPoints = NULL;
  110. //static GameWindow *textEntryMinPoints = NULL;
  111. static GameWindow *textEntryWaitTime = NULL;
  112. static GameWindow *comboBoxNumPlayers = NULL;
  113. static GameWindow *comboBoxMaxPing = NULL;
  114. static GameWindow *comboBoxLadder = NULL;
  115. static GameWindow *comboBoxDisabledLadder = NULL; // enable and disable this, but never use it. it is a stand-in for comboBoxLadder for when there are no ladders
  116. static GameWindow *comboBoxMaxDisconnects = NULL;
  117. static GameWindow *staticTextNumPlayers = NULL;
  118. static GameWindow *comboBoxSide = NULL;
  119. static GameWindow *comboBoxColor = NULL;
  120. static Bool isShuttingDown = false;
  121. static Bool buttonPushed = false;
  122. static char *nextScreen = NULL;
  123. static Bool raiseMessageBoxes = false;
  124. static Bool isInInit = FALSE;
  125. static const Image *selectedImage = NULL;
  126. static const Image *unselectedImage = NULL;
  127. static bool isPopulatingLadderBox = false;
  128. static Int maxPingEntries = 0;
  129. static Int maxPoints= 100;
  130. static Int minPoints = 0;
  131. static const LadderInfo * getLadderInfo( void );
  132. static Bool isInfoShown(void)
  133. {
  134. static NameKeyType parentStatsID = NAMEKEY("WOLQuickMatchMenu.wnd:ParentStats");
  135. GameWindow *parentStats = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, parentStatsID );
  136. if (parentStats)
  137. return !parentStats->winIsHidden();
  138. return FALSE;
  139. }
  140. static void hideInfoGadgets(Bool doIt)
  141. {
  142. static NameKeyType parentStatsID = NAMEKEY("WOLQuickMatchMenu.wnd:ParentStats");
  143. GameWindow *parentStats = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, parentStatsID );
  144. if (parentStats)
  145. {
  146. parentStats->winHide(doIt);
  147. }
  148. }
  149. static void hideOptionsGadgets(Bool doIt)
  150. {
  151. static NameKeyType parentOptionsID = NAMEKEY("WOLQuickMatchMenu.wnd:ParentOptions");
  152. GameWindow *parentOptions = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, parentOptionsID );
  153. if (parentOptions)
  154. {
  155. parentOptions->winHide(doIt);
  156. if (comboBoxSide)
  157. comboBoxSide->winHide(doIt);
  158. if (comboBoxColor)
  159. comboBoxColor->winHide(doIt);
  160. if (comboBoxNumPlayers)
  161. comboBoxNumPlayers->winHide(doIt);
  162. if (comboBoxLadder)
  163. comboBoxLadder->winHide(doIt);
  164. if (comboBoxDisabledLadder)
  165. comboBoxDisabledLadder->winHide(doIt);
  166. if (comboBoxMaxPing)
  167. comboBoxMaxPing->winHide(doIt);
  168. if (comboBoxMaxDisconnects)
  169. comboBoxMaxDisconnects->winHide(doIt);
  170. }
  171. }
  172. static void enableOptionsGadgets(Bool doIt)
  173. {
  174. #ifdef PERF_TEST
  175. s_inQM = !doIt;
  176. #endif // PERF_TEST
  177. static NameKeyType parentOptionsID = NAMEKEY("WOLQuickMatchMenu.wnd:ParentOptions");
  178. GameWindow *parentOptions = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, parentOptionsID );
  179. const LadderInfo *li = getLadderInfo();
  180. if (parentOptions)
  181. {
  182. parentOptions->winEnable(doIt);
  183. if (comboBoxSide)
  184. comboBoxSide->winEnable(doIt && (!li || !li->randomFactions));
  185. if (comboBoxColor)
  186. comboBoxColor->winEnable(doIt);
  187. if (comboBoxNumPlayers)
  188. comboBoxNumPlayers->winEnable(doIt);
  189. if (comboBoxLadder)
  190. comboBoxLadder->winEnable(doIt);
  191. if (comboBoxDisabledLadder)
  192. comboBoxDisabledLadder->winEnable(FALSE);
  193. if (comboBoxMaxPing)
  194. comboBoxMaxPing->winEnable(doIt);
  195. if (comboBoxMaxDisconnects)
  196. comboBoxMaxDisconnects->winEnable(doIt);
  197. }
  198. }
  199. enum
  200. {
  201. MAX_DISCONNECTS_ANY = 0,
  202. MAX_DISCONNECTS_5 = 5,
  203. MAX_DISCONNECTS_10 = 10,
  204. MAX_DISCONNECTS_25 = 25,
  205. MAX_DISCONNECTS_50 = 50,
  206. };
  207. enum{ MAX_DISCONNECTS_COUNT = 5 };
  208. static Int MAX_DISCONNECTS[MAX_DISCONNECTS_COUNT] = {MAX_DISCONNECTS_ANY, MAX_DISCONNECTS_5,
  209. MAX_DISCONNECTS_10, MAX_DISCONNECTS_25,
  210. MAX_DISCONNECTS_50};
  211. void UpdateStartButton(void)
  212. {
  213. if (!comboBoxLadder || !buttonStart || !listboxMapSelect)
  214. return;
  215. Int index;
  216. Int selected;
  217. GadgetComboBoxGetSelectedPos( comboBoxLadder, &selected );
  218. index = (Int)GadgetComboBoxGetItemData( comboBoxLadder, selected );
  219. const LadderInfo *li = TheLadderList->findLadderByIndex( index );
  220. if (li)
  221. {
  222. buttonStart->winEnable(TRUE);
  223. return;
  224. }
  225. Int numMaps = GadgetListBoxGetNumEntries(listboxMapSelect);
  226. for ( Int i=0; i<numMaps; ++i )
  227. {
  228. if ((Bool)GadgetListBoxGetItemData(listboxMapSelect, i, 0))
  229. {
  230. buttonStart->winEnable(TRUE);
  231. return;
  232. }
  233. }
  234. buttonStart->winEnable(FALSE);
  235. }
  236. // -----------------------------------------------------------------------------
  237. static void populateQMColorComboBox(QuickMatchPreferences& pref)
  238. {
  239. Int numColors = TheMultiplayerSettings->getNumColors();
  240. UnicodeString colorName;
  241. GadgetComboBoxReset(comboBoxColor);
  242. MultiplayerColorDefinition *def = TheMultiplayerSettings->getColor(PLAYERTEMPLATE_RANDOM);
  243. Int newIndex = GadgetComboBoxAddEntry(comboBoxColor, TheGameText->fetch("GUI:???"), def->getColor());
  244. GadgetComboBoxSetItemData(comboBoxColor, newIndex, (void *)-1);
  245. for (Int c=0; c<numColors; ++c)
  246. {
  247. def = TheMultiplayerSettings->getColor(c);
  248. if (!def)
  249. continue;
  250. colorName = TheGameText->fetch(def->getTooltipName().str());
  251. newIndex = GadgetComboBoxAddEntry(comboBoxColor, colorName, def->getColor());
  252. GadgetComboBoxSetItemData(comboBoxColor, newIndex, (void *)c);
  253. }
  254. GadgetComboBoxSetSelectedPos(comboBoxColor, pref.getColor());
  255. }
  256. // -----------------------------------------------------------------------------
  257. static void populateQMSideComboBox(Int favSide, const LadderInfo *li = NULL)
  258. {
  259. Int numPlayerTemplates = ThePlayerTemplateStore->getPlayerTemplateCount();
  260. UnicodeString playerTemplateName;
  261. GadgetComboBoxReset(comboBoxSide);
  262. MultiplayerColorDefinition *def = TheMultiplayerSettings->getColor(PLAYERTEMPLATE_RANDOM);
  263. Int newIndex = GadgetComboBoxAddEntry(comboBoxSide, TheGameText->fetch("GUI:Random"), def->getColor());
  264. GadgetComboBoxSetItemData(comboBoxSide, newIndex, (void *)PLAYERTEMPLATE_RANDOM);
  265. std::set<AsciiString> seenSides;
  266. Int entryToSelect = 0; // select Random by default
  267. for (Int c=0; c<numPlayerTemplates; ++c)
  268. {
  269. const PlayerTemplate *fac = ThePlayerTemplateStore->getNthPlayerTemplate(c);
  270. if (!fac)
  271. continue;
  272. if (fac->getStartingBuilding().isEmpty())
  273. continue;
  274. AsciiString side;
  275. side.format("SIDE:%s", fac->getSide().str());
  276. if (seenSides.find(side) != seenSides.end())
  277. continue;
  278. if (li)
  279. {
  280. if (std::find(li->validFactions.begin(), li->validFactions.end(), fac->getSide()) == li->validFactions.end())
  281. continue; // ladder doesn't allow it.
  282. }
  283. // Remove disallowed generals from the choice list.
  284. // This is also enforced at GUI setup (GUIUtil.cpp and UserPreferences.cpp).
  285. // @todo: unlock these when something rad happens
  286. Bool disallowLockedGenerals = TRUE;
  287. const GeneralPersona *general = TheChallengeGenerals->getGeneralByTemplateName(fac->getName());
  288. Bool startsLocked = general ? !general->isStartingEnabled() : FALSE;
  289. if (disallowLockedGenerals && startsLocked)
  290. continue;
  291. seenSides.insert(side);
  292. newIndex = GadgetComboBoxAddEntry(comboBoxSide, TheGameText->fetch(side), def->getColor());
  293. GadgetComboBoxSetItemData(comboBoxSide, newIndex, (void *)c);
  294. if (c == favSide)
  295. entryToSelect = newIndex;
  296. }
  297. seenSides.clear();
  298. GadgetComboBoxSetSelectedPos(comboBoxSide, entryToSelect);
  299. if (li && li->randomFactions)
  300. comboBoxSide->winEnable(FALSE);
  301. else
  302. comboBoxSide->winEnable(TRUE);
  303. }
  304. void HandleQMLadderSelection(Int ladderID)
  305. {
  306. if (!parentWOLQuickMatch)
  307. return;
  308. QuickMatchPreferences pref;
  309. if (ladderID < 1)
  310. {
  311. pref.setLastLadder(AsciiString::TheEmptyString, 0);
  312. pref.write();
  313. return;
  314. }
  315. const LadderInfo *info = TheLadderList->findLadderByIndex(ladderID);
  316. if (!info)
  317. {
  318. pref.setLastLadder(AsciiString::TheEmptyString, 0);
  319. }
  320. else
  321. {
  322. pref.setLastLadder(info->address, info->port);
  323. }
  324. pref.write();
  325. }
  326. static inline Bool isValidLadder( const LadderInfo *lad )
  327. {
  328. if (lad && lad->index > 0 && lad->validQM)
  329. {
  330. PSPlayerStats stats = TheGameSpyPSMessageQueue->findPlayerStatsByID(TheGameSpyInfo->getLocalProfileID());
  331. Int numWins = 0;
  332. PerGeneralMap::iterator it;
  333. for (it = stats.wins.begin(); it != stats.wins.end(); ++it)
  334. {
  335. numWins += it->second;
  336. }
  337. if (!lad->maxWins || lad->maxWins >=numWins)
  338. {
  339. if (!lad->minWins || lad->minWins<=numWins)
  340. {
  341. return TRUE;
  342. }
  343. }
  344. }
  345. return FALSE;
  346. }
  347. void PopulateQMLadderListBox( GameWindow *win )
  348. {
  349. if (!parentWOLQuickMatch || !comboBoxLadder)
  350. return;
  351. isPopulatingLadderBox = true;
  352. QuickMatchPreferences pref;
  353. AsciiString userPrefFilename;
  354. Int localProfile = TheGameSpyInfo->getLocalProfileID();
  355. Color specialColor = GameSpyColor[GSCOLOR_MAP_SELECTED];
  356. Color normalColor = GameSpyColor[GSCOLOR_MAP_UNSELECTED];
  357. Color favoriteColor = GameSpyColor[GSCOLOR_MAP_UNSELECTED];
  358. Int index;
  359. GadgetListBoxReset( win );
  360. std::set<const LadderInfo *> usedLadders;
  361. // start with "No Ladder"
  362. index = GadgetListBoxAddEntryText( win, TheGameText->fetch("GUI:NoLadder"), normalColor, -1 );
  363. GadgetListBoxSetItemData( win, 0, index );
  364. // add the last ladder
  365. Int selectedPos = 0;
  366. AsciiString lastLadderAddr = pref.getLastLadderAddr();
  367. UnsignedShort lastLadderPort = pref.getLastLadderPort();
  368. const LadderInfo *info = TheLadderList->findLadder( lastLadderAddr, lastLadderPort );
  369. if (isValidLadder(info))
  370. {
  371. usedLadders.insert(info);
  372. index = GadgetListBoxAddEntryText( win, info->name, favoriteColor, -1 );
  373. GadgetListBoxSetItemData( win, (void *)(info->index), index );
  374. selectedPos = index;
  375. }
  376. // our recent ladders
  377. LadderPreferences ladPref;
  378. ladPref.loadProfile( localProfile );
  379. const LadderPrefMap recentLadders = ladPref.getRecentLadders();
  380. for (LadderPrefMap::const_iterator cit = recentLadders.begin(); cit != recentLadders.end(); ++cit)
  381. {
  382. AsciiString addr = cit->second.address;
  383. UnsignedShort port = cit->second.port;
  384. if (addr == lastLadderAddr && port == lastLadderPort)
  385. continue;
  386. const LadderInfo *info = TheLadderList->findLadder( addr, port );
  387. if (isValidLadder(info) && usedLadders.find(info) == usedLadders.end())
  388. {
  389. usedLadders.insert(info);
  390. index = GadgetListBoxAddEntryText( win, info->name, favoriteColor, -1 );
  391. GadgetListBoxSetItemData( win, (void *)(info->index), index );
  392. }
  393. }
  394. // special ladders
  395. const LadderInfoList *lil = TheLadderList->getSpecialLadders();
  396. LadderInfoList::const_iterator lit;
  397. for (lit = lil->begin(); lit != lil->end(); ++lit)
  398. {
  399. const LadderInfo *info = *lit;
  400. if (isValidLadder(info) && usedLadders.find(info) == usedLadders.end())
  401. {
  402. usedLadders.insert(info);
  403. index = GadgetListBoxAddEntryText( win, info->name, specialColor, -1 );
  404. GadgetListBoxSetItemData( win, (void *)(info->index), index );
  405. }
  406. }
  407. // standard ladders
  408. lil = TheLadderList->getStandardLadders();
  409. for (lit = lil->begin(); lit != lil->end(); ++lit)
  410. {
  411. const LadderInfo *info = *lit;
  412. if (isValidLadder(info) && usedLadders.find(info) == usedLadders.end())
  413. {
  414. usedLadders.insert(info);
  415. index = GadgetListBoxAddEntryText( win, info->name, normalColor, -1 );
  416. GadgetListBoxSetItemData( win, (void *)(info->index), index );
  417. }
  418. }
  419. GadgetListBoxSetSelected( win, selectedPos );
  420. isPopulatingLadderBox = false;
  421. }
  422. static const LadderInfo * getLadderInfo( void )
  423. {
  424. Int index;
  425. Int selected;
  426. GadgetComboBoxGetSelectedPos( comboBoxLadder, &selected );
  427. index = (Int)GadgetComboBoxGetItemData( comboBoxLadder, selected );
  428. const LadderInfo *li = TheLadderList->findLadderByIndex( index );
  429. return li;
  430. }
  431. void PopulateQMLadderComboBox( void )
  432. {
  433. if (!parentWOLQuickMatch || !comboBoxLadder)
  434. return;
  435. isPopulatingLadderBox = true;
  436. QuickMatchPreferences pref;
  437. Int localProfile = TheGameSpyInfo->getLocalProfileID();
  438. Color specialColor = GameSpyColor[GSCOLOR_MAP_SELECTED];
  439. Color normalColor = GameSpyColor[GSCOLOR_MAP_UNSELECTED];
  440. Int index;
  441. GadgetComboBoxReset( comboBoxLadder );
  442. index = GadgetComboBoxAddEntry( comboBoxLadder, TheGameText->fetch("GUI:NoLadder"), normalColor );
  443. GadgetComboBoxSetItemData( comboBoxLadder, index, 0 );
  444. std::set<const LadderInfo *> usedLadders;
  445. Int selectedPos = 0;
  446. AsciiString lastLadderAddr = pref.getLastLadderAddr();
  447. UnsignedShort lastLadderPort = pref.getLastLadderPort();
  448. const LadderInfo *info = TheLadderList->findLadder( lastLadderAddr, lastLadderPort );
  449. if (isValidLadder(info))
  450. {
  451. usedLadders.insert(info);
  452. index = GadgetComboBoxAddEntry( comboBoxLadder, info->name, specialColor );
  453. GadgetComboBoxSetItemData( comboBoxLadder, index, (void *)(info->index) );
  454. selectedPos = index;
  455. // we selected a ladder? No game size choice for us...
  456. GadgetComboBoxSetSelectedPos(comboBoxNumPlayers, info->playersPerTeam-1);
  457. comboBoxNumPlayers->winEnable( FALSE );
  458. }
  459. else
  460. {
  461. comboBoxNumPlayers->winEnable( TRUE );
  462. }
  463. LadderPreferences ladPref;
  464. ladPref.loadProfile( localProfile );
  465. const LadderPrefMap recentLadders = ladPref.getRecentLadders();
  466. for (LadderPrefMap::const_iterator cit = recentLadders.begin(); cit != recentLadders.end(); ++cit)
  467. {
  468. AsciiString addr = cit->second.address;
  469. UnsignedShort port = cit->second.port;
  470. if (addr == lastLadderAddr && port == lastLadderPort)
  471. continue;
  472. const LadderInfo *info = TheLadderList->findLadder( addr, port );
  473. if (isValidLadder(info) && usedLadders.find(info) == usedLadders.end())
  474. {
  475. usedLadders.insert(info);
  476. index = GadgetComboBoxAddEntry( comboBoxLadder, info->name, normalColor );
  477. GadgetComboBoxSetItemData( comboBoxLadder, index, (void *)(info->index) );
  478. }
  479. }
  480. index = GadgetComboBoxAddEntry( comboBoxLadder, TheGameText->fetch("GUI:ChooseLadder"), normalColor );
  481. GadgetComboBoxSetItemData( comboBoxLadder, index, (void *)-1 );
  482. GadgetComboBoxSetSelectedPos( comboBoxLadder, selectedPos );
  483. isPopulatingLadderBox = false;
  484. populateQMSideComboBox(pref.getSide(), getLadderInfo()); // this will set side to random and disable if necessary
  485. }
  486. static void populateQuickMatchMapSelectListbox( QuickMatchPreferences& pref )
  487. {
  488. std::list<AsciiString> maps = TheGameSpyConfig->getQMMaps();
  489. // enable/disable box based on ladder status
  490. Int index;
  491. Int selected;
  492. GadgetComboBoxGetSelectedPos( comboBoxLadder, &selected );
  493. index = (Int)GadgetComboBoxGetItemData( comboBoxLadder, selected );
  494. const LadderInfo *li = TheLadderList->findLadderByIndex( index );
  495. //listboxMapSelect->winEnable( li == NULL || li->randomMaps == FALSE );
  496. Int numPlayers = 0;
  497. if (li)
  498. {
  499. numPlayers = li->playersPerTeam*2;
  500. maps = li->validMaps;
  501. }
  502. else
  503. {
  504. GadgetComboBoxGetSelectedPos(comboBoxNumPlayers, &selected);
  505. if (selected < 0)
  506. selected = 0;
  507. numPlayers = (selected+1)*2;
  508. }
  509. GadgetListBoxReset(listboxMapSelect);
  510. for (std::list<AsciiString>::const_iterator it = maps.begin(); it != maps.end(); ++it)
  511. {
  512. AsciiString theMap = *it;
  513. const MapMetaData *md = TheMapCache->findMap(theMap);
  514. if (md && md->m_numPlayers >= numPlayers)
  515. {
  516. UnicodeString displayName;
  517. displayName = md->m_displayName;
  518. Bool isSelected = pref.isMapSelected(theMap);
  519. if (li && li->randomMaps)
  520. isSelected = TRUE;
  521. Int width = 10;
  522. Int height = 10;
  523. const Image *img = (isSelected)?selectedImage:unselectedImage;
  524. if ( img )
  525. {
  526. width = min(GadgetListBoxGetColumnWidth(listboxMapSelect, 0), img->getImageWidth());
  527. height = width;
  528. }
  529. Int index = GadgetListBoxAddEntryImage(listboxMapSelect, img, -1, 0, height, width);
  530. GadgetListBoxAddEntryText(listboxMapSelect, displayName, GameSpyColor[(isSelected)?GSCOLOR_MAP_SELECTED:GSCOLOR_MAP_UNSELECTED], index, 1);
  531. GadgetListBoxSetItemData(listboxMapSelect, (void *)isSelected, index);
  532. GadgetListBoxSetItemData(listboxMapSelect, (void *)md, index, 1);
  533. }
  534. }
  535. }
  536. static void saveQuickMatchOptions( void )
  537. {
  538. if(isInInit)
  539. return;
  540. QuickMatchPreferences pref;
  541. std::list<AsciiString> maps = TheGameSpyConfig->getQMMaps();
  542. Int index;
  543. Int selected;
  544. GadgetComboBoxGetSelectedPos( comboBoxLadder, &selected );
  545. index = (Int)GadgetComboBoxGetItemData( comboBoxLadder, selected );
  546. const LadderInfo *li = TheLadderList->findLadderByIndex( index );
  547. Int numPlayers = 0;
  548. if (li)
  549. {
  550. pref.setLastLadder( li->address, li->port );
  551. numPlayers = li->playersPerTeam*2;
  552. pref.write();
  553. //return; // don't save our defaults based on the tournament's defaults
  554. }
  555. else
  556. {
  557. pref.setLastLadder( AsciiString::TheEmptyString, 0 );
  558. GadgetComboBoxGetSelectedPos(comboBoxNumPlayers, &selected);
  559. if (selected < 0)
  560. selected = 0;
  561. numPlayers = (selected+1)*2;
  562. }
  563. if (!li || !li->randomMaps) // don't save the map as selected if we couldn't choose
  564. {
  565. Int row = 0;
  566. Int entries = GadgetListBoxGetNumEntries(listboxMapSelect);
  567. while ( row < entries)
  568. {
  569. const MapMetaData *md = (const MapMetaData *)GadgetListBoxGetItemData(listboxMapSelect, row, 1);
  570. if(md)
  571. pref.setMapSelected(md->m_fileName, (Bool)GadgetListBoxGetItemData(listboxMapSelect, row));
  572. row++;
  573. }
  574. }
  575. UnicodeString u;
  576. AsciiString a;
  577. // u = GadgetTextEntryGetText(textEntryMaxDisconnects);
  578. // a.translate(u);
  579. // pref.setMaxDisconnects(atoi(a.str()));
  580. // u = GadgetTextEntryGetText(textEntryMaxPoints);
  581. // a.translate(u);
  582. // pref.setMaxPoints(max(100, atoi(a.str())));
  583. // u = GadgetTextEntryGetText(textEntryMinPoints);
  584. // a.translate(u);
  585. // pref.setMinPoints(min(100, atoi(a.str())));
  586. //u = GadgetTextEntryGetText(textEntryWaitTime);
  587. //a.translate(u);
  588. //pref.setWaitTime(atoi(a.str()));
  589. GadgetComboBoxGetSelectedPos(comboBoxNumPlayers, &selected);
  590. pref.setNumPlayers(selected);
  591. GadgetComboBoxGetSelectedPos(comboBoxMaxPing, &selected);
  592. pref.setMaxPing(selected);
  593. Int item;
  594. GadgetComboBoxGetSelectedPos(comboBoxSide, &selected);
  595. item = (Int)GadgetComboBoxGetItemData(comboBoxSide, selected);
  596. pref.setSide(max(0, item));
  597. GadgetComboBoxGetSelectedPos(comboBoxColor, &selected);
  598. pref.setColor(max(0, selected));
  599. GadgetComboBoxGetSelectedPos(comboBoxMaxDisconnects, &selected);
  600. pref.setMaxDisconnects(selected);
  601. pref.write();
  602. }
  603. //-------------------------------------------------------------------------------------------------
  604. /** Initialize the WOL Quick Match Menu */
  605. //-------------------------------------------------------------------------------------------------
  606. void WOLQuickMatchMenuInit( WindowLayout *layout, void *userData )
  607. {
  608. isInInit = TRUE;
  609. if (TheGameSpyGame && TheGameSpyGame->isGameInProgress())
  610. {
  611. TheGameSpyGame->setGameInProgress(FALSE);
  612. // check if we were disconnected
  613. Int disconReason;
  614. if (TheGameSpyInfo->isDisconnectedAfterGameStart(&disconReason))
  615. {
  616. AsciiString disconMunkee;
  617. disconMunkee.format("GUI:GSDisconReason%d", disconReason);
  618. UnicodeString title, body;
  619. title = TheGameText->fetch( "GUI:GSErrorTitle" );
  620. body = TheGameText->fetch( disconMunkee );
  621. GameSpyCloseAllOverlays();
  622. GSMessageBoxOk( title, body );
  623. TheGameSpyInfo->reset();
  624. DEBUG_LOG(("WOLQuickMatchMenuInit() - game was in progress, and we were disconnected, so pop immediate back to main menu\n"));
  625. TheShell->popImmediate();
  626. return;
  627. }
  628. }
  629. nextScreen = NULL;
  630. buttonPushed = false;
  631. isShuttingDown = false;
  632. raiseMessageBoxes = true;
  633. if (TheNAT != NULL) {
  634. delete TheNAT;
  635. TheNAT = NULL;
  636. }
  637. parentWOLQuickMatchID = NAMEKEY( "WOLQuickMatchMenu.wnd:WOLQuickMatchMenuParent" );
  638. buttonBackID = NAMEKEY( "WOLQuickMatchMenu.wnd:ButtonBack" );
  639. buttonBuddiesID = NAMEKEY( "WOLQuickMatchMenu.wnd:ButtonBuddies" );
  640. buttonStartID = NAMEKEY( "WOLQuickMatchMenu.wnd:ButtonStart" );
  641. buttonStopID = NAMEKEY( "WOLQuickMatchMenu.wnd:ButtonStop" );
  642. buttonWidenID = NAMEKEY( "WOLQuickMatchMenu.wnd:ButtonWiden" );
  643. listboxQuickMatchID = NAMEKEY( "WOLQuickMatchMenu.wnd:ListboxQuickMatch" );
  644. listboxMapSelectID = NAMEKEY( "WOLQuickMatchMenu.wnd:ListBoxMapSelect" );
  645. buttonSelectAllMapsID = NAMEKEY( "WOLQuickMatchMenu.wnd:ButtonSelectAllMaps" );
  646. buttonSelectNoMapsID = NAMEKEY( "WOLQuickMatchMenu.wnd:ButtonSelectNoMaps" );
  647. //textEntryMaxDisconnectsID = NAMEKEY( "WOLQuickMatchMenu.wnd:TextEntryMaxDisconnects" );
  648. //textEntryMaxPointsID = NAMEKEY( "WOLQuickMatchMenu.wnd:TextEntryMaxPointPercent" );
  649. //textEntryMinPointsID = NAMEKEY( "WOLQuickMatchMenu.wnd:TextEntryMinPointPercent" );
  650. textEntryWaitTimeID = NAMEKEY( "WOLQuickMatchMenu.wnd:TextEntryWaitTime" );
  651. comboBoxMaxPingID = NAMEKEY( "WOLQuickMatchMenu.wnd:ComboBoxMaxPing" );
  652. comboBoxNumPlayersID = NAMEKEY( "WOLQuickMatchMenu.wnd:ComboBoxNumPlayers" );
  653. comboBoxLadderID = NAMEKEY( "WOLQuickMatchMenu.wnd:ComboBoxLadder" );
  654. comboBoxMaxDisconnectsID = NAMEKEY( "WOLQuickMatchMenu.wnd:ComboBoxMaxDisconnects" );
  655. staticTextNumPlayersID = NAMEKEY( "WOLQuickMatchMenu.wnd:StaticTextNumPlayers" );
  656. comboBoxSideID = NAMEKEY( "WOLQuickMatchMenu.wnd:ComboBoxSide" );
  657. comboBoxColorID = NAMEKEY( "WOLQuickMatchMenu.wnd:ComboBoxColor" );
  658. parentWOLQuickMatch = TheWindowManager->winGetWindowFromId( NULL, parentWOLQuickMatchID );
  659. buttonBack = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, buttonBackID);
  660. buttonStart = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, buttonStartID);
  661. buttonStop = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, buttonStopID);
  662. buttonWiden = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, buttonWidenID);
  663. quickmatchTextWindow = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, listboxQuickMatchID);
  664. listboxMapSelect = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, listboxMapSelectID);
  665. //textEntryMaxDisconnects = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, textEntryMaxDisconnectsID );
  666. //textEntryMaxPoints = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, textEntryMaxPointsID );
  667. //textEntryMinPoints = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, textEntryMinPointsID );
  668. textEntryWaitTime = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, textEntryWaitTimeID );
  669. comboBoxMaxPing = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, comboBoxMaxPingID );
  670. comboBoxNumPlayers = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, comboBoxNumPlayersID );
  671. comboBoxLadder = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, comboBoxLadderID );
  672. comboBoxMaxDisconnects = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, comboBoxMaxDisconnectsID );
  673. TheGameSpyInfo->registerTextWindow(quickmatchTextWindow);
  674. staticTextNumPlayers = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, staticTextNumPlayersID );
  675. comboBoxSide = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, comboBoxSideID );
  676. comboBoxColor = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, comboBoxColorID );
  677. if (TheLadderList->getStandardLadders()->size() == 0
  678. && TheLadderList->getSpecialLadders()->size() == 0
  679. && TheLadderList->getLocalLadders()->size() == 0)
  680. {
  681. // no ladders, so just disable them
  682. comboBoxDisabledLadder = comboBoxLadder;
  683. comboBoxLadder = NULL;
  684. isPopulatingLadderBox = TRUE;
  685. Color normalColor = GameSpyColor[GSCOLOR_MAP_UNSELECTED];
  686. Int index;
  687. GadgetComboBoxReset( comboBoxDisabledLadder );
  688. index = GadgetComboBoxAddEntry( comboBoxDisabledLadder, TheGameText->fetch("GUI:NoLadder"), normalColor );
  689. GadgetComboBoxSetItemData( comboBoxDisabledLadder, index, 0 );
  690. GadgetComboBoxSetSelectedPos( comboBoxDisabledLadder, index );
  691. isPopulatingLadderBox = FALSE;
  692. /** This code would actually *hide* the combo box, but it doesn't look as good. Left here since someone will want to
  693. ** see it at some point. :P
  694. if (comboBoxLadder)
  695. {
  696. comboBoxLadder->winHide(TRUE);
  697. comboBoxLadder->winEnable(FALSE);
  698. }
  699. comboBoxLadder = NULL;
  700. GameWindow *staticTextLadder = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch,
  701. NAMEKEY("WOLQuickMatchMenu.wnd:StaticTextLadder") );
  702. if (staticTextLadder)
  703. staticTextLadder->winHide(TRUE);
  704. */
  705. }
  706. GameWindow *buttonBuddies = TheWindowManager->winGetWindowFromId(NULL, buttonBuddiesID);
  707. if (buttonBuddies)
  708. buttonBuddies->winEnable(TRUE);
  709. GameWindow *staticTextTitle = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch,
  710. NAMEKEY("WOLQuickMatchMenu.wnd:StaticTextTitle") );
  711. if (staticTextTitle)
  712. {
  713. UnicodeString tmp;
  714. tmp.format(TheGameText->fetch("GUI:QuickMatchTitle"), TheGameSpyInfo->getLocalName().str());
  715. GadgetStaticTextSetText(staticTextTitle, tmp);
  716. }
  717. // QM is not going yet, so disable the Widen Search button
  718. buttonWiden->winEnable( FALSE );
  719. buttonStop->winHide( TRUE );
  720. buttonStart->winHide( FALSE );
  721. GadgetListBoxReset(quickmatchTextWindow);
  722. enableOptionsGadgets(TRUE);
  723. // Show Menu
  724. layout->hide( FALSE );
  725. // Set Keyboard to Main Parent
  726. TheWindowManager->winSetFocus( parentWOLQuickMatch );
  727. // fill in preferences
  728. selectedImage = TheMappedImageCollection->findImageByName("CustomMatch_selected");
  729. unselectedImage = TheMappedImageCollection->findImageByName("CustomMatch_deselected");
  730. QuickMatchPreferences pref;
  731. UnicodeString s;
  732. // s.format(L"%d", pref.getMaxDisconnects());
  733. // GadgetTextEntrySetText(textEntryMaxDisconnects, s);
  734. // s.format(L"%d", pref.getMaxPoints());
  735. // GadgetTextEntrySetText(textEntryMaxPoints, s);
  736. // s.format(L"%d", pref.getMinPoints());
  737. // GadgetTextEntrySetText(textEntryMinPoints, s);
  738. //s.format(L"%d", pref.getWaitTime());
  739. //GadgetTextEntrySetText(textEntryWaitTime, s);
  740. maxPoints= pref.getMaxPoints();
  741. minPoints = pref.getMinPoints();
  742. Color c = GameSpyColor[GSCOLOR_DEFAULT];
  743. GadgetComboBoxReset( comboBoxNumPlayers );
  744. Int i;
  745. for (i=1; i<5; ++i)
  746. {
  747. s.format(TheGameText->fetch("GUI:PlayersVersusPlayers"), i, i);
  748. GadgetComboBoxAddEntry( comboBoxNumPlayers, s, c );
  749. }
  750. GadgetComboBoxSetSelectedPos( comboBoxNumPlayers, max(0, pref.getNumPlayers()) );
  751. GadgetComboBoxReset(comboBoxMaxDisconnects);
  752. GadgetComboBoxAddEntry( comboBoxMaxDisconnects, TheGameText->fetch("GUI:Any"), c);
  753. for( i = 1; i < MAX_DISCONNECTS_COUNT; ++i )
  754. {
  755. s.format(L"%d", MAX_DISCONNECTS[i]);
  756. GadgetComboBoxAddEntry( comboBoxMaxDisconnects, s, c );
  757. }
  758. Int maxDisconIndex = max(0, pref.getMaxDisconnects());
  759. GadgetComboBoxSetSelectedPos(comboBoxMaxDisconnects, maxDisconIndex);
  760. GadgetComboBoxReset( comboBoxMaxPing );
  761. maxPingEntries = (TheGameSpyConfig->getPingTimeoutInMs() - 1) / 100;
  762. maxPingEntries++; // need to add the entry for the actual timeout
  763. for (i=1; i <maxPingEntries; ++i)
  764. {
  765. s.format(TheGameText->fetch("GUI:TimeInMilliseconds"), i*100);
  766. GadgetComboBoxAddEntry( comboBoxMaxPing, s, c );
  767. }
  768. GadgetComboBoxAddEntry( comboBoxMaxPing, TheGameText->fetch("GUI:ANY"), c );
  769. i = pref.getMaxPing();
  770. if( i < 0 )
  771. i = 0;
  772. if( i >= maxPingEntries )
  773. i = maxPingEntries - 1;
  774. GadgetComboBoxSetSelectedPos( comboBoxMaxPing, i );
  775. populateQMColorComboBox(pref);
  776. populateQMSideComboBox(pref.getSide(), getLadderInfo());
  777. PopulateQMLadderComboBox();
  778. TheShell->showShellMap(TRUE);
  779. TheGameSpyGame->reset();
  780. GadgetListBoxReset(listboxMapSelect);
  781. populateQuickMatchMapSelectListbox(pref);
  782. UpdateLocalPlayerStats();
  783. UpdateStartButton();
  784. TheTransitionHandler->setGroup("WOLQuickMatchMenuFade");
  785. isInInit= FALSE;
  786. } // WOLQuickMatchMenuInit
  787. //-------------------------------------------------------------------------------------------------
  788. /** This is called when a shutdown is complete for this menu */
  789. //-------------------------------------------------------------------------------------------------
  790. static void shutdownComplete( WindowLayout *layout )
  791. {
  792. isShuttingDown = false;
  793. // hide the layout
  794. layout->hide( TRUE );
  795. // our shutdown is complete
  796. TheShell->shutdownComplete( layout, (nextScreen != NULL) );
  797. if (nextScreen != NULL)
  798. {
  799. TheShell->push(nextScreen);
  800. }
  801. nextScreen = NULL;
  802. } // end if
  803. //-------------------------------------------------------------------------------------------------
  804. /** WOL Quick Match Menu shutdown method */
  805. //-------------------------------------------------------------------------------------------------
  806. void WOLQuickMatchMenuShutdown( WindowLayout *layout, void *userData )
  807. {
  808. TheGameSpyInfo->unregisterTextWindow(quickmatchTextWindow);
  809. if (!TheGameEngine->getQuitting())
  810. saveQuickMatchOptions();
  811. parentWOLQuickMatch = NULL;
  812. buttonBack = NULL;
  813. quickmatchTextWindow = NULL;
  814. selectedImage = unselectedImage = NULL;
  815. isShuttingDown = true;
  816. // if we are shutting down for an immediate pop, skip the animations
  817. Bool popImmediate = *(Bool *)userData;
  818. if( popImmediate )
  819. {
  820. shutdownComplete( layout );
  821. return;
  822. } //end if
  823. TheShell->reverseAnimatewindow();
  824. TheTransitionHandler->reverse("WOLQuickMatchMenuFade");
  825. RaiseGSMessageBox();
  826. } // WOLQuickMatchMenuShutdown
  827. #ifdef PERF_TEST
  828. static const char* getMessageString(Int t)
  829. {
  830. switch(t)
  831. {
  832. case PeerResponse::PEERRESPONSE_LOGIN:
  833. return "login";
  834. case PeerResponse::PEERRESPONSE_DISCONNECT:
  835. return "disconnect";
  836. case PeerResponse::PEERRESPONSE_MESSAGE:
  837. return "message";
  838. case PeerResponse::PEERRESPONSE_GROUPROOM:
  839. return "group room";
  840. case PeerResponse::PEERRESPONSE_STAGINGROOM:
  841. return "staging room";
  842. case PeerResponse::PEERRESPONSE_STAGINGROOMPLAYERINFO:
  843. return "staging room player info";
  844. case PeerResponse::PEERRESPONSE_JOINGROUPROOM:
  845. return "group room join";
  846. case PeerResponse::PEERRESPONSE_CREATESTAGINGROOM:
  847. return "staging room create";
  848. case PeerResponse::PEERRESPONSE_JOINSTAGINGROOM:
  849. return "staging room join";
  850. case PeerResponse::PEERRESPONSE_PLAYERJOIN:
  851. return "player join";
  852. case PeerResponse::PEERRESPONSE_PLAYERLEFT:
  853. return "player part";
  854. case PeerResponse::PEERRESPONSE_PLAYERCHANGEDNICK:
  855. return "player nick";
  856. case PeerResponse::PEERRESPONSE_PLAYERINFO:
  857. return "player info";
  858. case PeerResponse::PEERRESPONSE_PLAYERCHANGEDFLAGS:
  859. return "player flags";
  860. case PeerResponse::PEERRESPONSE_ROOMUTM:
  861. return "room UTM";
  862. case PeerResponse::PEERRESPONSE_PLAYERUTM:
  863. return "player UTM";
  864. case PeerResponse::PEERRESPONSE_QUICKMATCHSTATUS:
  865. return "QM status";
  866. case PeerResponse::PEERRESPONSE_GAMESTART:
  867. return "game start";
  868. case PeerResponse::PEERRESPONSE_FAILEDTOHOST:
  869. return "host failure";
  870. }
  871. return "unknown";
  872. }
  873. #endif // PERF_TEST
  874. //-------------------------------------------------------------------------------------------------
  875. /** WOL Quick Match Menu update method */
  876. //-------------------------------------------------------------------------------------------------
  877. void WOLQuickMatchMenuUpdate( WindowLayout * layout, void *userData)
  878. {
  879. if (TheGameLogic->isInShellGame() && TheGameLogic->getFrame() == 1)
  880. {
  881. SignalUIInteraction(SHELL_SCRIPT_HOOK_GENERALS_ONLINE_ENTERED_FROM_GAME);
  882. }
  883. // We'll only be successful if we've requested to
  884. if(isShuttingDown && TheShell->isAnimFinished()&& TheTransitionHandler->isFinished())
  885. shutdownComplete(layout);
  886. if (raiseMessageBoxes)
  887. {
  888. RaiseGSMessageBox();
  889. raiseMessageBoxes = false;
  890. }
  891. /// @todo: MDC handle disconnects in-game the same way as Custom Match!
  892. if (TheShell->isAnimFinished() && !buttonPushed && TheGameSpyPeerMessageQueue)
  893. {
  894. HandleBuddyResponses();
  895. HandlePersistentStorageResponses();
  896. if (TheGameSpyGame && TheGameSpyGame->isGameInProgress())
  897. {
  898. if (TheGameSpyInfo->isDisconnectedAfterGameStart(NULL))
  899. {
  900. return; // already been disconnected, so don't worry.
  901. }
  902. Int allowedMessages = TheGameSpyInfo->getMaxMessagesPerUpdate();
  903. Bool sawImportantMessage = FALSE;
  904. PeerResponse resp;
  905. while (allowedMessages-- && !sawImportantMessage && TheGameSpyPeerMessageQueue->getResponse( resp ))
  906. {
  907. switch (resp.peerResponseType)
  908. {
  909. case PeerResponse::PEERRESPONSE_DISCONNECT:
  910. {
  911. sawImportantMessage = TRUE;
  912. AsciiString disconMunkee;
  913. disconMunkee.format("GUI:GSDisconReason%d", resp.discon.reason);
  914. // check for scorescreen
  915. NameKeyType listboxChatWindowScoreScreenID = NAMEKEY("ScoreScreen.wnd:ListboxChatWindowScoreScreen");
  916. GameWindow *listboxChatWindowScoreScreen = TheWindowManager->winGetWindowFromId( NULL, listboxChatWindowScoreScreenID );
  917. if (listboxChatWindowScoreScreen)
  918. {
  919. GadgetListBoxAddEntryText(listboxChatWindowScoreScreen, TheGameText->fetch(disconMunkee),
  920. GameSpyColor[GSCOLOR_DEFAULT], -1);
  921. }
  922. else
  923. {
  924. // still ingame
  925. TheInGameUI->message(disconMunkee);
  926. }
  927. TheGameSpyInfo->markAsDisconnectedAfterGameStart(resp.discon.reason);
  928. }
  929. }
  930. }
  931. return; // if we're in game, all we care about is if we've been disconnected from the chat server
  932. }
  933. if (TheNAT != NULL) {
  934. NATStateType NATState = TheNAT->update();
  935. if (NATState == NATSTATE_DONE)
  936. {
  937. TheGameSpyGame->launchGame();
  938. if (TheGameSpyInfo) // this can be blown away by a disconnect on the map transfer screen
  939. TheGameSpyInfo->leaveStagingRoom();
  940. return; // don't do any more processing this frame, in case the screen goes away
  941. }
  942. else if (NATState == NATSTATE_FAILED)
  943. {
  944. // delete TheNAT, its no good for us anymore.
  945. delete TheNAT;
  946. TheNAT = NULL;
  947. // Just back out. This cleans up some slot list problems
  948. buttonPushed = true;
  949. GSMessageBoxOk(TheGameText->fetch("GUI:Error"), TheGameText->fetch("GUI:NATNegotiationFailed"));
  950. nextScreen = "Menus/WOLWelcomeMenu.wnd";
  951. TheShell->pop();
  952. return; // don't do any more processing this frame, in case the screen goes away
  953. }
  954. }
  955. #ifdef PERF_TEST
  956. UnsignedInt start = timeGetTime();
  957. UnsignedInt end = timeGetTime();
  958. std::list<Int> responses;
  959. Int numMessages = 0;
  960. #endif // PERF_TEST
  961. Int allowedMessages = TheGameSpyInfo->getMaxMessagesPerUpdate();
  962. Bool sawImportantMessage = FALSE;
  963. PeerResponse resp;
  964. while (allowedMessages-- && !sawImportantMessage && TheGameSpyPeerMessageQueue->getResponse( resp ))
  965. {
  966. #ifdef PERF_TEST
  967. ++numMessages;
  968. responses.push_back(resp.peerResponseType);
  969. #endif // PERF_TEST
  970. switch (resp.peerResponseType)
  971. {
  972. case PeerResponse::PEERRESPONSE_PLAYERUTM:
  973. {
  974. if (!stricmp(resp.command.c_str(), "STATS"))
  975. {
  976. DEBUG_LOG(("Saw STATS from %s, data was '%s'\n", resp.nick.c_str(), resp.commandOptions.c_str()));
  977. AsciiString data = resp.commandOptions.c_str();
  978. AsciiString idStr;
  979. data.nextToken(&idStr, " ");
  980. Int id = atoi(idStr.str());
  981. DEBUG_LOG(("data: %d(%s) - '%s'\n", id, idStr.str(), data.str()));
  982. PSPlayerStats stats = TheGameSpyPSMessageQueue->parsePlayerKVPairs(data.str());
  983. PSPlayerStats oldStats = TheGameSpyPSMessageQueue->findPlayerStatsByID(id);
  984. stats.id = id;
  985. DEBUG_LOG(("Parsed ID is %d, old ID is %d\n", stats.id, oldStats.id));
  986. if (stats.id && (oldStats.id == 0))
  987. TheGameSpyPSMessageQueue->trackPlayerStats(stats);
  988. // now fill in the profileID in the game slot
  989. AsciiString nick = resp.nick.c_str();
  990. for (Int i=0; i<MAX_SLOTS; ++i)
  991. {
  992. GameSpyGameSlot *slot = TheGameSpyGame->getGameSpySlot(i);
  993. if (slot && slot->isHuman() && (slot->getLoginName().compareNoCase(nick) == 0))
  994. {
  995. slot->setProfileID(id);
  996. break;
  997. }
  998. }
  999. }
  1000. Int slotNum = TheGameSpyGame->getSlotNum(resp.nick.c_str());
  1001. if ((slotNum >= 0) && (slotNum < MAX_SLOTS) && (!stricmp(resp.command.c_str(), "NAT"))) {
  1002. // this is a command for NAT negotiations, pass if off to TheNAT
  1003. sawImportantMessage = TRUE;
  1004. if (TheNAT != NULL) {
  1005. TheNAT->processGlobalMessage(slotNum, resp.commandOptions.c_str());
  1006. }
  1007. }
  1008. /*
  1009. else if (key == "NAT")
  1010. {
  1011. if ((val >= FirewallHelperClass::FIREWALL_TYPE_SIMPLE) &&
  1012. (val <= FirewallHelperClass::FIREWALL_TYPE_DESTINATION_PORT_DELTA))
  1013. {
  1014. slot->setNATBehavior((FirewallHelperClass::FirewallBehaviorType)val);
  1015. DEBUG_LOG(("Setting NAT behavior to %d for player %d\n", val, slotNum));
  1016. change = true;
  1017. }
  1018. else
  1019. {
  1020. DEBUG_LOG(("Rejecting invalid NAT behavior %d from player %d\n", val, slotNum));
  1021. }
  1022. }
  1023. */
  1024. }
  1025. break;
  1026. case PeerResponse::PEERRESPONSE_DISCONNECT:
  1027. {
  1028. sawImportantMessage = TRUE;
  1029. UnicodeString title, body;
  1030. AsciiString disconMunkee;
  1031. disconMunkee.format("GUI:GSDisconReason%d", resp.discon.reason);
  1032. title = TheGameText->fetch( "GUI:GSErrorTitle" );
  1033. body = TheGameText->fetch( disconMunkee );
  1034. GameSpyCloseAllOverlays();
  1035. GSMessageBoxOk( title, body );
  1036. TheGameSpyInfo->reset();
  1037. TheShell->pop();
  1038. }
  1039. break; // LORENZEN ADDED. SORRY IF THIS "BREAKS" IT...
  1040. case PeerResponse::PEERRESPONSE_JOINGROUPROOM:
  1041. /*
  1042. if (resp.joinGroupRoom.ok)
  1043. {
  1044. TheGameSpyInfo->addText(UnicodeString(L"Joined group room"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1045. }
  1046. else
  1047. {
  1048. TheGameSpyInfo->addText(UnicodeString(L"Didn't join group room"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1049. }
  1050. */
  1051. break;
  1052. case PeerResponse::PEERRESPONSE_PLAYERJOIN:
  1053. {
  1054. //UnicodeString str;
  1055. //str.format(L"Player %hs joined the room", resp.nick.c_str());
  1056. //TheGameSpyInfo->addText(str, GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1057. }
  1058. break;
  1059. case PeerResponse::PEERRESPONSE_PLAYERLEFT:
  1060. {
  1061. //UnicodeString str;
  1062. //str.format(L"Player %hs left the room", resp.nick.c_str());
  1063. //TheGameSpyInfo->addText(str, GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1064. }
  1065. break;
  1066. case PeerResponse::PEERRESPONSE_MESSAGE:
  1067. {
  1068. //UnicodeString m;
  1069. //m.format(L"[%hs]: %ls", resp.nick.c_str(), resp.text.c_str());
  1070. //TheGameSpyInfo->addText(m, GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1071. }
  1072. break;
  1073. // LORENZEN EXPRESSES DOUBT ABOUT THIS ONE, AS IT MAY HAVE SUFFERED MERGE MANGLING... SORRY
  1074. // I THINK THIS IS THE OBSOLETE VERSION... SEE THE NEWER LOOKING ONE ABOVE
  1075. /*
  1076. case PeerResponse::PEERRESPONSE_DISCONNECT:
  1077. {
  1078. UnicodeString title, body;
  1079. AsciiString disconMunkee;
  1080. disconMunkee.format("GUI:GSDisconReason%d", resp.discon.reason);
  1081. title = TheGameText->fetch( "GUI:GSErrorTitle" );
  1082. body = TheGameText->fetch( disconMunkee );
  1083. GameSpyCloseAllOverlays();
  1084. GSMessageBoxOk( title, body );
  1085. TheGameSpyInfo->reset();
  1086. TheShell->pop();
  1087. }
  1088. */
  1089. case PeerResponse::PEERRESPONSE_CREATESTAGINGROOM:
  1090. {
  1091. if (resp.createStagingRoom.result == PEERJoinSuccess)
  1092. {
  1093. // Woohoo! On to our next screen!
  1094. UnicodeString str;
  1095. str.format(L"Created staging room");
  1096. TheGameSpyInfo->addText(str, GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1097. }
  1098. else
  1099. {
  1100. UnicodeString s;
  1101. s.format(L"createStagingRoom result: %d", resp.createStagingRoom.result);
  1102. TheGameSpyInfo->addText( s, GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow );
  1103. }
  1104. }
  1105. break;
  1106. case PeerResponse::PEERRESPONSE_JOINSTAGINGROOM:
  1107. {
  1108. if (resp.joinStagingRoom.ok == PEERTrue)
  1109. {
  1110. // Woohoo! On to our next screen!
  1111. UnicodeString s;
  1112. s.format(L"joinStagingRoom result: %d", resp.joinStagingRoom.ok);
  1113. TheGameSpyInfo->addText( s, GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow );
  1114. }
  1115. else
  1116. {
  1117. UnicodeString s;
  1118. s.format(L"joinStagingRoom result: %d", resp.joinStagingRoom.ok);
  1119. TheGameSpyInfo->addText( s, GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow );
  1120. }
  1121. }
  1122. break;
  1123. case PeerResponse::PEERRESPONSE_STAGINGROOM:
  1124. {
  1125. UnicodeString str;
  1126. str.format(L"Staging room list callback", resp.nick.c_str());
  1127. TheGameSpyInfo->addText(str, GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1128. }
  1129. break;
  1130. case PeerResponse::PEERRESPONSE_QUICKMATCHSTATUS:
  1131. {
  1132. sawImportantMessage = TRUE;
  1133. switch( resp.qmStatus.status )
  1134. {
  1135. case QM_IDLE:
  1136. //TheGameSpyInfo->addText(UnicodeString(L"Status: QM_IDLE"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1137. break;
  1138. case QM_JOININGQMCHANNEL:
  1139. TheGameSpyInfo->addText(TheGameText->fetch("QM:JOININGQMCHANNEL"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1140. break;
  1141. case QM_LOOKINGFORBOT:
  1142. TheGameSpyInfo->addText(TheGameText->fetch("QM:LOOKINGFORBOT"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1143. break;
  1144. case QM_SENTINFO:
  1145. TheGameSpyInfo->addText(TheGameText->fetch("QM:SENTINFO"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1146. break;
  1147. case QM_WORKING:
  1148. {
  1149. UnicodeString s;
  1150. s.format(TheGameText->fetch("QM:WORKING"), resp.qmStatus.poolSize);
  1151. TheGameSpyInfo->addText(s, GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1152. }
  1153. buttonWiden->winEnable( TRUE );
  1154. break;
  1155. case QM_POOLSIZE:
  1156. {
  1157. UnicodeString s;
  1158. s.format(TheGameText->fetch("QM:POOLSIZE"), resp.qmStatus.poolSize);
  1159. TheGameSpyInfo->addText(s, GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1160. }
  1161. break;
  1162. case QM_WIDENINGSEARCH:
  1163. TheGameSpyInfo->addText(TheGameText->fetch("QM:WIDENINGSEARCH"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1164. buttonWiden->winEnable( FALSE );
  1165. break;
  1166. case QM_MATCHED:
  1167. {
  1168. TheGameSpyInfo->addText(TheGameText->fetch("QM:MATCHED"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1169. buttonWiden->winEnable( FALSE );
  1170. TheGameSpyGame->enterGame();
  1171. TheGameSpyGame->setSeed(resp.qmStatus.seed);
  1172. TheGameSpyGame->markGameAsQM();
  1173. const LadderInfo *info = getLadderInfo();
  1174. if (!info)
  1175. {
  1176. TheGameSpyGame->setLadderIP("localhost");
  1177. TheGameSpyGame->setLadderPort(0);
  1178. }
  1179. else
  1180. {
  1181. TheGameSpyGame->setLadderIP(info->address);
  1182. TheGameSpyGame->setLadderPort(info->port);
  1183. }
  1184. Int i;
  1185. Int numPlayers = 0;
  1186. for (i=0; i<MAX_SLOTS; ++i)
  1187. {
  1188. if (!resp.stagingRoomPlayerNames[i].empty())
  1189. ++numPlayers;
  1190. }
  1191. std::list<AsciiString> maps = TheGameSpyConfig->getQMMaps();
  1192. for (std::list<AsciiString>::const_iterator it = maps.begin(); it != maps.end(); ++it)
  1193. {
  1194. AsciiString theMap = *it;
  1195. theMap.toLower();
  1196. const MapMetaData *md = TheMapCache->findMap(theMap);
  1197. if (md && md->m_numPlayers >= numPlayers)
  1198. {
  1199. TheGameSpyGame->setMap(*it);
  1200. if (resp.qmStatus.mapIdx-- == 0)
  1201. break;
  1202. }
  1203. }
  1204. Int numPlayersPerTeam = numPlayers/2;
  1205. DEBUG_ASSERTCRASH(numPlayersPerTeam, ("0 players per team???"));
  1206. if (!numPlayersPerTeam)
  1207. numPlayersPerTeam = 1;
  1208. for (i=0; i<MAX_SLOTS; ++i)
  1209. {
  1210. GameSpyGameSlot *slot = TheGameSpyGame->getGameSpySlot(i);
  1211. if (resp.stagingRoomPlayerNames[i].empty())
  1212. {
  1213. slot->setState(SLOT_CLOSED);
  1214. }
  1215. else
  1216. {
  1217. AsciiString aName = resp.stagingRoomPlayerNames[i].c_str();
  1218. UnicodeString uName;
  1219. uName.translate(aName);
  1220. slot->setState(SLOT_PLAYER, uName, resp.qmStatus.IP[i]);
  1221. slot->setColor(resp.qmStatus.color[i]);
  1222. slot->setPlayerTemplate(resp.qmStatus.side[i]);
  1223. //slot->setProfileID(0);
  1224. slot->setNATBehavior((FirewallHelperClass::FirewallBehaviorType)resp.qmStatus.nat[i]);
  1225. slot->setLocale("");
  1226. slot->setTeamNumber( i/numPlayersPerTeam );
  1227. if (i==0)
  1228. TheGameSpyGame->setGameName(uName);
  1229. }
  1230. }
  1231. DEBUG_LOG(("Starting a QM game: options=[%s]\n", GameInfoToAsciiString(TheGameSpyGame).str()));
  1232. SendStatsToOtherPlayers(TheGameSpyGame);
  1233. TheGameSpyGame->startGame(0);
  1234. GameWindow *buttonBuddies = TheWindowManager->winGetWindowFromId(NULL, buttonBuddiesID);
  1235. if (buttonBuddies)
  1236. buttonBuddies->winEnable(FALSE);
  1237. GameSpyCloseOverlay(GSOVERLAY_BUDDY);
  1238. }
  1239. break;
  1240. case QM_INCHANNEL:
  1241. TheGameSpyInfo->addText(TheGameText->fetch("QM:INCHANNEL"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1242. break;
  1243. case QM_NEGOTIATINGFIREWALLS:
  1244. TheGameSpyInfo->addText(TheGameText->fetch("QM:NEGOTIATINGFIREWALLS"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1245. break;
  1246. case QM_STARTINGGAME:
  1247. TheGameSpyInfo->addText(TheGameText->fetch("QM:STARTINGGAME"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1248. break;
  1249. case QM_COULDNOTFINDBOT:
  1250. TheGameSpyInfo->addText(TheGameText->fetch("QM:COULDNOTFINDBOT"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1251. buttonWiden->winEnable( FALSE );
  1252. buttonStart->winHide( FALSE );
  1253. buttonStop->winHide( TRUE );
  1254. enableOptionsGadgets(TRUE);
  1255. break;
  1256. case QM_COULDNOTFINDCHANNEL:
  1257. TheGameSpyInfo->addText(TheGameText->fetch("QM:COULDNOTFINDCHANNEL"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1258. buttonWiden->winEnable( FALSE );
  1259. buttonStart->winHide( FALSE );
  1260. buttonStop->winHide( TRUE );
  1261. enableOptionsGadgets(TRUE);
  1262. break;
  1263. case QM_COULDNOTNEGOTIATEFIREWALLS:
  1264. TheGameSpyInfo->addText(TheGameText->fetch("QM:COULDNOTNEGOTIATEFIREWALLS"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1265. buttonWiden->winEnable( FALSE );
  1266. buttonStart->winHide( FALSE );
  1267. buttonStop->winHide( TRUE );
  1268. enableOptionsGadgets(TRUE);
  1269. break;
  1270. case QM_STOPPED:
  1271. TheGameSpyInfo->addText(TheGameText->fetch("QM:STOPPED"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1272. buttonWiden->winEnable( FALSE );
  1273. buttonStart->winHide( FALSE );
  1274. buttonStop->winHide( TRUE );
  1275. enableOptionsGadgets(TRUE);
  1276. break;
  1277. }
  1278. }
  1279. break;
  1280. }
  1281. }
  1282. #ifdef PERF_TEST
  1283. // check performance
  1284. end = timeGetTime();
  1285. UnsignedInt frameTime = end-start;
  1286. if (frameTime > 100 || responses.size() > 20)
  1287. {
  1288. UnicodeString munkee;
  1289. munkee.format(L"inQM:%d %d ms, %d messages", s_inQM, frameTime, responses.size());
  1290. TheGameSpyInfo->addText(munkee, GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1291. PERF_LOG(("%ls\n", munkee.str()));
  1292. std::list<Int>::const_iterator it;
  1293. for (it = responses.begin(); it != responses.end(); ++it)
  1294. {
  1295. PERF_LOG((" %s\n", getMessageString(*it)));
  1296. }
  1297. }
  1298. #endif // PERF_TEST
  1299. }
  1300. }// WOLQuickMatchMenuUpdate
  1301. //-------------------------------------------------------------------------------------------------
  1302. /** WOL Quick Match Menu input callback */
  1303. //-------------------------------------------------------------------------------------------------
  1304. WindowMsgHandledType WOLQuickMatchMenuInput( GameWindow *window, UnsignedInt msg,
  1305. WindowMsgData mData1, WindowMsgData mData2 )
  1306. {
  1307. switch( msg )
  1308. {
  1309. // --------------------------------------------------------------------------------------------
  1310. case GWM_CHAR:
  1311. {
  1312. UnsignedByte key = mData1;
  1313. UnsignedByte state = mData2;
  1314. if (buttonPushed)
  1315. break;
  1316. switch( key )
  1317. {
  1318. // ----------------------------------------------------------------------------------------
  1319. case KEY_ESC:
  1320. {
  1321. //
  1322. // send a simulated selected event to the parent window of the
  1323. // back/exit button
  1324. //
  1325. if( BitTest( state, KEY_STATE_UP ) )
  1326. {
  1327. if(!buttonBack->winIsHidden())
  1328. TheWindowManager->winSendSystemMsg( window, GBM_SELECTED,
  1329. (WindowMsgData)buttonBack, buttonBackID );
  1330. } // end if
  1331. // don't let key fall through anywhere else
  1332. return MSG_HANDLED;
  1333. } // end escape
  1334. } // end switch( key )
  1335. } // end char
  1336. } // end switch( msg )
  1337. return MSG_IGNORED;
  1338. }// WOLQuickMatchMenuInput
  1339. //-------------------------------------------------------------------------------------------------
  1340. /** WOL Quick Match Menu window system callback */
  1341. //-------------------------------------------------------------------------------------------------
  1342. WindowMsgHandledType WOLQuickMatchMenuSystem( GameWindow *window, UnsignedInt msg,
  1343. WindowMsgData mData1, WindowMsgData mData2 )
  1344. {
  1345. UnicodeString txtInput;
  1346. switch( msg )
  1347. {
  1348. case GWM_CREATE:
  1349. {
  1350. break;
  1351. } // case GWM_DESTROY:
  1352. case GWM_DESTROY:
  1353. {
  1354. break;
  1355. } // case GWM_DESTROY:
  1356. case GWM_INPUT_FOCUS:
  1357. {
  1358. // if we're givin the opportunity to take the keyboard focus we must say we want it
  1359. if( mData1 == TRUE )
  1360. *(Bool *)mData2 = TRUE;
  1361. return MSG_HANDLED;
  1362. }//case GWM_INPUT_FOCUS:
  1363. case GCM_SELECTED:
  1364. {
  1365. if (buttonPushed)
  1366. break;
  1367. GameWindow *control = (GameWindow *)mData1;
  1368. Int controlID = control->winGetWindowId();
  1369. Int pos = -1;
  1370. GadgetComboBoxGetSelectedPos(control, &pos);
  1371. saveQuickMatchOptions();
  1372. if (controlID == comboBoxLadderID && !isPopulatingLadderBox)
  1373. {
  1374. if (pos >= 0)
  1375. {
  1376. QuickMatchPreferences pref;
  1377. Int ladderID = (Int)GadgetComboBoxGetItemData(control, pos);
  1378. if (ladderID == 0)
  1379. {
  1380. // no ladder selected - enable buttons
  1381. GadgetComboBoxSetSelectedPos(comboBoxNumPlayers, max(0, pref.getNumPlayers()/2-1));
  1382. comboBoxNumPlayers->winEnable( TRUE );
  1383. populateQMSideComboBox(pref.getSide()); // this will set side to random and disable if necessary
  1384. }
  1385. else if (ladderID > 0)
  1386. {
  1387. // ladder selected - disable buttons
  1388. const LadderInfo *li = TheLadderList->findLadderByIndex(ladderID);
  1389. if (li)
  1390. GadgetComboBoxSetSelectedPos(comboBoxNumPlayers, li->playersPerTeam-1);
  1391. else
  1392. GadgetComboBoxSetSelectedPos(comboBoxNumPlayers, 0);
  1393. comboBoxNumPlayers->winEnable( FALSE );
  1394. populateQMSideComboBox(pref.getSide(), li); // this will set side to random and disable if necessary
  1395. }
  1396. else
  1397. {
  1398. // "Choose a ladder" selected - open overlay
  1399. PopulateQMLadderComboBox(); // this restores the non-"Choose a ladder" selection
  1400. GameSpyOpenOverlay( GSOVERLAY_LADDERSELECT );
  1401. }
  1402. }
  1403. }
  1404. if (!isInInit)
  1405. {
  1406. QuickMatchPreferences pref;
  1407. populateQuickMatchMapSelectListbox(pref);
  1408. UpdateStartButton();
  1409. }
  1410. break;
  1411. } // case GCM_SELECTED
  1412. case GBM_SELECTED:
  1413. {
  1414. if (buttonPushed)
  1415. break;
  1416. GameWindow *control = (GameWindow *)mData1;
  1417. Int controlID = control->winGetWindowId();
  1418. static NameKeyType buttonOptionsID = NAMEKEY("WOLQuickMatchMenu.wnd:ButtonOptions");
  1419. if ( controlID == buttonStopID )
  1420. {
  1421. PeerRequest req;
  1422. req.peerRequestType = PeerRequest::PEERREQUEST_STOPQUICKMATCH;
  1423. TheGameSpyPeerMessageQueue->addRequest(req);
  1424. buttonWiden->winEnable( FALSE );
  1425. buttonStart->winHide( FALSE );
  1426. buttonStop->winHide( TRUE );
  1427. enableOptionsGadgets(TRUE);
  1428. TheGameSpyInfo->addText(TheGameText->fetch("GUI:QMAborted"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
  1429. }
  1430. else if ( controlID == buttonOptionsID )
  1431. {
  1432. GameWindow *win =TheWindowManager->winGetWindowFromId(parentWOLQuickMatch,buttonOptionsID);
  1433. if (isInfoShown())
  1434. {
  1435. hideInfoGadgets(TRUE);
  1436. hideOptionsGadgets(FALSE);
  1437. GadgetButtonSetText(win, TheGameText->fetch("GUI:PlayerInfo"));
  1438. }
  1439. else
  1440. {
  1441. hideInfoGadgets(FALSE);
  1442. hideOptionsGadgets(TRUE);
  1443. GadgetButtonSetText(win, TheGameText->fetch("GUI:Setup"));
  1444. }
  1445. }
  1446. else if ( controlID == buttonWidenID )
  1447. {
  1448. PeerRequest req;
  1449. req.peerRequestType = PeerRequest::PEERREQUEST_WIDENQUICKMATCHSEARCH;
  1450. TheGameSpyPeerMessageQueue->addRequest(req);
  1451. buttonWiden->winEnable( FALSE );
  1452. }
  1453. else if ( controlID == buttonStartID )
  1454. {
  1455. PeerRequest req;
  1456. req.peerRequestType = PeerRequest::PEERREQUEST_STARTQUICKMATCH;
  1457. req.qmMaps.clear();
  1458. Int numMaps = GadgetListBoxGetNumEntries(listboxMapSelect);
  1459. for ( Int i=0; i<numMaps; ++i )
  1460. {
  1461. req.qmMaps.push_back(GadgetListBoxGetItemData(listboxMapSelect, i, 0));
  1462. }
  1463. UnicodeString u;
  1464. AsciiString a;
  1465. // u = GadgetTextEntryGetText(textEntryMaxDisconnects);
  1466. // a.translate(u);
  1467. // req.QM.maxDiscons = atoi(a.str());
  1468. // u = GadgetTextEntryGetText(textEntryMaxPoints);
  1469. // a.translate(u);
  1470. req.QM.maxPointPercentage = max(100, maxPoints);
  1471. // u = GadgetTextEntryGetText(textEntryMinPoints);
  1472. // a.translate(u);
  1473. req.QM.minPointPercentage = min(100, minPoints);
  1474. //u = GadgetTextEntryGetText(textEntryWaitTime);
  1475. //a.translate(u);
  1476. //req.QM.widenTime = atoi(a.str());
  1477. req.QM.widenTime = 0;
  1478. Int val;
  1479. GadgetComboBoxGetSelectedPos(comboBoxMaxDisconnects, &val);
  1480. if( val < 0)
  1481. val = 0;
  1482. req.QM.maxDiscons = MAX_DISCONNECTS[val];
  1483. GadgetComboBoxGetSelectedPos(comboBoxMaxPing, &val);
  1484. if (val < 0)
  1485. val = 0;
  1486. if( val >= maxPingEntries - 1)
  1487. {
  1488. req.QM.maxPing = TheGameSpyConfig->getPingTimeoutInMs();
  1489. }
  1490. else
  1491. req.QM.maxPing = (val+1)*100;
  1492. PSPlayerStats stats = TheGameSpyPSMessageQueue->findPlayerStatsByID(TheGameSpyInfo->getLocalProfileID());
  1493. req.QM.points = CalculateRank(stats);
  1494. Int ladderIndex, index, selected;
  1495. GadgetComboBoxGetSelectedPos( comboBoxLadder, &selected );
  1496. ladderIndex = (Int)GadgetComboBoxGetItemData( comboBoxLadder, selected );
  1497. const LadderInfo *ladderInfo = NULL;
  1498. if (ladderIndex < 0)
  1499. {
  1500. ladderIndex = 0;
  1501. }
  1502. if (ladderIndex)
  1503. {
  1504. ladderInfo = TheLadderList->findLadderByIndex( ladderIndex );
  1505. if (!ladderInfo)
  1506. {
  1507. ladderIndex = 0; // sanity
  1508. }
  1509. }
  1510. req.QM.ladderID = ladderIndex;
  1511. req.QM.ladderPassCRC = 0;
  1512. index = -1;
  1513. GadgetComboBoxGetSelectedPos( comboBoxSide, &selected );
  1514. if (selected >= 0)
  1515. index = (Int)GadgetComboBoxGetItemData( comboBoxSide, selected );
  1516. req.QM.side = index;
  1517. if (ladderInfo && ladderInfo->randomFactions)
  1518. {
  1519. Int sideNum = GameClientRandomValue(0, ladderInfo->validFactions.size()-1);
  1520. DEBUG_LOG(("Looking for %d out of %d random sides\n", sideNum, ladderInfo->validFactions.size()));
  1521. AsciiStringListConstIterator cit = ladderInfo->validFactions.begin();
  1522. while (sideNum)
  1523. {
  1524. ++cit;
  1525. --sideNum;
  1526. }
  1527. if (cit != ladderInfo->validFactions.end())
  1528. {
  1529. Int numPlayerTemplates = ThePlayerTemplateStore->getPlayerTemplateCount();
  1530. AsciiString sideStr = *cit;
  1531. DEBUG_LOG(("Chose %s as our side... finding\n", sideStr.str()));
  1532. for (Int c=0; c<numPlayerTemplates; ++c)
  1533. {
  1534. const PlayerTemplate *fac = ThePlayerTemplateStore->getNthPlayerTemplate(c);
  1535. if (fac && fac->getSide() == sideStr)
  1536. {
  1537. DEBUG_LOG(("Found %s in index %d\n", sideStr.str(), c));
  1538. req.QM.side = c;
  1539. break;
  1540. }
  1541. }
  1542. }
  1543. }
  1544. else if( index == PLAYERTEMPLATE_RANDOM )
  1545. {
  1546. // If not a forced random ladder, then we need to resolve our pick of random right now anyway, or else
  1547. // we will get the same pick every darn time.
  1548. Int randomTries = 0;// Rare to hit Random 10 times in a row, but if it does then random will be converted to a set side by the very bug this tries to fix, so no harm done.
  1549. while( randomTries < 10 && index == PLAYERTEMPLATE_RANDOM )
  1550. {
  1551. Int numberComboBoxEntries = GadgetComboBoxGetLength(comboBoxSide);
  1552. Int randomPick = GameClientRandomValue(0, numberComboBoxEntries - 1);
  1553. index = (Int)GadgetComboBoxGetItemData( comboBoxSide, randomPick );
  1554. req.QM.side = index;
  1555. randomTries++;
  1556. }
  1557. }
  1558. index = -1;
  1559. GadgetComboBoxGetSelectedPos( comboBoxColor, &selected );
  1560. if (selected >= 0)
  1561. index = (Int)GadgetComboBoxGetItemData( comboBoxColor, selected );
  1562. req.QM.color = index;
  1563. OptionPreferences natPref;
  1564. req.QM.NAT = natPref.getFirewallBehavior();
  1565. if (ladderIndex)
  1566. {
  1567. req.QM.numPlayers = (ladderInfo)?ladderInfo->playersPerTeam*2 : 2;
  1568. }
  1569. else
  1570. {
  1571. GadgetComboBoxGetSelectedPos(comboBoxNumPlayers, &val);
  1572. if (val < 0)
  1573. val = 0;
  1574. req.QM.numPlayers = (val+1)*2;
  1575. }
  1576. Int numDiscons = 0;
  1577. PerGeneralMap::iterator it;
  1578. for(it =stats.discons.begin(); it != stats.discons.end(); ++it)
  1579. {
  1580. numDiscons += it->second;
  1581. }
  1582. for(it =stats.desyncs.begin(); it != stats.desyncs.end(); ++it)
  1583. {
  1584. numDiscons += it->second;
  1585. }
  1586. req.QM.discons = numDiscons;
  1587. strncpy(req.QM.pings, TheGameSpyInfo->getPingString().str(), 17);
  1588. req.QM.pings[16] = 0;
  1589. req.QM.botID = TheGameSpyConfig->getQMBotID();
  1590. req.QM.roomID = TheGameSpyConfig->getQMChannel();
  1591. req.QM.exeCRC = TheGlobalData->m_exeCRC;
  1592. req.QM.iniCRC = TheGlobalData->m_iniCRC;
  1593. TheGameSpyPeerMessageQueue->addRequest(req);
  1594. buttonWiden->winEnable( FALSE );
  1595. buttonStart->winHide( TRUE );
  1596. buttonStop->winHide( FALSE );
  1597. enableOptionsGadgets(FALSE);
  1598. if (ladderIndex > 0)
  1599. {
  1600. // save the ladder as being played upon even if we cancel out of matching early...
  1601. LadderPreferences ladPref;
  1602. ladPref.loadProfile( TheGameSpyInfo->getLocalProfileID() );
  1603. LadderPref p;
  1604. p.lastPlayDate = time(NULL);
  1605. p.address = ladderInfo->address;
  1606. p.port = ladderInfo->port;
  1607. p.name = ladderInfo->name;
  1608. ladPref.addRecentLadder( p );
  1609. ladPref.write();
  1610. }
  1611. }
  1612. else if ( controlID == buttonBuddiesID )
  1613. {
  1614. GameSpyToggleOverlay( GSOVERLAY_BUDDY );
  1615. }
  1616. else if ( controlID == buttonBackID )
  1617. {
  1618. buttonPushed = true;
  1619. TheGameSpyInfo->leaveGroupRoom();
  1620. nextScreen = "Menus/WOLWelcomeMenu.wnd";
  1621. TheShell->pop();
  1622. } //if ( controlID == buttonBack )
  1623. else if ( controlID == buttonSelectAllMapsID )
  1624. {
  1625. Int numMaps = GadgetListBoxGetNumEntries(listboxMapSelect);
  1626. for ( Int i=0; i<numMaps; ++i )
  1627. {
  1628. GadgetListBoxAddEntryImage(listboxMapSelect, selectedImage, i, 0);
  1629. GadgetListBoxSetItemData(listboxMapSelect, (void *)1, i);
  1630. GadgetListBoxAddEntryText(listboxMapSelect, GadgetListBoxGetText(listboxMapSelect, i, 1), GameSpyColor[GSCOLOR_MAP_SELECTED], i, 1);
  1631. }
  1632. } //if ( controlID == buttonSelectAllMapsID )
  1633. else if ( controlID == buttonSelectNoMapsID )
  1634. {
  1635. Int numMaps = GadgetListBoxGetNumEntries(listboxMapSelect);
  1636. for ( Int i=0; i<numMaps; ++i )
  1637. {
  1638. GadgetListBoxAddEntryImage(listboxMapSelect, unselectedImage, i, 0);
  1639. GadgetListBoxSetItemData(listboxMapSelect, (void *)0, i);
  1640. GadgetListBoxAddEntryText(listboxMapSelect, GadgetListBoxGetText(listboxMapSelect, i, 1), GameSpyColor[GSCOLOR_MAP_UNSELECTED], i, 1);
  1641. }
  1642. } //if ( controlID == buttonSelectNoMapsID )
  1643. break;
  1644. }// case GBM_SELECTED:
  1645. case GLM_SELECTED:
  1646. {
  1647. GameWindow *control = (GameWindow *)mData1;
  1648. Int controlID = control->winGetWindowId();
  1649. Int selected = (Int)mData2;
  1650. if ( controlID == listboxMapSelectID )
  1651. {
  1652. const LadderInfo *li = getLadderInfo();
  1653. if (selected >= 0 && (!li || !li->randomMaps))
  1654. {
  1655. Bool wasSelected = (Bool)GadgetListBoxGetItemData(control, selected, 0);
  1656. GadgetListBoxSetItemData(control, (void *)(!wasSelected), selected, 0);
  1657. Int width = 10;
  1658. Int height = 10;
  1659. const Image *img = (!wasSelected)?selectedImage:unselectedImage;
  1660. if ( img )
  1661. {
  1662. width = min(GadgetListBoxGetColumnWidth(control, 0), img->getImageWidth());
  1663. height = width;
  1664. }
  1665. GadgetListBoxAddEntryImage(control, img, selected, 0, height, width);
  1666. GadgetListBoxAddEntryText(control, GadgetListBoxGetText(control, selected, 1), GameSpyColor[(wasSelected)?GSCOLOR_MAP_UNSELECTED:GSCOLOR_MAP_SELECTED], selected, 1);
  1667. }
  1668. if (selected >= 0)
  1669. GadgetListBoxSetSelected(control, -1);
  1670. }
  1671. UpdateStartButton();
  1672. break;
  1673. }// case GLM_SELECTED
  1674. case GEM_EDIT_DONE:
  1675. {
  1676. break;
  1677. }
  1678. default:
  1679. return MSG_IGNORED;
  1680. }//Switch
  1681. return MSG_HANDLED;
  1682. }// WOLQuickMatchMenuSystem