/* ** Command & Conquer Generals(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: PopupLadderSelect.cpp //////////////////////////////////////////////// //----------------------------------------------------------------------------- // // Electronic Arts Pacific. // // Confidential Information // Copyright (C) 2002 - All Rights Reserved // //----------------------------------------------------------------------------- // // created: August 2002 // // Filename: PopupLadderSelect.cpp // // author: Matthew D. Campbell // // purpose: Contains the Callbacks for the Ladder Select Popup // //----------------------------------------------------------------------------- /////////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- // SYSTEM INCLUDES //////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // USER INCLUDES ////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine #include "Common/GlobalData.h" #include "Common/Encrypt.h" #include "Common/NameKeyGenerator.h" #include "GameClient/WindowLayout.h" #include "GameClient/Gadget.h" #include "GameClient/GameText.h" #include "GameClient/KeyDefs.h" #include "GameClient/MapUtil.h" #include "GameClient/GadgetListBox.h" #include "GameClient/GadgetStaticText.h" #include "GameClient/GadgetTextEntry.h" #include "GameNetwork/GameSpy/LadderDefs.h" #include "GameNetwork/GameSpy/PeerDefs.h" //#include "GameNetwork/GameSpy/PeerThread.h" #include "GameNetwork/GameSpyOverlay.h" #ifdef _INTERNAL // for occasional debugging... //#pragma optimize("", off) //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes") #endif //----------------------------------------------------------------------------- // DEFINES //////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- static NameKeyType parentID = NAMEKEY_INVALID; static NameKeyType listboxLadderSelectID = NAMEKEY_INVALID; static NameKeyType listboxLadderDetailsID = NAMEKEY_INVALID; static NameKeyType staticTextLadderNameID = NAMEKEY_INVALID; static NameKeyType buttonOkID = NAMEKEY_INVALID; static NameKeyType buttonCancelID = NAMEKEY_INVALID; static GameWindow *parent = NULL; static GameWindow *listboxLadderSelect = NULL; static GameWindow *listboxLadderDetails = NULL; static GameWindow *staticTextLadderName = NULL; static GameWindow *buttonOk = NULL; static GameWindow *buttonCancel = NULL; // password entry popup static NameKeyType passwordParentID = NAMEKEY_INVALID; static NameKeyType buttonPasswordOkID = NAMEKEY_INVALID; static NameKeyType buttonPasswordCancelID = NAMEKEY_INVALID; static NameKeyType textEntryPasswordID = NAMEKEY_INVALID; static GameWindow *passwordParent = NULL; static GameWindow *textEntryPassword = NULL; // incorrect password popup static NameKeyType badPasswordParentID = NAMEKEY_INVALID; static NameKeyType buttonBadPasswordOkID = NAMEKEY_INVALID; static GameWindow *badPasswordParent = NULL; static void updateLadderDetails( Int ladderID, GameWindow *staticTextLadderName, GameWindow *listboxLadderDetails ); void PopulateQMLadderComboBox( void ); void PopulateCustomLadderComboBox( void ); void PopulateQMLadderListBox( GameWindow *win ); void PopulateCustomLadderListBox( GameWindow *win ); void HandleQMLadderSelection(Int ladderID); void HandleCustomLadderSelection(Int ladderID); void CustomMatchHideHostPopup(Bool hide); static void populateLadderComboBox( void ) { // only one of these will do any work... PopulateQMLadderComboBox(); PopulateCustomLadderComboBox(); } static void populateLadderListBox( void ) { // only one of these will do any work... PopulateQMLadderListBox(listboxLadderSelect); PopulateCustomLadderListBox(listboxLadderSelect); Int selIndex, selID; GadgetListBoxGetSelected(listboxLadderSelect, &selIndex); if (selIndex < 0) return; selID = (Int)GadgetListBoxGetItemData(listboxLadderSelect, selIndex); if (!selID) return; updateLadderDetails(selID, staticTextLadderName, listboxLadderDetails); } static void handleLadderSelection( Int ladderID ) { // only one of these will do any work... HandleQMLadderSelection(ladderID); HandleCustomLadderSelection(ladderID); } enum PasswordMode { PASS_NONE, PASS_ENTRY, PASS_ERROR }; static PasswordMode s_currentMode = PASS_NONE; static void setPasswordMode(PasswordMode mode) { s_currentMode = mode; switch(mode) { case PASS_NONE: if (passwordParent) passwordParent->winHide(TRUE); if (badPasswordParent) badPasswordParent->winHide(TRUE); if (buttonOk) buttonOk->winEnable(TRUE); if (buttonCancel) buttonCancel->winEnable(TRUE); if (textEntryPassword) textEntryPassword->winEnable(FALSE); if (listboxLadderSelect) listboxLadderSelect->winEnable(TRUE); TheWindowManager->winSetFocus(listboxLadderSelect); break; case PASS_ENTRY: if (passwordParent) passwordParent->winHide(FALSE); if (badPasswordParent) badPasswordParent->winHide(TRUE); if (buttonOk) buttonOk->winEnable(FALSE); if (buttonCancel) buttonCancel->winEnable(FALSE); if (textEntryPassword) { textEntryPassword->winEnable(TRUE); GadgetTextEntrySetText(textEntryPassword, UnicodeString::TheEmptyString); } if (listboxLadderSelect) listboxLadderSelect->winEnable(FALSE); TheWindowManager->winSetFocus(textEntryPassword); break; case PASS_ERROR: if (passwordParent) passwordParent->winHide(TRUE); if (badPasswordParent) badPasswordParent->winHide(FALSE); if (buttonOk) buttonOk->winEnable(FALSE); if (buttonCancel) buttonCancel->winEnable(FALSE); if (textEntryPassword) textEntryPassword->winEnable(FALSE); if (listboxLadderSelect) listboxLadderSelect->winEnable(FALSE); TheWindowManager->winSetFocus(parent); break; } } //----------------------------------------------------------------------------- // PUBLIC FUNCTIONS /////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- /** Initialize the menu */ //------------------------------------------------------------------------------------------------- void PopupLadderSelectInit( WindowLayout *layout, void *userData ) { parentID = NAMEKEY("PopupLadderSelect.wnd:Parent"); parent = TheWindowManager->winGetWindowFromId(NULL, parentID); listboxLadderSelectID = NAMEKEY("PopupLadderSelect.wnd:ListBoxLadderSelect"); listboxLadderSelect = TheWindowManager->winGetWindowFromId(parent, listboxLadderSelectID); listboxLadderDetailsID = NAMEKEY("PopupLadderSelect.wnd:ListBoxLadderDetails"); listboxLadderDetails = TheWindowManager->winGetWindowFromId(parent, listboxLadderDetailsID); staticTextLadderNameID = NAMEKEY("PopupLadderSelect.wnd:StaticTextLadderName"); staticTextLadderName = TheWindowManager->winGetWindowFromId(parent, staticTextLadderNameID); buttonOkID = NAMEKEY("PopupLadderSelect.wnd:ButtonOk"); buttonCancelID = NAMEKEY("PopupLadderSelect.wnd:ButtonCancel"); buttonOk = TheWindowManager->winGetWindowFromId(parent, buttonOkID); buttonCancel = TheWindowManager->winGetWindowFromId(parent, buttonCancelID); TheWindowManager->winSetFocus( parent ); TheWindowManager->winSetModal( parent ); // password entry popup passwordParentID = NAMEKEY("PopupLadderSelect.wnd:PasswordParent"); passwordParent = TheWindowManager->winGetWindowFromId(parent, passwordParentID); buttonPasswordOkID = NAMEKEY("PopupLadderSelect.wnd:ButtonPasswordOk"); buttonPasswordCancelID = NAMEKEY("PopupLadderSelect.wnd:ButtonPasswordCancel"); textEntryPasswordID = NAMEKEY("PopupLadderSelect.wnd:PasswordEntry"); textEntryPassword = TheWindowManager->winGetWindowFromId(parent, textEntryPasswordID); // bad password popup badPasswordParentID = NAMEKEY("PopupLadderSelect.wnd:BadPasswordParent"); badPasswordParent = TheWindowManager->winGetWindowFromId(parent, badPasswordParentID); buttonBadPasswordOkID = NAMEKEY("PopupLadderSelect.wnd:ButtonBadPasswordOk"); setPasswordMode(PASS_NONE); CustomMatchHideHostPopup(TRUE); // populate list box (based on whether we're in custom or quickmatch) populateLadderListBox(); } //------------------------------------------------------------------------------------------------- /** Input callback */ //------------------------------------------------------------------------------------------------- WindowMsgHandledType PopupLadderSelectInput( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 ) { switch( msg ) { // -------------------------------------------------------------------------------------------- case GWM_CHAR: { UnsignedByte key = mData1; UnsignedByte state = mData2; // if (buttonPushed) // break; switch( key ) { // ---------------------------------------------------------------------------------------- case KEY_ESC: { // // send a simulated selected event to the parent window of the // back/exit button // if( BitTest( state, KEY_STATE_UP ) ) { switch (s_currentMode) { case PASS_NONE: // re-select whatever was chosen before populateLadderComboBox(); GameSpyCloseOverlay(GSOVERLAY_LADDERSELECT); break; case PASS_ENTRY: case PASS_ERROR: setPasswordMode(PASS_NONE); break; } } // end if // don't let key fall through anywhere else return MSG_HANDLED; } // end escape } // end switch( key ) } // end char } // end switch( msg ) return MSG_IGNORED; } static Int ladderIndex = 0; void ladderSelectedCallback(void) { handleLadderSelection( ladderIndex ); // update combo box populateLadderComboBox(); // tear down overlay GameSpyCloseOverlay( GSOVERLAY_LADDERSELECT ); } //------------------------------------------------------------------------------------------------- /** System callback */ //------------------------------------------------------------------------------------------------- WindowMsgHandledType PopupLadderSelectSystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 ) { switch( msg ) { // -------------------------------------------------------------------------------------------- case GWM_CREATE: { break; } // end create //--------------------------------------------------------------------------------------------- case GWM_DESTROY: { parent = NULL; listboxLadderSelect = NULL; listboxLadderDetails = NULL; CustomMatchHideHostPopup(FALSE); break; } // end case //---------------------------------------------------------------------------------------------- case GWM_INPUT_FOCUS: { // if we're given the opportunity to take the keyboard focus we must say we want it if( mData1 == TRUE ) *(Bool *)mData2 = TRUE; break; } // end input //---------------------------------------------------------------------------------------------- case GBM_SELECTED: { GameWindow *control = (GameWindow *)mData1; Int controlID = control->winGetWindowId(); if (controlID == buttonOkID) { // save selection Int selectPos = -1; GadgetListBoxGetSelected( listboxLadderSelect, &selectPos ); if (selectPos < 0) break; ladderIndex = (Int)GadgetListBoxGetItemData( listboxLadderSelect, selectPos, 0 ); const LadderInfo *li = TheLadderList->findLadderByIndex( ladderIndex ); if (li && li->cryptedPassword.isNotEmpty()) { // need password asking setPasswordMode(PASS_ENTRY); } else { ladderSelectedCallback(); } } else if (controlID == buttonCancelID) { // reset what had been populateLadderComboBox(); // tear down overlay GameSpyCloseOverlay( GSOVERLAY_LADDERSELECT ); } else if (controlID == buttonPasswordOkID) { const LadderInfo *li = TheLadderList->findLadderByIndex( ladderIndex ); if (!li || li->cryptedPassword.isEmpty()) { // eh? something's not right. just pretend they typed something wrong... setPasswordMode(PASS_ERROR); break; } AsciiString pass; pass.translate(GadgetTextEntryGetText(textEntryPassword)); if ( pass.isNotEmpty() ) // password ok { AsciiString cryptPass = EncryptString(pass.str()); DEBUG_LOG(("pass is %s, crypted pass is %s, comparing to %s\n", pass.str(), cryptPass.str(), li->cryptedPassword.str())); if (cryptPass == li->cryptedPassword) ladderSelectedCallback(); else setPasswordMode(PASS_ERROR); } else { setPasswordMode(PASS_ERROR); } } else if (controlID == buttonPasswordCancelID) { setPasswordMode(PASS_NONE); } else if (controlID == buttonBadPasswordOkID) { setPasswordMode(PASS_NONE); } break; } // end input //--------------------------------------------------------------------------------------------- case GLM_SELECTED: { Int selIndex, selID; GadgetListBoxGetSelected(listboxLadderSelect, &selIndex); if (selIndex < 0) break; selID = (Int)GadgetListBoxGetItemData(listboxLadderSelect, selIndex); if (!selID) break; updateLadderDetails(selID, staticTextLadderName, listboxLadderDetails); break; } // end GLM_DOUBLE_CLICKED //--------------------------------------------------------------------------------------------- case GLM_DOUBLE_CLICKED: { GameWindow *control = (GameWindow *)mData1; Int controlID = control->winGetWindowId(); Int selectPos = (Int)mData2; GadgetListBoxSetSelected(control, &selectPos); if( controlID == listboxLadderSelectID ) { TheWindowManager->winSendSystemMsg( parent, GBM_SELECTED, (WindowMsgData)buttonOk, buttonOk->winGetWindowId() ); } break; } //--------------------------------------------------------------------------------------------- case GEM_EDIT_DONE: { GameWindow *control = (GameWindow *)mData1; Int controlID = control->winGetWindowId(); if (controlID == textEntryPasswordID) { TheWindowManager->winSendSystemMsg( parent, GBM_SELECTED, (WindowMsgData)(TheWindowManager->winGetWindowFromId(passwordParent, buttonPasswordOkID)), buttonPasswordOkID ); } break; } default: return MSG_IGNORED; } // end switch return MSG_HANDLED; } //----------------------------------------------------------------------------- // PRIVATE FUNCTIONS ////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- static void updateLadderDetails( Int selID, GameWindow *staticTextLadderName, GameWindow *listboxLadderDetails ) { if (!staticTextLadderName || !listboxLadderDetails) return; GadgetStaticTextSetText(staticTextLadderName, UnicodeString::TheEmptyString); GadgetListBoxReset(listboxLadderDetails); const LadderInfo *info = TheLadderList->findLadderByIndex(selID); if (!info) return; UnicodeString line; Color color = GameMakeColor( 255, 255, 255, 255 ); Color captionColor = GameMakeColor( 0, 255, 255, 255 ); // name line.format(TheGameText->fetch("GUI:LadderNameAndSize"), info->name.str(), info->playersPerTeam, info->playersPerTeam); GadgetStaticTextSetText(staticTextLadderName, line); // location if (!info->location.isEmpty()) GadgetListBoxAddEntryText(listboxLadderDetails, info->location, captionColor, -1); // homepage line.format(TheGameText->fetch("GUI:LadderURL"), info->homepageURL.str()); GadgetListBoxAddEntryText(listboxLadderDetails, line, captionColor, -1); // description if (!info->description.isEmpty()) GadgetListBoxAddEntryText(listboxLadderDetails, info->description, color, -1); // requires password? if (info->cryptedPassword.isNotEmpty()) { GadgetListBoxAddEntryText(listboxLadderDetails, TheGameText->fetch("GUI:LadderHasPassword"), captionColor, -1); } // wins limits if (info->minWins) { line.format(TheGameText->fetch("GUI:LadderMinWins"), info->minWins); GadgetListBoxAddEntryText(listboxLadderDetails, line, captionColor, -1); } if (info->maxWins) { line.format(TheGameText->fetch("GUI:LadderMaxWins"), info->maxWins); GadgetListBoxAddEntryText(listboxLadderDetails, line, captionColor, -1); } // random factions? if (info->randomFactions) { GadgetListBoxAddEntryText(listboxLadderDetails, TheGameText->fetch("GUI:LadderRandomFactions"), captionColor, -1); } else { GadgetListBoxAddEntryText(listboxLadderDetails, TheGameText->fetch("GUI:LadderFactions"), captionColor, -1); } // factions AsciiStringList validFactions = info->validFactions; for (AsciiStringListIterator it = validFactions.begin(); it != validFactions.end(); ++it) { AsciiString marker; marker.format("INI:Faction%s", it->str()); GadgetListBoxAddEntryText(listboxLadderDetails, TheGameText->fetch(marker), color, -1); } // random maps? if (info->randomMaps) { GadgetListBoxAddEntryText(listboxLadderDetails, TheGameText->fetch("GUI:LadderRandomMaps"), captionColor, -1); } else { GadgetListBoxAddEntryText(listboxLadderDetails, TheGameText->fetch("GUI:LadderMaps"), captionColor, -1); } // maps AsciiStringList validMaps = info->validMaps; for (it = validMaps.begin(); it != validMaps.end(); ++it) { const MapMetaData *md = TheMapCache->findMap(*it); if (md) { GadgetListBoxAddEntryText(listboxLadderDetails, md->m_displayName, color, -1); } } } static void closeRightClickMenu(GameWindow *win) { if(win) { WindowLayout *winLay = win->winGetLayout(); if(!winLay) return; winLay->destroyWindows(); winLay->deleteInstance(); winLay = NULL; } } void RCGameDetailsMenuInit( WindowLayout *layout, void *userData ) { } WindowMsgHandledType RCGameDetailsMenuSystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 ) { static NameKeyType ladderInfoID = NAMEKEY_INVALID; static NameKeyType buttonOkID = NAMEKEY_INVALID; switch( msg ) { case GWM_CREATE: { ladderInfoID = NAMEKEY("RCGameDetailsMenu.wnd:ButtonLadderDetails"); buttonOkID = NAMEKEY("PopupLadderDetails.wnd:ButtonOk"); break; } // case GWM_DESTROY: case GGM_CLOSE: { closeRightClickMenu(window); //rcMenu = NULL; break; } case GWM_DESTROY: { break; } // case GWM_DESTROY: case GBM_SELECTED: { GameWindow *control = (GameWindow *)mData1; Int controlID = control->winGetWindowId(); Int selectedID = (Int)window->winGetUserData(); if(!selectedID) break; closeRightClickMenu(window); if (controlID == ladderInfoID) { StagingRoomMap *srm = TheGameSpyInfo->getStagingRoomList(); StagingRoomMap::iterator srmIt = srm->find(selectedID); if (srmIt != srm->end()) { GameSpyStagingRoom *theRoom = srmIt->second; if (!theRoom) break; const LadderInfo *linfo = TheLadderList->findLadder(theRoom->getLadderIP(), theRoom->getLadderPort()); if (linfo) { WindowLayout *rcLayout = TheWindowManager->winCreateLayout(AsciiString("Menus/PopupLadderDetails.wnd")); if (!rcLayout) break; GameWindow *rcMenu = rcLayout->getFirstWindow(); rcMenu->winGetLayout()->runInit(); rcMenu->winBringToTop(); rcMenu->winHide(FALSE); rcMenu->winSetUserData((void *)selectedID); TheWindowManager->winSetLoneWindow(rcMenu); GameWindow *st = TheWindowManager->winGetWindowFromId(NULL, NAMEKEY("PopupLadderDetails.wnd:StaticTextLadderName")); GameWindow *lb = TheWindowManager->winGetWindowFromId(NULL, NAMEKEY("PopupLadderDetails.wnd:ListBoxLadderDetails")); updateLadderDetails(selectedID, st, lb); } } } break; } default: return MSG_IGNORED; }//Switch return MSG_HANDLED; }