LobbyUtils.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938
  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: LobbyUtils.cpp
  25. // Author: Matthew D. Campbell, Sept 2002
  26. // Description: GameSpy lobby utils
  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/MultiplayerSettings.h"
  32. #include "Common/PlayerTemplate.h"
  33. #include "Common/Version.h"
  34. #include "GameClient/AnimateWindowManager.h"
  35. #include "GameClient/WindowLayout.h"
  36. #include "GameClient/Gadget.h"
  37. #include "GameClient/Image.h"
  38. #include "GameClient/Shell.h"
  39. #include "GameClient/KeyDefs.h"
  40. #include "GameClient/GameWindowManager.h"
  41. #include "GameClient/GadgetComboBox.h"
  42. #include "GameClient/GadgetListBox.h"
  43. #include "GameClient/GadgetTextEntry.h"
  44. #include "GameClient/GameText.h"
  45. #include "GameClient/MapUtil.h"
  46. #include "GameClient/MessageBox.h"
  47. #include "GameClient/Mouse.h"
  48. #include "GameNetwork/GameSpyOverlay.h"
  49. #include "GameClient/LanguageFilter.h"
  50. #include "GameNetwork/GameSpy/BuddyDefs.h"
  51. #include "GameNetwork/GameSpy/LadderDefs.h"
  52. #include "GameNetwork/GameSpy/LobbyUtils.h"
  53. #include "GameNetwork/GameSpy/PeerDefs.h"
  54. #include "GameNetwork/GameSpy/PeerThread.h"
  55. #include "GameNetwork/GameSpy/PersistentStorageDefs.h"
  56. #include "GameNetwork/GameSpy/GSConfig.h"
  57. #include "Common/STLTypedefs.h"
  58. #ifdef _INTERNAL
  59. // for occasional debugging...
  60. //#pragma optimize("", off)
  61. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  62. #endif
  63. // PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
  64. // Note: if you add more columns, you must modify the .wnd files and change the listbox properties (yuck!)
  65. static enum {
  66. COLUMN_NAME = 0,
  67. COLUMN_MAP,
  68. COLUMN_LADDER,
  69. COLUMN_NUMPLAYERS,
  70. COLUMN_PASSWORD,
  71. COLUMN_OBSERVER,
  72. COLUMN_USE_STATS,
  73. COLUMN_PING,
  74. };
  75. static NameKeyType buttonSortAlphaID = NAMEKEY_INVALID;
  76. static NameKeyType buttonSortPingID = NAMEKEY_INVALID;
  77. static NameKeyType buttonSortBuddiesID = NAMEKEY_INVALID;
  78. static NameKeyType windowSortAlphaID = NAMEKEY_INVALID;
  79. static NameKeyType windowSortPingID = NAMEKEY_INVALID;
  80. static NameKeyType windowSortBuddiesID = NAMEKEY_INVALID;
  81. static GameWindow *buttonSortAlpha = NULL;
  82. static GameWindow *buttonSortPing = NULL;
  83. static GameWindow *buttonSortBuddies = NULL;
  84. static GameWindow *windowSortAlpha = NULL;
  85. static GameWindow *windowSortPing = NULL;
  86. static GameWindow *windowSortBuddies = NULL;
  87. static GameSortType theGameSortType = GAMESORT_ALPHA_ASCENDING;
  88. static Bool sortBuddies = TRUE;
  89. static void showSortIcons(void)
  90. {
  91. if (windowSortAlpha && windowSortPing)
  92. {
  93. switch(theGameSortType)
  94. {
  95. case GAMESORT_ALPHA_ASCENDING:
  96. windowSortAlpha->winHide(FALSE);
  97. windowSortAlpha->winEnable(TRUE);
  98. windowSortPing->winHide(TRUE);
  99. break;
  100. case GAMESORT_ALPHA_DESCENDING:
  101. windowSortAlpha->winHide(FALSE);
  102. windowSortAlpha->winEnable(FALSE);
  103. windowSortPing->winHide(TRUE);
  104. break;
  105. case GAMESORT_PING_ASCENDING:
  106. windowSortPing->winHide(FALSE);
  107. windowSortPing->winEnable(TRUE);
  108. windowSortAlpha->winHide(TRUE);
  109. break;
  110. case GAMESORT_PING_DESCENDING:
  111. windowSortPing->winHide(FALSE);
  112. windowSortPing->winEnable(FALSE);
  113. windowSortAlpha->winHide(TRUE);
  114. break;
  115. }
  116. }
  117. if (sortBuddies)
  118. {
  119. if (windowSortBuddies)
  120. {
  121. windowSortBuddies->winHide(FALSE);
  122. }
  123. }
  124. else
  125. {
  126. if (windowSortBuddies)
  127. {
  128. windowSortBuddies->winHide(TRUE);
  129. }
  130. }
  131. }
  132. void setSortMode( GameSortType sortType ) { theGameSortType = sortType; showSortIcons(); RefreshGameListBoxes(); }
  133. void sortByBuddies( Bool doSort ) { sortBuddies = doSort; showSortIcons(); RefreshGameListBoxes(); }
  134. Bool HandleSortButton( NameKeyType sortButton )
  135. {
  136. if (sortButton == buttonSortBuddiesID)
  137. {
  138. sortByBuddies( !sortBuddies );
  139. return TRUE;
  140. }
  141. else if (sortButton == buttonSortAlphaID)
  142. {
  143. if (theGameSortType == GAMESORT_ALPHA_ASCENDING)
  144. {
  145. setSortMode(GAMESORT_ALPHA_DESCENDING);
  146. }
  147. else
  148. {
  149. setSortMode(GAMESORT_ALPHA_ASCENDING);
  150. }
  151. return TRUE;
  152. }
  153. else if (sortButton == buttonSortPingID)
  154. {
  155. if (theGameSortType == GAMESORT_PING_ASCENDING)
  156. {
  157. setSortMode(GAMESORT_PING_DESCENDING);
  158. }
  159. else
  160. {
  161. setSortMode(GAMESORT_PING_ASCENDING);
  162. }
  163. return TRUE;
  164. }
  165. return FALSE;
  166. }
  167. // window ids ------------------------------------------------------------------------------
  168. static NameKeyType parentID = NAMEKEY_INVALID;
  169. //static NameKeyType parentGameListSmallID = NAMEKEY_INVALID;
  170. static NameKeyType parentGameListLargeID = NAMEKEY_INVALID;
  171. static NameKeyType listboxLobbyGamesSmallID = NAMEKEY_INVALID;
  172. static NameKeyType listboxLobbyGamesLargeID = NAMEKEY_INVALID;
  173. //static NameKeyType listboxLobbyGameInfoID = NAMEKEY_INVALID;
  174. // Window Pointers ------------------------------------------------------------------------
  175. static GameWindow *parent = NULL;
  176. //static GameWindow *parentGameListSmall = NULL;
  177. static GameWindow *parentGameListLarge = NULL;
  178. //GameWindow *listboxLobbyGamesSmall = NULL;
  179. GameWindow *listboxLobbyGamesLarge = NULL;
  180. //GameWindow *listboxLobbyGameInfo = NULL;
  181. static const Image *pingImages[3] = { NULL, NULL, NULL };
  182. static void gameTooltip(GameWindow *window,
  183. WinInstanceData *instData,
  184. UnsignedInt mouse)
  185. {
  186. Int x, y, row, col;
  187. x = LOLONGTOSHORT(mouse);
  188. y = HILONGTOSHORT(mouse);
  189. GadgetListBoxGetEntryBasedOnXY(window, x, y, row, col);
  190. if (row == -1 || col == -1)
  191. {
  192. TheMouse->setCursorTooltip( UnicodeString::TheEmptyString);//TheGameText->fetch("TOOLTIP:GamesBeingFormed") );
  193. return;
  194. }
  195. Int gameID = (Int)GadgetListBoxGetItemData(window, row, 0);
  196. GameSpyStagingRoom *room = TheGameSpyInfo->findStagingRoomByID(gameID);
  197. if (!room)
  198. {
  199. TheMouse->setCursorTooltip( TheGameText->fetch("TOOLTIP:UnknownGame") );
  200. return;
  201. }
  202. if (col == COLUMN_PING)
  203. {
  204. #if 0 //def DEBUG_LOGGING
  205. UnicodeString s;
  206. s.format(L"Ping is %d ms (cutoffs are %d ms and %d ms\n%hs local pings\n%hs remote pings",
  207. room->getPingAsInt(), TheGameSpyConfig->getPingCutoffGood(), TheGameSpyConfig->getPingCutoffBad(),
  208. TheGameSpyInfo->getPingString().str(), room->getPingString().str()
  209. );
  210. TheMouse->setCursorTooltip( s, 10, NULL, 2.0f ); // the text and width are the only params used. the others are the default values.
  211. #else
  212. TheMouse->setCursorTooltip( TheGameText->fetch("TOOLTIP:PingInfo"), 10, NULL, 2.0f ); // the text and width are the only params used. the others are the default values.
  213. #endif
  214. return;
  215. }
  216. if (col == COLUMN_NUMPLAYERS)
  217. {
  218. TheMouse->setCursorTooltip( TheGameText->fetch("TOOLTIP:NumberOfPlayers"), 10, NULL, 2.0f ); // the text and width are the only params used. the others are the default values.
  219. return;
  220. }
  221. if (col == COLUMN_PASSWORD)
  222. {
  223. if (room->getHasPassword())
  224. {
  225. UnicodeString checkTooltip =TheGameText->fetch("TOOTIP:Password");
  226. if(!checkTooltip.compare(L"Password required to joing game"))
  227. checkTooltip.set(L"Password required to join game");
  228. TheMouse->setCursorTooltip( checkTooltip, 10, NULL, 2.0f ); // the text and width are the only params used. the others are the default values.
  229. }
  230. else
  231. TheMouse->setCursorTooltip( UnicodeString::TheEmptyString );
  232. return;
  233. }
  234. if (col == COLUMN_USE_STATS)
  235. {
  236. if ( room->getUseStats() )
  237. {
  238. TheMouse->setCursorTooltip( TheGameText->fetch("TOOLTIP:UseStatsOn") );
  239. }
  240. else
  241. {
  242. TheMouse->setCursorTooltip( TheGameText->fetch("TOOLTIP:UseStatsOff") );
  243. }
  244. return;
  245. }
  246. UnicodeString tooltip;
  247. UnicodeString mapName;
  248. const MapMetaData *md = TheMapCache->findMap(room->getMap());
  249. if (md)
  250. {
  251. mapName = md->m_displayName;
  252. }
  253. else
  254. {
  255. const char *start = room->getMap().reverseFind('\\');
  256. if (start)
  257. {
  258. ++start;
  259. }
  260. else
  261. {
  262. start = room->getMap().str();
  263. }
  264. mapName.translate( start );
  265. }
  266. UnicodeString tmp;
  267. tooltip.format(TheGameText->fetch("TOOLTIP:GameInfoGameName"), room->getGameName().str());
  268. if (room->getLadderPort() != 0)
  269. {
  270. const LadderInfo *linfo = TheLadderList->findLadder(room->getLadderIP(), room->getLadderPort());
  271. if (linfo)
  272. {
  273. tmp.format(TheGameText->fetch("TOOLTIP:GameInfoLadderName"), linfo->name.str());
  274. tooltip.concat(tmp);
  275. }
  276. }
  277. if (room->getExeCRC() != TheGlobalData->m_exeCRC || room->getIniCRC() != TheGlobalData->m_iniCRC)
  278. {
  279. tmp.format(TheGameText->fetch("TOOLTIP:InvalidGameVersion"), mapName.str());
  280. tooltip.concat(tmp);
  281. }
  282. tmp.format(TheGameText->fetch("TOOLTIP:GameInfoMap"), mapName.str());
  283. tooltip.concat(tmp);
  284. AsciiString aPlayer;
  285. UnicodeString player;
  286. Int numPlayers = 0;
  287. for (Int i=0; i<MAX_SLOTS; ++i)
  288. {
  289. GameSpyGameSlot *slot = room->getGameSpySlot(i);
  290. if (i == 0 && (!slot || !slot->isHuman()))
  291. {
  292. DEBUG_CRASH(("About to tooltip a non-hosted game!\n"));
  293. }
  294. if (slot && slot->isHuman())
  295. {
  296. tmp.format(TheGameText->fetch("TOOLTIP:GameInfoPlayer"), slot->getName().str(), slot->getWins(), slot->getLosses());
  297. tooltip.concat(tmp);
  298. ++numPlayers;
  299. }
  300. else if (slot && slot->isAI())
  301. {
  302. ++numPlayers;
  303. switch(slot->getState())
  304. {
  305. case SLOT_EASY_AI:
  306. tooltip.concat(L'\n');
  307. tooltip.concat(TheGameText->fetch("GUI:EasyAI"));
  308. break;
  309. case SLOT_MED_AI:
  310. tooltip.concat(L'\n');
  311. tooltip.concat(TheGameText->fetch("GUI:MediumAI"));
  312. break;
  313. case SLOT_BRUTAL_AI:
  314. tooltip.concat(L'\n');
  315. tooltip.concat(TheGameText->fetch("GUI:HardAI"));
  316. break;
  317. }
  318. }
  319. }
  320. DEBUG_ASSERTCRASH(numPlayers, ("Tooltipping a 0-player game!\n"));
  321. TheMouse->setCursorTooltip( tooltip, 10, NULL, 2.0f ); // the text and width are the only params used. the others are the default values.
  322. }
  323. static Bool isSmall = TRUE;
  324. GameWindow *GetGameListBox( void )
  325. {
  326. return listboxLobbyGamesLarge;
  327. }
  328. GameWindow *GetGameInfoListBox( void )
  329. {
  330. return NULL;
  331. }
  332. NameKeyType GetGameListBoxID( void )
  333. {
  334. return listboxLobbyGamesLargeID;
  335. }
  336. NameKeyType GetGameInfoListBoxID( void )
  337. {
  338. return NAMEKEY_INVALID;
  339. }
  340. void GrabWindowInfo( void )
  341. {
  342. isSmall = TRUE;
  343. parentID = NAMEKEY( "WOLCustomLobby.wnd:WOLLobbyMenuParent" );
  344. parent = TheWindowManager->winGetWindowFromId(NULL, parentID);
  345. pingImages[0] = TheMappedImageCollection->findImageByName("Ping03");
  346. pingImages[1] = TheMappedImageCollection->findImageByName("Ping02");
  347. pingImages[2] = TheMappedImageCollection->findImageByName("Ping01");
  348. DEBUG_ASSERTCRASH(pingImages[0], ("Can't find ping image!"));
  349. DEBUG_ASSERTCRASH(pingImages[1], ("Can't find ping image!"));
  350. DEBUG_ASSERTCRASH(pingImages[2], ("Can't find ping image!"));
  351. // parentGameListSmallID = NAMEKEY( "WOLCustomLobby.wnd:ParentGameListSmall" );
  352. // parentGameListSmall = TheWindowManager->winGetWindowFromId(NULL, parentGameListSmallID);
  353. parentGameListLargeID = NAMEKEY( "WOLCustomLobby.wnd:ParentGameListLarge" );
  354. parentGameListLarge = TheWindowManager->winGetWindowFromId(NULL, parentGameListLargeID);
  355. listboxLobbyGamesSmallID = NAMEKEY( "WOLCustomLobby.wnd:ListboxGames" );
  356. // listboxLobbyGamesSmall = TheWindowManager->winGetWindowFromId(NULL, listboxLobbyGamesSmallID);
  357. // listboxLobbyGamesSmall->winSetTooltipFunc(gameTooltip);
  358. listboxLobbyGamesLargeID = NAMEKEY( "WOLCustomLobby.wnd:ListboxGamesLarge" );
  359. listboxLobbyGamesLarge = TheWindowManager->winGetWindowFromId(NULL, listboxLobbyGamesLargeID);
  360. listboxLobbyGamesLarge->winSetTooltipFunc(gameTooltip);
  361. //
  362. // listboxLobbyGameInfoID = NAMEKEY( "WOLCustomLobby.wnd:ListboxGameInfo" );
  363. // listboxLobbyGameInfo = TheWindowManager->winGetWindowFromId(NULL, listboxLobbyGameInfoID);
  364. buttonSortAlphaID = NAMEKEY("WOLCustomLobby.wnd:ButtonSortAlpha");
  365. buttonSortPingID = NAMEKEY("WOLCustomLobby.wnd:ButtonSortPing");
  366. buttonSortBuddiesID = NAMEKEY("WOLCustomLobby.wnd:ButtonSortBuddies");
  367. windowSortAlphaID = NAMEKEY("WOLCustomLobby.wnd:WindowSortAlpha");
  368. windowSortPingID = NAMEKEY("WOLCustomLobby.wnd:WindowSortPing");
  369. windowSortBuddiesID = NAMEKEY("WOLCustomLobby.wnd:WindowSortBuddies");
  370. buttonSortAlpha = TheWindowManager->winGetWindowFromId(parent, buttonSortAlphaID);
  371. buttonSortPing = TheWindowManager->winGetWindowFromId(parent, buttonSortPingID);
  372. buttonSortBuddies = TheWindowManager->winGetWindowFromId(parent, buttonSortBuddiesID);
  373. windowSortAlpha = TheWindowManager->winGetWindowFromId(parent, windowSortAlphaID);
  374. windowSortPing = TheWindowManager->winGetWindowFromId(parent, windowSortPingID);
  375. windowSortBuddies = TheWindowManager->winGetWindowFromId(parent, windowSortBuddiesID);
  376. showSortIcons();
  377. }
  378. void ReleaseWindowInfo( void )
  379. {
  380. isSmall = TRUE;
  381. parent = NULL;
  382. // parentGameListSmall = NULL;
  383. parentGameListLarge = NULL;
  384. // listboxLobbyGamesSmall = NULL;
  385. listboxLobbyGamesLarge = NULL;
  386. // listboxLobbyGameInfo = NULL;
  387. buttonSortAlpha = NULL;
  388. buttonSortPing = NULL;
  389. buttonSortBuddies = NULL;
  390. windowSortAlpha = NULL;
  391. windowSortPing = NULL;
  392. windowSortBuddies = NULL;
  393. }
  394. typedef std::set<GameSpyStagingRoom *> BuddyGameSet;
  395. static BuddyGameSet *theBuddyGames = NULL;
  396. static void populateBuddyGames(void)
  397. {
  398. BuddyInfoMap *m = TheGameSpyInfo->getBuddyMap();
  399. theBuddyGames = NEW BuddyGameSet;
  400. if (!m)
  401. {
  402. return;
  403. }
  404. for (BuddyInfoMap::const_iterator bit = m->begin(); bit != m->end(); ++bit)
  405. {
  406. BuddyInfo info = bit->second;
  407. if (info.m_status == GP_STAGING)
  408. {
  409. StagingRoomMap *srm = TheGameSpyInfo->getStagingRoomList();
  410. for (StagingRoomMap::iterator srmIt = srm->begin(); srmIt != srm->end(); ++srmIt)
  411. {
  412. GameSpyStagingRoom *game = srmIt->second;
  413. game->cleanUpSlotPointers();
  414. const GameSpyGameSlot *slot = game->getGameSpySlot(0);
  415. if (slot && slot->getName() == info.m_locationString)
  416. {
  417. theBuddyGames->insert(game);
  418. break;
  419. }
  420. }
  421. }
  422. }
  423. }
  424. static void clearBuddyGames(void)
  425. {
  426. if (theBuddyGames)
  427. delete theBuddyGames;
  428. theBuddyGames = NULL;
  429. }
  430. struct GameSortStruct
  431. {
  432. bool operator()(GameSpyStagingRoom *g1, GameSpyStagingRoom *g2)
  433. {
  434. // sort CRC mismatches to the bottom
  435. Bool g1Good = (g1->getExeCRC() != TheGlobalData->m_exeCRC || g1->getIniCRC() != TheGlobalData->m_iniCRC);
  436. Bool g2Good = (g1->getExeCRC() != TheGlobalData->m_exeCRC || g1->getIniCRC() != TheGlobalData->m_iniCRC);
  437. if ( g1Good ^ g2Good )
  438. {
  439. return g1Good;
  440. }
  441. // sort games with private ladders to the bottom
  442. Bool g1UnknownLadder = (g1->getLadderPort() && TheLadderList->findLadder(g1->getLadderIP(), g1->getLadderPort()) == NULL);
  443. Bool g2UnknownLadder = (g2->getLadderPort() && TheLadderList->findLadder(g2->getLadderIP(), g2->getLadderPort()) == NULL);
  444. if ( g1UnknownLadder ^ g2UnknownLadder )
  445. {
  446. return g2UnknownLadder;
  447. }
  448. // sort full games to the bottom
  449. Bool g1Full = (g1->getNumNonObserverPlayers() == g1->getMaxPlayers() || g1->getNumPlayers() == MAX_SLOTS);
  450. Bool g2Full = (g2->getNumNonObserverPlayers() == g2->getMaxPlayers() || g2->getNumPlayers() == MAX_SLOTS);
  451. if ( g1Full ^ g2Full )
  452. {
  453. return g2Full;
  454. }
  455. if (sortBuddies)
  456. {
  457. Bool g1HasBuddies = (theBuddyGames->find(g1) != theBuddyGames->end());
  458. Bool g2HasBuddies = (theBuddyGames->find(g2) != theBuddyGames->end());
  459. if ( g1HasBuddies ^ g2HasBuddies )
  460. {
  461. return g1HasBuddies;
  462. }
  463. }
  464. switch(theGameSortType)
  465. {
  466. case GAMESORT_ALPHA_ASCENDING:
  467. return wcsicmp(g1->getGameName().str(), g2->getGameName().str()) < 0;
  468. break;
  469. case GAMESORT_ALPHA_DESCENDING:
  470. return wcsicmp(g1->getGameName().str(),g2->getGameName().str()) > 0;
  471. break;
  472. case GAMESORT_PING_ASCENDING:
  473. return g1->getPingAsInt() < g2->getPingAsInt();
  474. break;
  475. case GAMESORT_PING_DESCENDING:
  476. return g1->getPingAsInt() > g2->getPingAsInt();
  477. break;
  478. }
  479. return false;
  480. }
  481. };
  482. static Int insertGame( GameWindow *win, GameSpyStagingRoom *game, Bool showMap )
  483. {
  484. game->cleanUpSlotPointers();
  485. Color gameColor = GameSpyColor[GSCOLOR_GAME];
  486. if (game->getNumNonObserverPlayers() == game->getMaxPlayers() || game->getNumPlayers() == MAX_SLOTS)
  487. {
  488. gameColor = GameSpyColor[GSCOLOR_GAME_FULL];
  489. }
  490. if (game->getExeCRC() != TheGlobalData->m_exeCRC || game->getIniCRC() != TheGlobalData->m_iniCRC)
  491. {
  492. gameColor = GameSpyColor[GSCOLOR_GAME_CRCMISMATCH];
  493. }
  494. UnicodeString gameName = game->getGameName();
  495. if(TheGameSpyInfo->getDisallowAsianText())
  496. {
  497. const WideChar *buff = gameName.str();
  498. Int length = gameName.getLength();
  499. for(Int i = 0; i < length; ++i)
  500. {
  501. if(buff[i] >= 256)
  502. return -1;
  503. }
  504. }
  505. else if(TheGameSpyInfo->getDisallowNonAsianText())
  506. {
  507. const WideChar *buff = gameName.str();
  508. Int length = gameName.getLength();
  509. Bool hasUnicode = FALSE;
  510. for(Int i = 0; i < length; ++i)
  511. {
  512. if(buff[i] >= 256)
  513. {
  514. hasUnicode = TRUE;
  515. break;
  516. }
  517. }
  518. if(!hasUnicode)
  519. return -1;
  520. }
  521. Int index = GadgetListBoxAddEntryText(win, game->getGameName(), gameColor, -1, COLUMN_NAME);
  522. GadgetListBoxSetItemData(win, (void *)game->getID(), index);
  523. UnicodeString s;
  524. if (showMap)
  525. {
  526. UnicodeString mapName;
  527. const MapMetaData *md = TheMapCache->findMap(game->getMap());
  528. if (md)
  529. {
  530. mapName = md->m_displayName;
  531. }
  532. else
  533. {
  534. const char *start = game->getMap().reverseFind('\\');
  535. if (start)
  536. {
  537. ++start;
  538. }
  539. else
  540. {
  541. start = game->getMap().str();
  542. }
  543. mapName.translate( start );
  544. }
  545. GadgetListBoxAddEntryText(win, mapName, gameColor, index, COLUMN_MAP);
  546. const LadderInfo * li = TheLadderList->findLadder(game->getLadderIP(), game->getLadderPort());
  547. if (li)
  548. {
  549. GadgetListBoxAddEntryText(win, li->name, gameColor, index, COLUMN_LADDER);
  550. }
  551. else if (game->getLadderPort())
  552. {
  553. GadgetListBoxAddEntryText(win, TheGameText->fetch("GUI:UnknownLadder"), gameColor, index, COLUMN_LADDER);
  554. }
  555. else
  556. {
  557. GadgetListBoxAddEntryText(win, TheGameText->fetch("GUI:NoLadder"), gameColor, index, COLUMN_LADDER);
  558. }
  559. }
  560. else
  561. {
  562. GadgetListBoxAddEntryText(win, UnicodeString(L" "), gameColor, index, COLUMN_MAP);
  563. GadgetListBoxAddEntryText(win, UnicodeString(L" "), gameColor, index, COLUMN_LADDER);
  564. }
  565. s.format(L"%d/%d", game->getReportedNumPlayers(), game->getReportedMaxPlayers());
  566. GadgetListBoxAddEntryText(win, s, gameColor, index, COLUMN_NUMPLAYERS);
  567. if (game->getHasPassword())
  568. {
  569. const Image *img = TheMappedImageCollection->findImageByName("Password");
  570. Int width = 10, height = 10;
  571. if (img)
  572. {
  573. width = img->getImageWidth();
  574. height = img->getImageHeight();
  575. }
  576. GadgetListBoxAddEntryImage(win, img, index, COLUMN_PASSWORD, width, height);
  577. }
  578. else
  579. {
  580. GadgetListBoxAddEntryText(win, UnicodeString(L" "), gameColor, index, COLUMN_PASSWORD);
  581. }
  582. if (game->getAllowObservers())
  583. {
  584. const Image *img = TheMappedImageCollection->findImageByName("Observer");
  585. GadgetListBoxAddEntryImage(win, img, index, COLUMN_OBSERVER);
  586. }
  587. else
  588. {
  589. GadgetListBoxAddEntryText(win, UnicodeString(L" "), gameColor, index, COLUMN_OBSERVER);
  590. }
  591. {
  592. if (game->getUseStats())
  593. {
  594. const Image *img = TheMappedImageCollection->findImageByName("GoodStatsIcon");
  595. GadgetListBoxAddEntryImage(win, img, index, COLUMN_USE_STATS, img->getImageHeight(), img->getImageWidth());
  596. }
  597. }
  598. s.format(L"%d", game->getPingAsInt());
  599. GadgetListBoxAddEntryText(win, s, gameColor, index, COLUMN_PING);
  600. Int ping = game->getPingAsInt();
  601. Int width = 10, height = 10;
  602. if (pingImages[0])
  603. {
  604. width = pingImages[0]->getImageWidth();
  605. height = pingImages[0]->getImageHeight();
  606. }
  607. // CLH picking an arbitrary number for our ping display
  608. if (ping < TheGameSpyConfig->getPingCutoffGood())
  609. {
  610. GadgetListBoxAddEntryImage(win, pingImages[0], index, COLUMN_PING, width, height);
  611. }
  612. else if (ping < TheGameSpyConfig->getPingCutoffBad())
  613. {
  614. GadgetListBoxAddEntryImage(win, pingImages[1], index, COLUMN_PING, width, height);
  615. }
  616. else
  617. {
  618. GadgetListBoxAddEntryImage(win, pingImages[2], index, COLUMN_PING, width, height);
  619. }
  620. return index;
  621. }
  622. void RefreshGameListBox( GameWindow *win, Bool showMap )
  623. {
  624. if (!win)
  625. return;
  626. // save off selection
  627. Int selectedIndex = -1;
  628. Int indexToSelect = -1;
  629. Int selectedID = 0;
  630. GadgetListBoxGetSelected(win, &selectedIndex);
  631. if (selectedIndex != -1 )
  632. {
  633. selectedID = (Int)GadgetListBoxGetItemData(win, selectedIndex);
  634. }
  635. int prevPos = GadgetListBoxGetTopVisibleEntry( win );
  636. // empty listbox
  637. GadgetListBoxReset(win);
  638. // sort our games
  639. typedef std::multiset<GameSpyStagingRoom *, GameSortStruct> SortedGameList;
  640. SortedGameList sgl;
  641. StagingRoomMap *srm = TheGameSpyInfo->getStagingRoomList();
  642. populateBuddyGames();
  643. for (StagingRoomMap::iterator srmIt = srm->begin(); srmIt != srm->end(); ++srmIt)
  644. {
  645. sgl.insert(srmIt->second);
  646. }
  647. // populate listbox
  648. for (SortedGameList::iterator sglIt = sgl.begin(); sglIt != sgl.end(); ++sglIt)
  649. {
  650. GameSpyStagingRoom *game = *sglIt;
  651. if (game)
  652. {
  653. Int index = insertGame(win, game, showMap);
  654. if (game->getID() == selectedID)
  655. {
  656. indexToSelect = index;
  657. }
  658. }
  659. }
  660. clearBuddyGames();
  661. // restore selection
  662. GadgetListBoxSetSelected(win, indexToSelect); // even for -1, so we can disable the 'Join Game' button
  663. // if(prevPos > 10)
  664. GadgetListBoxSetTopVisibleEntry( win, prevPos );//+ 1
  665. if (indexToSelect < 0 && selectedID)
  666. {
  667. TheWindowManager->winSetLoneWindow(NULL);
  668. }
  669. }
  670. void RefreshGameInfoListBox( GameWindow *mainWin, GameWindow *win )
  671. {
  672. // if (!mainWin || !win)
  673. // return;
  674. //
  675. // GadgetListBoxReset(win);
  676. //
  677. // Int selected = -1;
  678. // GadgetListBoxGetSelected(mainWin, &selected);
  679. // if (selected < 0)
  680. // {
  681. // return;
  682. // }
  683. //
  684. // Int selectedID = (Int)GadgetListBoxGetItemData(mainWin, selected);
  685. // if (selectedID < 0)
  686. // {
  687. // return;
  688. // }
  689. //
  690. // StagingRoomMap *srm = TheGameSpyInfo->getStagingRoomList();
  691. // StagingRoomMap::iterator srmIt = srm->find(selectedID);
  692. // if (srmIt != srm->end())
  693. // {
  694. // GameSpyStagingRoom *theRoom = srmIt->second;
  695. // theRoom->cleanUpSlotPointers();
  696. //
  697. // // game name
  698. //// GadgetListBoxAddEntryText(listboxLobbyGameInfo, theRoom->getGameName(), GameSpyColor[GSCOLOR_DEFAULT], -1);
  699. //
  700. // const LadderInfo * li = TheLadderList->findLadder(theRoom->getLadderIP(), theRoom->getLadderPort());
  701. // if (li)
  702. // {
  703. // UnicodeString tmp;
  704. // tmp.format(TheGameText->fetch("TOOLTIP:LadderName"), li->name.str());
  705. // GadgetListBoxAddEntryText(listboxLobbyGameInfo, tmp, GameSpyColor[GSCOLOR_DEFAULT], -1);
  706. // }
  707. // else if (theRoom->getLadderPort())
  708. // {
  709. // GadgetListBoxAddEntryText(listboxLobbyGameInfo, TheGameText->fetch("TOOLTIP:UnknownLadder"), GameSpyColor[GSCOLOR_DEFAULT], -1);
  710. // }
  711. // else
  712. // {
  713. // GadgetListBoxAddEntryText(listboxLobbyGameInfo, TheGameText->fetch("TOOLTIP:NoLadder"), GameSpyColor[GSCOLOR_DEFAULT], -1);
  714. // }
  715. //
  716. // if (theRoom->getExeCRC() != TheGlobalData->m_exeCRC || theRoom->getIniCRC() != TheGlobalData->m_iniCRC)
  717. // {
  718. // GadgetListBoxAddEntryText(listboxLobbyGameInfo, TheGameText->fetch("TOOLTIP:InvalidGameVersionSingleLine"), GameSpyColor[GSCOLOR_DEFAULT], -1);
  719. // }
  720. //
  721. // // map name
  722. // UnicodeString mapName;
  723. // const MapMetaData *md = TheMapCache->findMap(theRoom->getMap());
  724. // if (md)
  725. // {
  726. // mapName = md->m_displayName;
  727. // }
  728. // else
  729. // {
  730. // const char *start = theRoom->getMap().reverseFind('\\');
  731. // if (start)
  732. // {
  733. // ++start;
  734. // }
  735. // else
  736. // {
  737. // start = theRoom->getMap().str();
  738. // }
  739. // mapName.translate( start );
  740. // }
  741. //
  742. // GadgetListBoxAddEntryText(listboxLobbyGameInfo, mapName, GameSpyColor[GSCOLOR_DEFAULT], -1);
  743. //
  744. // // player list (rank, win/loss, side)
  745. // for (Int i=0; i<MAX_SLOTS; ++i)
  746. // {
  747. // const GameSpyGameSlot *slot = theRoom->getGameSpySlot(i);
  748. // if (slot && slot->isHuman())
  749. // {
  750. // UnicodeString theName, theRating, thePlayerTemplate;
  751. // Int colorIdx = slot->getColor();
  752. // theName = slot->getName();
  753. // theRating.format(L" (%d-%d)", slot->getWins(), slot->getLosses());
  754. // const PlayerTemplate * pt = ThePlayerTemplateStore->getNthPlayerTemplate(slot->getPlayerTemplate());
  755. // if (pt)
  756. // {
  757. // thePlayerTemplate = pt->getDisplayName();
  758. // }
  759. // else
  760. // {
  761. // thePlayerTemplate = TheGameText->fetch("GUI:Random");
  762. // }
  763. //
  764. // UnicodeString theText;
  765. // theText.format(L"%ls - %ls - %ls", theName.str(), thePlayerTemplate.str(), theRating.str());
  766. //
  767. // Int theColor = GameSpyColor[GSCOLOR_DEFAULT];
  768. // const MultiplayerColorDefinition *mcd = TheMultiplayerSettings->getColor(colorIdx);
  769. // if (mcd)
  770. // {
  771. // theColor = mcd->getColor();
  772. // }
  773. //
  774. // GadgetListBoxAddEntryText(listboxLobbyGameInfo, theText, theColor, -1);
  775. // }
  776. // }
  777. // }
  778. }
  779. void RefreshGameListBoxes( void )
  780. {
  781. GameWindow *main = GetGameListBox();
  782. GameWindow *info = GetGameInfoListBox();
  783. RefreshGameListBox( main, (info == NULL) );
  784. if (info)
  785. {
  786. RefreshGameInfoListBox( main, info );
  787. }
  788. }
  789. void ToggleGameListType( void )
  790. {
  791. isSmall = !isSmall;
  792. if(isSmall)
  793. {
  794. parentGameListLarge->winHide(TRUE);
  795. // parentGameListSmall->winHide(FALSE);
  796. }
  797. else
  798. {
  799. parentGameListLarge->winHide(FALSE);
  800. // parentGameListSmall->winHide(TRUE);
  801. }
  802. RefreshGameListBoxes();
  803. }
  804. // for use by GameWindow::winSetTooltipFunc
  805. // displays the Army Tooltip for the player templates
  806. void playerTemplateComboBoxTooltip(GameWindow *wndComboBox, WinInstanceData *instData, UnsignedInt mouse)
  807. {
  808. Int index = 0;
  809. GadgetComboBoxGetSelectedPos(wndComboBox, &index);
  810. Int templateNum = (Int)GadgetComboBoxGetItemData(wndComboBox, index);
  811. UnicodeString ustringTooltip;
  812. if (templateNum == -1)
  813. {
  814. // the "Random" template is always first
  815. ustringTooltip = TheGameText->fetch("TOOLTIP:BioStrategyLong_Random");
  816. }
  817. else
  818. {
  819. const PlayerTemplate *playerTemplate = ThePlayerTemplateStore->getNthPlayerTemplate(templateNum);
  820. if (playerTemplate)
  821. {
  822. ustringTooltip = TheGameText->fetch(playerTemplate->getTooltip());
  823. }
  824. }
  825. TheMouse->setCursorTooltip(ustringTooltip);
  826. }
  827. // -----------------------------------------------------------------------------
  828. // for use by GameWindow::winSetTooltipFunc
  829. // displays the Army Tooltip for the player templates
  830. void playerTemplateListBoxTooltip(GameWindow *wndListBox, WinInstanceData *instData, UnsignedInt mouse)
  831. {
  832. Int x, y, row, col;
  833. x = LOLONGTOSHORT(mouse);
  834. y = HILONGTOSHORT(mouse);
  835. GadgetListBoxGetEntryBasedOnXY(wndListBox, x, y, row, col);
  836. if (row == -1 || col == -1)
  837. return;
  838. Int templateNum = (Int)GadgetListBoxGetItemData(wndListBox, row, col);
  839. UnicodeString ustringTooltip;
  840. if (templateNum == -1)
  841. {
  842. // the "Random" template is always first
  843. ustringTooltip = TheGameText->fetch("TOOLTIP:BioStrategyLong_Random");
  844. }
  845. else
  846. {
  847. const PlayerTemplate *playerTemplate = ThePlayerTemplateStore->getNthPlayerTemplate(templateNum);
  848. if (playerTemplate)
  849. {
  850. ustringTooltip = TheGameText->fetch(playerTemplate->getTooltip());
  851. }
  852. }
  853. // use no tooltip delay here
  854. TheMouse->setCursorTooltip(ustringTooltip, 0);
  855. }