/*
** 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: WOLQuickMatchMenu.cpp
// Author: Chris Huybregts, November 2001
// Description: Lan Lobby Menu
///////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/GameEngine.h"
#include "Common/QuickmatchPreferences.h"
#include "Common/LadderPreferences.h"
#include "Common/MultiplayerSettings.h"
#include "Common/PlayerTemplate.h"
#include "GameClient/AnimateWindowManager.h"
#include "GameClient/WindowLayout.h"
#include "GameClient/Gadget.h"
#include "GameClient/GameText.h"
#include "GameClient/InGameUI.h"
#include "GameClient/Shell.h"
#include "GameClient/ShellHooks.h"
#include "GameClient/KeyDefs.h"
#include "GameClient/GameWindowManager.h"
#include "GameClient/GadgetComboBox.h"
#include "GameClient/GadgetPushButton.h"
#include "GameClient/GadgetListBox.h"
#include "GameClient/GadgetTextEntry.h"
#include "GameClient/GadgetStaticText.h"
#include "GameClient/MapUtil.h"
#include "GameClient/GameWindowTransitions.h"
#include "GameLogic/GameLogic.h"
#include "GameNetwork/NAT.h"
#include "GameNetwork/GameSpyOverlay.h"
#include "GameNetwork/GameSpy/BuddyDefs.h"
#include "GameNetwork/GameSpy/GSConfig.h"
#include "GameNetwork/GameSpy/PeerDefs.h"
#include "GameNetwork/GameSpy/PeerThread.h"
#include "GameNetwork/GameSpy/PersistentStorageDefs.h"
#include "GameNetwork/GameSpy/PersistentStorageThread.h"
#include "GameNetwork/RankPointValue.h"
#include "GameNetwork/GameSpy/LadderDefs.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
#ifdef DEBUG_LOGGING
#include "Common/MiniLog.h"
//#define PERF_TEST
static LogClass s_perfLog("QMPerf.txt");
static Bool s_inQM = FALSE;
#define PERF_LOG(x) s_perfLog.log x
#else // DEBUG_LOGGING
#define PERF_LOG(x) {}
#endif // DEBUG_LOGGING
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
// window ids ------------------------------------------------------------------------------
static NameKeyType parentWOLQuickMatchID = NAMEKEY_INVALID;
static NameKeyType buttonBackID = NAMEKEY_INVALID;
static NameKeyType buttonStartID = NAMEKEY_INVALID;
static NameKeyType buttonStopID = NAMEKEY_INVALID;
static NameKeyType buttonWidenID = NAMEKEY_INVALID;
static NameKeyType buttonBuddiesID = NAMEKEY_INVALID;
static NameKeyType listboxQuickMatchID = NAMEKEY_INVALID;
static NameKeyType listboxMapSelectID = NAMEKEY_INVALID;
static NameKeyType buttonSelectAllMapsID = NAMEKEY_INVALID;
static NameKeyType buttonSelectNoMapsID = NAMEKEY_INVALID;
//static NameKeyType textEntryMaxDisconnectsID = NAMEKEY_INVALID;
//static NameKeyType textEntryMaxPointsID = NAMEKEY_INVALID;
//static NameKeyType textEntryMinPointsID = NAMEKEY_INVALID;
static NameKeyType textEntryWaitTimeID = NAMEKEY_INVALID;
static NameKeyType comboBoxNumPlayersID = NAMEKEY_INVALID;
static NameKeyType comboBoxMaxPingID = NAMEKEY_INVALID;
static NameKeyType comboBoxLadderID = NAMEKEY_INVALID;
static NameKeyType comboBoxMaxDisconnectsID = NAMEKEY_INVALID;
static NameKeyType staticTextNumPlayersID = NAMEKEY_INVALID;
static NameKeyType comboBoxSideID = NAMEKEY_INVALID;
static NameKeyType comboBoxColorID = NAMEKEY_INVALID;
// Window Pointers ------------------------------------------------------------------------
static GameWindow *parentWOLQuickMatch = NULL;
static GameWindow *buttonBack = NULL;
static GameWindow *buttonStart = NULL;
static GameWindow *buttonStop = NULL;
static GameWindow *buttonWiden = NULL;
GameWindow *quickmatchTextWindow = NULL;
static GameWindow *listboxMapSelect = NULL;
//static GameWindow *textEntryMaxDisconnects = NULL;
//static GameWindow *textEntryMaxPoints = NULL;
//static GameWindow *textEntryMinPoints = NULL;
static GameWindow *textEntryWaitTime = NULL;
static GameWindow *comboBoxNumPlayers = NULL;
static GameWindow *comboBoxMaxPing = NULL;
static GameWindow *comboBoxLadder = NULL;
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
static GameWindow *comboBoxMaxDisconnects = NULL;
static GameWindow *staticTextNumPlayers = NULL;
static GameWindow *comboBoxSide = NULL;
static GameWindow *comboBoxColor = NULL;
static Bool isShuttingDown = false;
static Bool buttonPushed = false;
static char *nextScreen = NULL;
static Bool raiseMessageBoxes = false;
static Bool isInInit = FALSE;
static const Image *selectedImage = NULL;
static const Image *unselectedImage = NULL;
static bool isPopulatingLadderBox = false;
static Int maxPingEntries = 0;
static Int maxPoints= 100;
static Int minPoints = 0;
static const LadderInfo * getLadderInfo( void );
// [SKB: Jul 01 2003 @ 7:7pm] :
// German2 now has fewer maps. When trying to do a QM with a Retail version
// the bool string sent to the QMBot is almost always a different size. This
// mapping is kept so that we now send a string of all maps instead of just
// the ones that are visible to the user.
#define VARIABLE_NUMBER_OF_MAPS 1
#if VARIABLE_NUMBER_OF_MAPS
typedef std::vector MapListboxIndex;
static MapListboxIndex mapListboxIndex;
#endif
static Bool isInfoShown(void)
{
static NameKeyType parentStatsID = NAMEKEY("WOLQuickMatchMenu.wnd:ParentStats");
GameWindow *parentStats = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, parentStatsID );
if (parentStats)
return !parentStats->winIsHidden();
return FALSE;
}
static void hideInfoGadgets(Bool doIt)
{
static NameKeyType parentStatsID = NAMEKEY("WOLQuickMatchMenu.wnd:ParentStats");
GameWindow *parentStats = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, parentStatsID );
if (parentStats)
{
parentStats->winHide(doIt);
}
}
static void hideOptionsGadgets(Bool doIt)
{
static NameKeyType parentOptionsID = NAMEKEY("WOLQuickMatchMenu.wnd:ParentOptions");
GameWindow *parentOptions = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, parentOptionsID );
if (parentOptions)
{
parentOptions->winHide(doIt);
if (comboBoxSide)
comboBoxSide->winHide(doIt);
if (comboBoxColor)
comboBoxColor->winHide(doIt);
if (comboBoxNumPlayers)
comboBoxNumPlayers->winHide(doIt);
if (comboBoxLadder)
comboBoxLadder->winHide(doIt);
if (comboBoxDisabledLadder)
comboBoxDisabledLadder->winHide(doIt);
if (comboBoxMaxPing)
comboBoxMaxPing->winHide(doIt);
if (comboBoxMaxDisconnects)
comboBoxMaxDisconnects->winHide(doIt);
}
}
static void enableOptionsGadgets(Bool doIt)
{
#ifdef PERF_TEST
s_inQM = !doIt;
#endif // PERF_TEST
static NameKeyType parentOptionsID = NAMEKEY("WOLQuickMatchMenu.wnd:ParentOptions");
GameWindow *parentOptions = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, parentOptionsID );
const LadderInfo *li = getLadderInfo();
if (parentOptions)
{
parentOptions->winEnable(doIt);
if (comboBoxSide)
comboBoxSide->winEnable(doIt && (!li || !li->randomFactions));
if (comboBoxColor)
comboBoxColor->winEnable(doIt);
if (comboBoxNumPlayers)
comboBoxNumPlayers->winEnable(doIt);
if (comboBoxLadder)
comboBoxLadder->winEnable(doIt);
if (comboBoxDisabledLadder)
comboBoxDisabledLadder->winEnable(FALSE);
if (comboBoxMaxPing)
comboBoxMaxPing->winEnable(doIt);
if (comboBoxMaxDisconnects)
comboBoxMaxDisconnects->winEnable(doIt);
}
}
enum
{
MAX_DISCONNECTS_ANY = 0,
MAX_DISCONNECTS_5 = 5,
MAX_DISCONNECTS_10 = 10,
MAX_DISCONNECTS_25 = 25,
MAX_DISCONNECTS_50 = 50,
};
enum{ MAX_DISCONNECTS_COUNT = 5 };
static Int MAX_DISCONNECTS[MAX_DISCONNECTS_COUNT] = {MAX_DISCONNECTS_ANY, MAX_DISCONNECTS_5,
MAX_DISCONNECTS_10, MAX_DISCONNECTS_25,
MAX_DISCONNECTS_50};
void UpdateStartButton(void)
{
if (!comboBoxLadder || !buttonStart || !listboxMapSelect)
return;
Int index;
Int selected;
GadgetComboBoxGetSelectedPos( comboBoxLadder, &selected );
index = (Int)GadgetComboBoxGetItemData( comboBoxLadder, selected );
const LadderInfo *li = TheLadderList->findLadderByIndex( index );
if (li)
{
buttonStart->winEnable(TRUE);
return;
}
Int numMaps = GadgetListBoxGetNumEntries(listboxMapSelect);
for ( Int i=0; iwinEnable(TRUE);
return;
}
}
buttonStart->winEnable(FALSE);
}
// -----------------------------------------------------------------------------
static void populateQMColorComboBox(QuickMatchPreferences& pref)
{
Int numColors = TheMultiplayerSettings->getNumColors();
UnicodeString colorName;
GadgetComboBoxReset(comboBoxColor);
MultiplayerColorDefinition *def = TheMultiplayerSettings->getColor(PLAYERTEMPLATE_RANDOM);
Int newIndex = GadgetComboBoxAddEntry(comboBoxColor, TheGameText->fetch("GUI:???"), def->getColor());
GadgetComboBoxSetItemData(comboBoxColor, newIndex, (void *)-1);
for (Int c=0; cgetColor(c);
if (!def)
continue;
colorName = TheGameText->fetch(def->getTooltipName().str());
newIndex = GadgetComboBoxAddEntry(comboBoxColor, colorName, def->getColor());
GadgetComboBoxSetItemData(comboBoxColor, newIndex, (void *)c);
}
GadgetComboBoxSetSelectedPos(comboBoxColor, pref.getColor());
}
// -----------------------------------------------------------------------------
static void populateQMSideComboBox(Int favSide, const LadderInfo *li = NULL)
{
Int numPlayerTemplates = ThePlayerTemplateStore->getPlayerTemplateCount();
UnicodeString playerTemplateName;
GadgetComboBoxReset(comboBoxSide);
MultiplayerColorDefinition *def = TheMultiplayerSettings->getColor(PLAYERTEMPLATE_RANDOM);
Int newIndex = GadgetComboBoxAddEntry(comboBoxSide, TheGameText->fetch("GUI:Random"), def->getColor());
GadgetComboBoxSetItemData(comboBoxSide, newIndex, (void *)PLAYERTEMPLATE_RANDOM);
std::set seenSides;
Int entryToSelect = 0; // select Random by default
for (Int c=0; cgetNthPlayerTemplate(c);
if (!fac)
continue;
if (fac->getStartingBuilding().isEmpty())
continue;
AsciiString side;
side.format("SIDE:%s", fac->getSide().str());
if (seenSides.find(side) != seenSides.end())
continue;
if (li)
{
if (std::find(li->validFactions.begin(), li->validFactions.end(), fac->getSide()) == li->validFactions.end())
continue; // ladder doesn't allow it.
}
seenSides.insert(side);
newIndex = GadgetComboBoxAddEntry(comboBoxSide, TheGameText->fetch(side), def->getColor());
GadgetComboBoxSetItemData(comboBoxSide, newIndex, (void *)c);
if (c == favSide)
entryToSelect = newIndex;
}
seenSides.clear();
GadgetComboBoxSetSelectedPos(comboBoxSide, entryToSelect);
if (li && li->randomFactions)
comboBoxSide->winEnable(FALSE);
else
comboBoxSide->winEnable(TRUE);
}
void HandleQMLadderSelection(Int ladderID)
{
if (!parentWOLQuickMatch)
return;
QuickMatchPreferences pref;
if (ladderID < 1)
{
pref.setLastLadder(AsciiString::TheEmptyString, 0);
pref.write();
return;
}
const LadderInfo *info = TheLadderList->findLadderByIndex(ladderID);
if (!info)
{
pref.setLastLadder(AsciiString::TheEmptyString, 0);
}
else
{
pref.setLastLadder(info->address, info->port);
}
pref.write();
}
static inline Bool isValidLadder( const LadderInfo *lad )
{
if (lad && lad->index > 0 && lad->validQM)
{
PSPlayerStats stats = TheGameSpyPSMessageQueue->findPlayerStatsByID(TheGameSpyInfo->getLocalProfileID());
Int numWins = 0;
PerGeneralMap::iterator it;
for (it = stats.wins.begin(); it != stats.wins.end(); ++it)
{
numWins += it->second;
}
if (!lad->maxWins || lad->maxWins >=numWins)
{
if (!lad->minWins || lad->minWins<=numWins)
{
return TRUE;
}
}
}
return FALSE;
}
void PopulateQMLadderListBox( GameWindow *win )
{
if (!parentWOLQuickMatch || !comboBoxLadder)
return;
isPopulatingLadderBox = true;
QuickMatchPreferences pref;
AsciiString userPrefFilename;
Int localProfile = TheGameSpyInfo->getLocalProfileID();
Color specialColor = GameSpyColor[GSCOLOR_MAP_SELECTED];
Color normalColor = GameSpyColor[GSCOLOR_MAP_UNSELECTED];
Color favoriteColor = GameSpyColor[GSCOLOR_MAP_UNSELECTED];
Int index;
GadgetListBoxReset( win );
std::set usedLadders;
// start with "No Ladder"
index = GadgetListBoxAddEntryText( win, TheGameText->fetch("GUI:NoLadder"), normalColor, -1 );
GadgetListBoxSetItemData( win, 0, index );
// add the last ladder
Int selectedPos = 0;
AsciiString lastLadderAddr = pref.getLastLadderAddr();
UnsignedShort lastLadderPort = pref.getLastLadderPort();
const LadderInfo *info = TheLadderList->findLadder( lastLadderAddr, lastLadderPort );
if (isValidLadder(info))
{
usedLadders.insert(info);
index = GadgetListBoxAddEntryText( win, info->name, favoriteColor, -1 );
GadgetListBoxSetItemData( win, (void *)(info->index), index );
selectedPos = index;
}
// our recent ladders
LadderPreferences ladPref;
ladPref.loadProfile( localProfile );
const LadderPrefMap recentLadders = ladPref.getRecentLadders();
for (LadderPrefMap::const_iterator cit = recentLadders.begin(); cit != recentLadders.end(); ++cit)
{
AsciiString addr = cit->second.address;
UnsignedShort port = cit->second.port;
if (addr == lastLadderAddr && port == lastLadderPort)
continue;
const LadderInfo *info = TheLadderList->findLadder( addr, port );
if (isValidLadder(info) && usedLadders.find(info) == usedLadders.end())
{
usedLadders.insert(info);
index = GadgetListBoxAddEntryText( win, info->name, favoriteColor, -1 );
GadgetListBoxSetItemData( win, (void *)(info->index), index );
}
}
// special ladders
const LadderInfoList *lil = TheLadderList->getSpecialLadders();
LadderInfoList::const_iterator lit;
for (lit = lil->begin(); lit != lil->end(); ++lit)
{
const LadderInfo *info = *lit;
if (isValidLadder(info) && usedLadders.find(info) == usedLadders.end())
{
usedLadders.insert(info);
index = GadgetListBoxAddEntryText( win, info->name, specialColor, -1 );
GadgetListBoxSetItemData( win, (void *)(info->index), index );
}
}
// standard ladders
lil = TheLadderList->getStandardLadders();
for (lit = lil->begin(); lit != lil->end(); ++lit)
{
const LadderInfo *info = *lit;
if (isValidLadder(info) && usedLadders.find(info) == usedLadders.end())
{
usedLadders.insert(info);
index = GadgetListBoxAddEntryText( win, info->name, normalColor, -1 );
GadgetListBoxSetItemData( win, (void *)(info->index), index );
}
}
GadgetListBoxSetSelected( win, selectedPos );
isPopulatingLadderBox = false;
}
static const LadderInfo * getLadderInfo( void )
{
Int index;
Int selected;
GadgetComboBoxGetSelectedPos( comboBoxLadder, &selected );
index = (Int)GadgetComboBoxGetItemData( comboBoxLadder, selected );
const LadderInfo *li = TheLadderList->findLadderByIndex( index );
return li;
}
void PopulateQMLadderComboBox( void )
{
if (!parentWOLQuickMatch || !comboBoxLadder)
return;
isPopulatingLadderBox = true;
QuickMatchPreferences pref;
Int localProfile = TheGameSpyInfo->getLocalProfileID();
Color specialColor = GameSpyColor[GSCOLOR_MAP_SELECTED];
Color normalColor = GameSpyColor[GSCOLOR_MAP_UNSELECTED];
Int index;
GadgetComboBoxReset( comboBoxLadder );
index = GadgetComboBoxAddEntry( comboBoxLadder, TheGameText->fetch("GUI:NoLadder"), normalColor );
GadgetComboBoxSetItemData( comboBoxLadder, index, 0 );
std::set usedLadders;
Int selectedPos = 0;
AsciiString lastLadderAddr = pref.getLastLadderAddr();
UnsignedShort lastLadderPort = pref.getLastLadderPort();
const LadderInfo *info = TheLadderList->findLadder( lastLadderAddr, lastLadderPort );
if (isValidLadder(info))
{
usedLadders.insert(info);
index = GadgetComboBoxAddEntry( comboBoxLadder, info->name, specialColor );
GadgetComboBoxSetItemData( comboBoxLadder, index, (void *)(info->index) );
selectedPos = index;
// we selected a ladder? No game size choice for us...
GadgetComboBoxSetSelectedPos(comboBoxNumPlayers, info->playersPerTeam-1);
comboBoxNumPlayers->winEnable( FALSE );
}
else
{
comboBoxNumPlayers->winEnable( TRUE );
}
LadderPreferences ladPref;
ladPref.loadProfile( localProfile );
const LadderPrefMap recentLadders = ladPref.getRecentLadders();
for (LadderPrefMap::const_iterator cit = recentLadders.begin(); cit != recentLadders.end(); ++cit)
{
AsciiString addr = cit->second.address;
UnsignedShort port = cit->second.port;
if (addr == lastLadderAddr && port == lastLadderPort)
continue;
const LadderInfo *info = TheLadderList->findLadder( addr, port );
if (isValidLadder(info) && usedLadders.find(info) == usedLadders.end())
{
usedLadders.insert(info);
index = GadgetComboBoxAddEntry( comboBoxLadder, info->name, normalColor );
GadgetComboBoxSetItemData( comboBoxLadder, index, (void *)(info->index) );
}
}
index = GadgetComboBoxAddEntry( comboBoxLadder, TheGameText->fetch("GUI:ChooseLadder"), normalColor );
GadgetComboBoxSetItemData( comboBoxLadder, index, (void *)-1 );
GadgetComboBoxSetSelectedPos( comboBoxLadder, selectedPos );
isPopulatingLadderBox = false;
populateQMSideComboBox(pref.getSide(), getLadderInfo()); // this will set side to random and disable if necessary
}
static void populateQuickMatchMapSelectListbox( QuickMatchPreferences& pref )
{
std::list maps = TheGameSpyConfig->getQMMaps();
// enable/disable box based on ladder status
Int index;
Int selected;
GadgetComboBoxGetSelectedPos( comboBoxLadder, &selected );
index = (Int)GadgetComboBoxGetItemData( comboBoxLadder, selected );
const LadderInfo *li = TheLadderList->findLadderByIndex( index );
//listboxMapSelect->winEnable( li == NULL || li->randomMaps == FALSE );
Int numPlayers = 0;
if (li)
{
numPlayers = li->playersPerTeam*2;
maps = li->validMaps;
}
else
{
GadgetComboBoxGetSelectedPos(comboBoxNumPlayers, &selected);
if (selected < 0)
selected = 0;
numPlayers = (selected+1)*2;
}
#if VARIABLE_NUMBER_OF_MAPS
mapListboxIndex.clear();
#endif
GadgetListBoxReset(listboxMapSelect);
for (std::list::const_iterator it = maps.begin(); it != maps.end(); ++it)
{
AsciiString theMap = *it;
const MapMetaData *md = TheMapCache->findMap(theMap);
if (md && md->m_numPlayers >= numPlayers)
{
UnicodeString displayName;
displayName = md->m_displayName;
Bool isSelected = pref.isMapSelected(theMap);
if (li && li->randomMaps)
isSelected = TRUE;
Int width = 10;
Int height = 10;
const Image *img = (isSelected)?selectedImage:unselectedImage;
if ( img )
{
width = min(GadgetListBoxGetColumnWidth(listboxMapSelect, 0), img->getImageWidth());
height = width;
}
Int index = GadgetListBoxAddEntryImage(listboxMapSelect, img, -1, 0, height, width);
GadgetListBoxAddEntryText(listboxMapSelect, displayName, GameSpyColor[(isSelected)?GSCOLOR_MAP_SELECTED:GSCOLOR_MAP_UNSELECTED], index, 1);
GadgetListBoxSetItemData(listboxMapSelect, (void *)isSelected, index);
GadgetListBoxSetItemData(listboxMapSelect, (void *)md, index, 1);
#if VARIABLE_NUMBER_OF_MAPS
mapListboxIndex.push_back(index);
#endif
}
else
{
#if VARIABLE_NUMBER_OF_MAPS
// [SKB: Jul 01 2003 @ 7:9pm] :
// Keep track of maps that are not visible right now so
// they are added to the information sent to the QMBot.
mapListboxIndex.push_back(-1);
#endif
}
}
}
static void saveQuickMatchOptions( void )
{
if(isInInit)
return;
QuickMatchPreferences pref;
std::list maps = TheGameSpyConfig->getQMMaps();
Int index;
Int selected;
GadgetComboBoxGetSelectedPos( comboBoxLadder, &selected );
index = (Int)GadgetComboBoxGetItemData( comboBoxLadder, selected );
const LadderInfo *li = TheLadderList->findLadderByIndex( index );
Int numPlayers = 0;
if (li)
{
pref.setLastLadder( li->address, li->port );
numPlayers = li->playersPerTeam*2;
pref.write();
//return; // don't save our defaults based on the tournament's defaults
}
else
{
pref.setLastLadder( AsciiString::TheEmptyString, 0 );
GadgetComboBoxGetSelectedPos(comboBoxNumPlayers, &selected);
if (selected < 0)
selected = 0;
numPlayers = (selected+1)*2;
}
if (!li || !li->randomMaps) // don't save the map as selected if we couldn't choose
{
Int row = 0;
Int entries = GadgetListBoxGetNumEntries(listboxMapSelect);
while ( row < entries)
{
const MapMetaData *md = (const MapMetaData *)GadgetListBoxGetItemData(listboxMapSelect, row, 1);
if(md)
pref.setMapSelected(md->m_fileName, (Bool)GadgetListBoxGetItemData(listboxMapSelect, row));
row++;
}
}
UnicodeString u;
AsciiString a;
// u = GadgetTextEntryGetText(textEntryMaxDisconnects);
// a.translate(u);
// pref.setMaxDisconnects(atoi(a.str()));
// u = GadgetTextEntryGetText(textEntryMaxPoints);
// a.translate(u);
// pref.setMaxPoints(max(100, atoi(a.str())));
// u = GadgetTextEntryGetText(textEntryMinPoints);
// a.translate(u);
// pref.setMinPoints(min(100, atoi(a.str())));
//u = GadgetTextEntryGetText(textEntryWaitTime);
//a.translate(u);
//pref.setWaitTime(atoi(a.str()));
GadgetComboBoxGetSelectedPos(comboBoxNumPlayers, &selected);
pref.setNumPlayers(selected);
GadgetComboBoxGetSelectedPos(comboBoxMaxPing, &selected);
pref.setMaxPing(selected);
Int item;
GadgetComboBoxGetSelectedPos(comboBoxSide, &selected);
item = (Int)GadgetComboBoxGetItemData(comboBoxSide, selected);
pref.setSide(max(0, item));
GadgetComboBoxGetSelectedPos(comboBoxColor, &selected);
pref.setColor(max(0, selected));
GadgetComboBoxGetSelectedPos(comboBoxMaxDisconnects, &selected);
pref.setMaxDisconnects(selected);
pref.write();
}
//-------------------------------------------------------------------------------------------------
/** Initialize the WOL Quick Match Menu */
//-------------------------------------------------------------------------------------------------
void WOLQuickMatchMenuInit( WindowLayout *layout, void *userData )
{
isInInit = TRUE;
if (TheGameSpyGame && TheGameSpyGame->isGameInProgress())
{
TheGameSpyGame->setGameInProgress(FALSE);
// check if we were disconnected
Int disconReason;
if (TheGameSpyInfo->isDisconnectedAfterGameStart(&disconReason))
{
AsciiString disconMunkee;
disconMunkee.format("GUI:GSDisconReason%d", disconReason);
UnicodeString title, body;
title = TheGameText->fetch( "GUI:GSErrorTitle" );
body = TheGameText->fetch( disconMunkee );
GameSpyCloseAllOverlays();
GSMessageBoxOk( title, body );
TheGameSpyInfo->reset();
DEBUG_LOG(("WOLQuickMatchMenuInit() - game was in progress, and we were disconnected, so pop immediate back to main menu\n"));
TheShell->popImmediate();
return;
}
}
nextScreen = NULL;
buttonPushed = false;
isShuttingDown = false;
raiseMessageBoxes = true;
if (TheNAT != NULL) {
delete TheNAT;
TheNAT = NULL;
}
parentWOLQuickMatchID = NAMEKEY( "WOLQuickMatchMenu.wnd:WOLQuickMatchMenuParent" );
buttonBackID = NAMEKEY( "WOLQuickMatchMenu.wnd:ButtonBack" );
buttonBuddiesID = NAMEKEY( "WOLQuickMatchMenu.wnd:ButtonBuddies" );
buttonStartID = NAMEKEY( "WOLQuickMatchMenu.wnd:ButtonStart" );
buttonStopID = NAMEKEY( "WOLQuickMatchMenu.wnd:ButtonStop" );
buttonWidenID = NAMEKEY( "WOLQuickMatchMenu.wnd:ButtonWiden" );
listboxQuickMatchID = NAMEKEY( "WOLQuickMatchMenu.wnd:ListboxQuickMatch" );
listboxMapSelectID = NAMEKEY( "WOLQuickMatchMenu.wnd:ListBoxMapSelect" );
buttonSelectAllMapsID = NAMEKEY( "WOLQuickMatchMenu.wnd:ButtonSelectAllMaps" );
buttonSelectNoMapsID = NAMEKEY( "WOLQuickMatchMenu.wnd:ButtonSelectNoMaps" );
//textEntryMaxDisconnectsID = NAMEKEY( "WOLQuickMatchMenu.wnd:TextEntryMaxDisconnects" );
//textEntryMaxPointsID = NAMEKEY( "WOLQuickMatchMenu.wnd:TextEntryMaxPointPercent" );
//textEntryMinPointsID = NAMEKEY( "WOLQuickMatchMenu.wnd:TextEntryMinPointPercent" );
textEntryWaitTimeID = NAMEKEY( "WOLQuickMatchMenu.wnd:TextEntryWaitTime" );
comboBoxMaxPingID = NAMEKEY( "WOLQuickMatchMenu.wnd:ComboBoxMaxPing" );
comboBoxNumPlayersID = NAMEKEY( "WOLQuickMatchMenu.wnd:ComboBoxNumPlayers" );
comboBoxLadderID = NAMEKEY( "WOLQuickMatchMenu.wnd:ComboBoxLadder" );
comboBoxMaxDisconnectsID = NAMEKEY( "WOLQuickMatchMenu.wnd:ComboBoxMaxDisconnects" );
staticTextNumPlayersID = NAMEKEY( "WOLQuickMatchMenu.wnd:StaticTextNumPlayers" );
comboBoxSideID = NAMEKEY( "WOLQuickMatchMenu.wnd:ComboBoxSide" );
comboBoxColorID = NAMEKEY( "WOLQuickMatchMenu.wnd:ComboBoxColor" );
parentWOLQuickMatch = TheWindowManager->winGetWindowFromId( NULL, parentWOLQuickMatchID );
buttonBack = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, buttonBackID);
buttonStart = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, buttonStartID);
buttonStop = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, buttonStopID);
buttonWiden = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, buttonWidenID);
quickmatchTextWindow = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, listboxQuickMatchID);
listboxMapSelect = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, listboxMapSelectID);
//textEntryMaxDisconnects = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, textEntryMaxDisconnectsID );
//textEntryMaxPoints = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, textEntryMaxPointsID );
//textEntryMinPoints = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, textEntryMinPointsID );
textEntryWaitTime = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, textEntryWaitTimeID );
comboBoxMaxPing = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, comboBoxMaxPingID );
comboBoxNumPlayers = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, comboBoxNumPlayersID );
comboBoxLadder = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, comboBoxLadderID );
comboBoxMaxDisconnects = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, comboBoxMaxDisconnectsID );
TheGameSpyInfo->registerTextWindow(quickmatchTextWindow);
staticTextNumPlayers = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, staticTextNumPlayersID );
comboBoxSide = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, comboBoxSideID );
comboBoxColor = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch, comboBoxColorID );
if (TheLadderList->getStandardLadders()->size() == 0
&& TheLadderList->getSpecialLadders()->size() == 0
&& TheLadderList->getLocalLadders()->size() == 0)
{
// no ladders, so just disable them
comboBoxDisabledLadder = comboBoxLadder;
comboBoxLadder = NULL;
isPopulatingLadderBox = TRUE;
Color normalColor = GameSpyColor[GSCOLOR_MAP_UNSELECTED];
Int index;
GadgetComboBoxReset( comboBoxDisabledLadder );
index = GadgetComboBoxAddEntry( comboBoxDisabledLadder, TheGameText->fetch("GUI:NoLadder"), normalColor );
GadgetComboBoxSetItemData( comboBoxDisabledLadder, index, 0 );
GadgetComboBoxSetSelectedPos( comboBoxDisabledLadder, index );
isPopulatingLadderBox = FALSE;
/** This code would actually *hide* the combo box, but it doesn't look as good. Left here since someone will want to
** see it at some point. :P
if (comboBoxLadder)
{
comboBoxLadder->winHide(TRUE);
comboBoxLadder->winEnable(FALSE);
}
comboBoxLadder = NULL;
GameWindow *staticTextLadder = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch,
NAMEKEY("WOLQuickMatchMenu.wnd:StaticTextLadder") );
if (staticTextLadder)
staticTextLadder->winHide(TRUE);
*/
}
GameWindow *buttonBuddies = TheWindowManager->winGetWindowFromId(NULL, buttonBuddiesID);
if (buttonBuddies)
buttonBuddies->winEnable(TRUE);
GameWindow *staticTextTitle = TheWindowManager->winGetWindowFromId( parentWOLQuickMatch,
NAMEKEY("WOLQuickMatchMenu.wnd:StaticTextTitle") );
if (staticTextTitle)
{
UnicodeString tmp;
tmp.format(TheGameText->fetch("GUI:QuickMatchTitle"), TheGameSpyInfo->getLocalName().str());
GadgetStaticTextSetText(staticTextTitle, tmp);
}
// QM is not going yet, so disable the Widen Search button
buttonWiden->winEnable( FALSE );
buttonStop->winHide( TRUE );
buttonStart->winHide( FALSE );
GadgetListBoxReset(quickmatchTextWindow);
enableOptionsGadgets(TRUE);
// Show Menu
layout->hide( FALSE );
// Set Keyboard to Main Parent
TheWindowManager->winSetFocus( parentWOLQuickMatch );
// fill in preferences
selectedImage = TheMappedImageCollection->findImageByName("CustomMatch_selected");
unselectedImage = TheMappedImageCollection->findImageByName("CustomMatch_deselected");
QuickMatchPreferences pref;
UnicodeString s;
// s.format(L"%d", pref.getMaxDisconnects());
// GadgetTextEntrySetText(textEntryMaxDisconnects, s);
// s.format(L"%d", pref.getMaxPoints());
// GadgetTextEntrySetText(textEntryMaxPoints, s);
// s.format(L"%d", pref.getMinPoints());
// GadgetTextEntrySetText(textEntryMinPoints, s);
//s.format(L"%d", pref.getWaitTime());
//GadgetTextEntrySetText(textEntryWaitTime, s);
maxPoints= pref.getMaxPoints();
minPoints = pref.getMinPoints();
Color c = GameSpyColor[GSCOLOR_DEFAULT];
GadgetComboBoxReset( comboBoxNumPlayers );
Int i;
for (i=1; i<5; ++i)
{
s.format(TheGameText->fetch("GUI:PlayersVersusPlayers"), i, i);
GadgetComboBoxAddEntry( comboBoxNumPlayers, s, c );
}
GadgetComboBoxSetSelectedPos( comboBoxNumPlayers, max(0, pref.getNumPlayers()) );
GadgetComboBoxReset(comboBoxMaxDisconnects);
GadgetComboBoxAddEntry( comboBoxMaxDisconnects, TheGameText->fetch("GUI:Any"), c);
for( i = 1; i < MAX_DISCONNECTS_COUNT; ++i )
{
s.format(L"%d", MAX_DISCONNECTS[i]);
GadgetComboBoxAddEntry( comboBoxMaxDisconnects, s, c );
}
Int maxDisconIndex = max(0, pref.getMaxDisconnects());
GadgetComboBoxSetSelectedPos(comboBoxMaxDisconnects, maxDisconIndex);
GadgetComboBoxReset( comboBoxMaxPing );
maxPingEntries = (TheGameSpyConfig->getPingTimeoutInMs() - 1) / 100;
maxPingEntries++; // need to add the entry for the actual timeout
for (i=1; i fetch("GUI:TimeInMilliseconds"), i*100);
GadgetComboBoxAddEntry( comboBoxMaxPing, s, c );
}
GadgetComboBoxAddEntry( comboBoxMaxPing, TheGameText->fetch("GUI:ANY"), c );
i = pref.getMaxPing();
if( i < 0 )
i = 0;
if( i >= maxPingEntries )
i = maxPingEntries - 1;
GadgetComboBoxSetSelectedPos( comboBoxMaxPing, i );
populateQMColorComboBox(pref);
populateQMSideComboBox(pref.getSide(), getLadderInfo());
PopulateQMLadderComboBox();
TheShell->showShellMap(TRUE);
TheGameSpyGame->reset();
GadgetListBoxReset(listboxMapSelect);
populateQuickMatchMapSelectListbox(pref);
UpdateLocalPlayerStats();
UpdateStartButton();
TheTransitionHandler->setGroup("WOLQuickMatchMenuFade");
isInInit= FALSE;
} // WOLQuickMatchMenuInit
//-------------------------------------------------------------------------------------------------
/** This is called when a shutdown is complete for this menu */
//-------------------------------------------------------------------------------------------------
static void shutdownComplete( WindowLayout *layout )
{
isShuttingDown = false;
// hide the layout
layout->hide( TRUE );
// our shutdown is complete
TheShell->shutdownComplete( layout, (nextScreen != NULL) );
if (nextScreen != NULL)
{
TheShell->push(nextScreen);
}
nextScreen = NULL;
} // end if
//-------------------------------------------------------------------------------------------------
/** WOL Quick Match Menu shutdown method */
//-------------------------------------------------------------------------------------------------
void WOLQuickMatchMenuShutdown( WindowLayout *layout, void *userData )
{
TheGameSpyInfo->unregisterTextWindow(quickmatchTextWindow);
if (!TheGameEngine->getQuitting())
saveQuickMatchOptions();
parentWOLQuickMatch = NULL;
buttonBack = NULL;
quickmatchTextWindow = NULL;
selectedImage = unselectedImage = NULL;
isShuttingDown = true;
// if we are shutting down for an immediate pop, skip the animations
Bool popImmediate = *(Bool *)userData;
if( popImmediate )
{
shutdownComplete( layout );
return;
} //end if
TheShell->reverseAnimatewindow();
TheTransitionHandler->reverse("WOLQuickMatchMenuFade");
RaiseGSMessageBox();
} // WOLQuickMatchMenuShutdown
#ifdef PERF_TEST
static const char* getMessageString(Int t)
{
switch(t)
{
case PeerResponse::PEERRESPONSE_LOGIN:
return "login";
case PeerResponse::PEERRESPONSE_DISCONNECT:
return "disconnect";
case PeerResponse::PEERRESPONSE_MESSAGE:
return "message";
case PeerResponse::PEERRESPONSE_GROUPROOM:
return "group room";
case PeerResponse::PEERRESPONSE_STAGINGROOM:
return "staging room";
case PeerResponse::PEERRESPONSE_STAGINGROOMPLAYERINFO:
return "staging room player info";
case PeerResponse::PEERRESPONSE_JOINGROUPROOM:
return "group room join";
case PeerResponse::PEERRESPONSE_CREATESTAGINGROOM:
return "staging room create";
case PeerResponse::PEERRESPONSE_JOINSTAGINGROOM:
return "staging room join";
case PeerResponse::PEERRESPONSE_PLAYERJOIN:
return "player join";
case PeerResponse::PEERRESPONSE_PLAYERLEFT:
return "player part";
case PeerResponse::PEERRESPONSE_PLAYERCHANGEDNICK:
return "player nick";
case PeerResponse::PEERRESPONSE_PLAYERINFO:
return "player info";
case PeerResponse::PEERRESPONSE_PLAYERCHANGEDFLAGS:
return "player flags";
case PeerResponse::PEERRESPONSE_ROOMUTM:
return "room UTM";
case PeerResponse::PEERRESPONSE_PLAYERUTM:
return "player UTM";
case PeerResponse::PEERRESPONSE_QUICKMATCHSTATUS:
return "QM status";
case PeerResponse::PEERRESPONSE_GAMESTART:
return "game start";
case PeerResponse::PEERRESPONSE_FAILEDTOHOST:
return "host failure";
}
return "unknown";
}
#endif // PERF_TEST
//-------------------------------------------------------------------------------------------------
/** WOL Quick Match Menu update method */
//-------------------------------------------------------------------------------------------------
void WOLQuickMatchMenuUpdate( WindowLayout * layout, void *userData)
{
if (TheGameLogic->isInShellGame() && TheGameLogic->getFrame() == 1)
{
SignalUIInteraction(SHELL_SCRIPT_HOOK_GENERALS_ONLINE_ENTERED_FROM_GAME);
}
// We'll only be successful if we've requested to
if(isShuttingDown && TheShell->isAnimFinished()&& TheTransitionHandler->isFinished())
shutdownComplete(layout);
if (raiseMessageBoxes)
{
RaiseGSMessageBox();
raiseMessageBoxes = false;
}
/// @todo: MDC handle disconnects in-game the same way as Custom Match!
if (TheShell->isAnimFinished() && !buttonPushed && TheGameSpyPeerMessageQueue)
{
HandleBuddyResponses();
HandlePersistentStorageResponses();
if (TheGameSpyGame && TheGameSpyGame->isGameInProgress())
{
if (TheGameSpyInfo->isDisconnectedAfterGameStart(NULL))
{
return; // already been disconnected, so don't worry.
}
Int allowedMessages = TheGameSpyInfo->getMaxMessagesPerUpdate();
Bool sawImportantMessage = FALSE;
PeerResponse resp;
while (allowedMessages-- && !sawImportantMessage && TheGameSpyPeerMessageQueue->getResponse( resp ))
{
switch (resp.peerResponseType)
{
case PeerResponse::PEERRESPONSE_DISCONNECT:
{
sawImportantMessage = TRUE;
AsciiString disconMunkee;
disconMunkee.format("GUI:GSDisconReason%d", resp.discon.reason);
// check for scorescreen
NameKeyType listboxChatWindowScoreScreenID = NAMEKEY("ScoreScreen.wnd:ListboxChatWindowScoreScreen");
GameWindow *listboxChatWindowScoreScreen = TheWindowManager->winGetWindowFromId( NULL, listboxChatWindowScoreScreenID );
if (listboxChatWindowScoreScreen)
{
GadgetListBoxAddEntryText(listboxChatWindowScoreScreen, TheGameText->fetch(disconMunkee),
GameSpyColor[GSCOLOR_DEFAULT], -1);
}
else
{
// still ingame
TheInGameUI->message(disconMunkee);
}
TheGameSpyInfo->markAsDisconnectedAfterGameStart(resp.discon.reason);
}
}
}
return; // if we're in game, all we care about is if we've been disconnected from the chat server
}
if (TheNAT != NULL) {
NATStateType NATState = TheNAT->update();
if (NATState == NATSTATE_DONE)
{
TheGameSpyGame->launchGame();
if (TheGameSpyInfo) // this can be blown away by a disconnect on the map transfer screen
TheGameSpyInfo->leaveStagingRoom();
return; // don't do any more processing this frame, in case the screen goes away
}
else if (NATState == NATSTATE_FAILED)
{
// delete TheNAT, its no good for us anymore.
delete TheNAT;
TheNAT = NULL;
// Just back out. This cleans up some slot list problems
buttonPushed = true;
GSMessageBoxOk(TheGameText->fetch("GUI:Error"), TheGameText->fetch("GUI:NATNegotiationFailed"));
nextScreen = "Menus/WOLWelcomeMenu.wnd";
TheShell->pop();
return; // don't do any more processing this frame, in case the screen goes away
}
}
#ifdef PERF_TEST
UnsignedInt start = timeGetTime();
UnsignedInt end = timeGetTime();
std::list responses;
Int numMessages = 0;
#endif // PERF_TEST
Int allowedMessages = TheGameSpyInfo->getMaxMessagesPerUpdate();
Bool sawImportantMessage = FALSE;
PeerResponse resp;
while (allowedMessages-- && !sawImportantMessage && TheGameSpyPeerMessageQueue->getResponse( resp ))
{
#ifdef PERF_TEST
++numMessages;
responses.push_back(resp.peerResponseType);
#endif // PERF_TEST
switch (resp.peerResponseType)
{
case PeerResponse::PEERRESPONSE_PLAYERUTM:
{
if (!stricmp(resp.command.c_str(), "STATS"))
{
DEBUG_LOG(("Saw STATS from %s, data was '%s'\n", resp.nick.c_str(), resp.commandOptions.c_str()));
AsciiString data = resp.commandOptions.c_str();
AsciiString idStr;
data.nextToken(&idStr, " ");
Int id = atoi(idStr.str());
DEBUG_LOG(("data: %d(%s) - '%s'\n", id, idStr.str(), data.str()));
PSPlayerStats stats = TheGameSpyPSMessageQueue->parsePlayerKVPairs(data.str());
PSPlayerStats oldStats = TheGameSpyPSMessageQueue->findPlayerStatsByID(id);
stats.id = id;
DEBUG_LOG(("Parsed ID is %d, old ID is %d\n", stats.id, oldStats.id));
if (stats.id && (oldStats.id == 0))
TheGameSpyPSMessageQueue->trackPlayerStats(stats);
// now fill in the profileID in the game slot
AsciiString nick = resp.nick.c_str();
for (Int i=0; igetGameSpySlot(i);
if (slot && slot->isHuman() && (slot->getLoginName().compareNoCase(nick) == 0))
{
slot->setProfileID(id);
break;
}
}
}
Int slotNum = TheGameSpyGame->getSlotNum(resp.nick.c_str());
if ((slotNum >= 0) && (slotNum < MAX_SLOTS) && (!stricmp(resp.command.c_str(), "NAT"))) {
// this is a command for NAT negotiations, pass if off to TheNAT
sawImportantMessage = TRUE;
if (TheNAT != NULL) {
TheNAT->processGlobalMessage(slotNum, resp.commandOptions.c_str());
}
}
/*
else if (key == "NAT")
{
if ((val >= FirewallHelperClass::FIREWALL_TYPE_SIMPLE) &&
(val <= FirewallHelperClass::FIREWALL_TYPE_DESTINATION_PORT_DELTA))
{
slot->setNATBehavior((FirewallHelperClass::FirewallBehaviorType)val);
DEBUG_LOG(("Setting NAT behavior to %d for player %d\n", val, slotNum));
change = true;
}
else
{
DEBUG_LOG(("Rejecting invalid NAT behavior %d from player %d\n", val, slotNum));
}
}
*/
}
break;
case PeerResponse::PEERRESPONSE_DISCONNECT:
{
sawImportantMessage = TRUE;
UnicodeString title, body;
AsciiString disconMunkee;
disconMunkee.format("GUI:GSDisconReason%d", resp.discon.reason);
title = TheGameText->fetch( "GUI:GSErrorTitle" );
body = TheGameText->fetch( disconMunkee );
GameSpyCloseAllOverlays();
GSMessageBoxOk( title, body );
TheGameSpyInfo->reset();
TheShell->pop();
}
case PeerResponse::PEERRESPONSE_QUICKMATCHSTATUS:
{
sawImportantMessage = TRUE;
switch( resp.qmStatus.status )
{
case QM_IDLE:
//TheGameSpyInfo->addText(UnicodeString(L"Status: QM_IDLE"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
break;
case QM_JOININGQMCHANNEL:
TheGameSpyInfo->addText(TheGameText->fetch("QM:JOININGQMCHANNEL"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
break;
case QM_LOOKINGFORBOT:
TheGameSpyInfo->addText(TheGameText->fetch("QM:LOOKINGFORBOT"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
break;
case QM_SENTINFO:
TheGameSpyInfo->addText(TheGameText->fetch("QM:SENTINFO"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
break;
case QM_WORKING:
{
UnicodeString s;
s.format(TheGameText->fetch("QM:WORKING"), resp.qmStatus.poolSize);
TheGameSpyInfo->addText(s, GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
}
buttonWiden->winEnable( TRUE );
break;
case QM_POOLSIZE:
{
UnicodeString s;
s.format(TheGameText->fetch("QM:POOLSIZE"), resp.qmStatus.poolSize);
TheGameSpyInfo->addText(s, GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
}
break;
case QM_WIDENINGSEARCH:
TheGameSpyInfo->addText(TheGameText->fetch("QM:WIDENINGSEARCH"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
buttonWiden->winEnable( FALSE );
break;
case QM_MATCHED:
{
TheGameSpyInfo->addText(TheGameText->fetch("QM:MATCHED"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
buttonWiden->winEnable( FALSE );
TheGameSpyGame->enterGame();
TheGameSpyGame->setSeed(resp.qmStatus.seed);
TheGameSpyGame->markGameAsQM();
const LadderInfo *info = getLadderInfo();
if (!info)
{
TheGameSpyGame->setLadderIP("localhost");
TheGameSpyGame->setLadderPort(0);
}
else
{
TheGameSpyGame->setLadderIP(info->address);
TheGameSpyGame->setLadderPort(info->port);
}
Int i;
Int numPlayers = 0;
for (i=0; i maps = TheGameSpyConfig->getQMMaps();
#if VARIABLE_NUMBER_OF_MAPS
std::list::const_iterator it = maps.begin();
std::advance(it, resp.qmStatus.mapIdx);
AsciiString theMap = *it;
theMap.toLower();
TheGameSpyGame->setMap(theMap);
#else
for (std::list::const_iterator it = maps.begin(); it != maps.end(); ++it)
{
AsciiString theMap = *it;
theMap.toLower();
const MapMetaData *md = TheMapCache->findMap(theMap);
if (md && md->m_numPlayers >= numPlayers)
{
TheGameSpyGame->setMap(*it);
if (resp.qmStatus.mapIdx-- == 0)
break;
}
}
#endif
Int numPlayersPerTeam = numPlayers/2;
DEBUG_ASSERTCRASH(numPlayersPerTeam, ("0 players per team???"));
if (!numPlayersPerTeam)
numPlayersPerTeam = 1;
for (i=0; igetGameSpySlot(i);
if (resp.stagingRoomPlayerNames[i].empty())
{
slot->setState(SLOT_CLOSED);
}
else
{
AsciiString aName = resp.stagingRoomPlayerNames[i].c_str();
UnicodeString uName;
uName.translate(aName);
slot->setState(SLOT_PLAYER, uName, resp.qmStatus.IP[i]);
slot->setColor(resp.qmStatus.color[i]);
slot->setPlayerTemplate(resp.qmStatus.side[i]);
//slot->setProfileID(0);
slot->setNATBehavior((FirewallHelperClass::FirewallBehaviorType)resp.qmStatus.nat[i]);
slot->setLocale("");
slot->setTeamNumber( i/numPlayersPerTeam );
if (i==0)
TheGameSpyGame->setGameName(uName);
}
}
DEBUG_LOG(("Starting a QM game: options=[%s]\n", GameInfoToAsciiString(TheGameSpyGame).str()));
SendStatsToOtherPlayers(TheGameSpyGame);
TheGameSpyGame->startGame(0);
GameWindow *buttonBuddies = TheWindowManager->winGetWindowFromId(NULL, buttonBuddiesID);
if (buttonBuddies)
buttonBuddies->winEnable(FALSE);
GameSpyCloseOverlay(GSOVERLAY_BUDDY);
}
break;
case QM_INCHANNEL:
TheGameSpyInfo->addText(TheGameText->fetch("QM:INCHANNEL"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
break;
case QM_NEGOTIATINGFIREWALLS:
TheGameSpyInfo->addText(TheGameText->fetch("QM:NEGOTIATINGFIREWALLS"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
break;
case QM_STARTINGGAME:
TheGameSpyInfo->addText(TheGameText->fetch("QM:STARTINGGAME"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
break;
case QM_COULDNOTFINDBOT:
TheGameSpyInfo->addText(TheGameText->fetch("QM:COULDNOTFINDBOT"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
buttonWiden->winEnable( FALSE );
buttonStart->winHide( FALSE );
buttonStop->winHide( TRUE );
enableOptionsGadgets(TRUE);
break;
case QM_COULDNOTFINDCHANNEL:
TheGameSpyInfo->addText(TheGameText->fetch("QM:COULDNOTFINDCHANNEL"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
buttonWiden->winEnable( FALSE );
buttonStart->winHide( FALSE );
buttonStop->winHide( TRUE );
enableOptionsGadgets(TRUE);
break;
case QM_COULDNOTNEGOTIATEFIREWALLS:
TheGameSpyInfo->addText(TheGameText->fetch("QM:COULDNOTNEGOTIATEFIREWALLS"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
buttonWiden->winEnable( FALSE );
buttonStart->winHide( FALSE );
buttonStop->winHide( TRUE );
enableOptionsGadgets(TRUE);
break;
case QM_STOPPED:
TheGameSpyInfo->addText(TheGameText->fetch("QM:STOPPED"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
buttonWiden->winEnable( FALSE );
buttonStart->winHide( FALSE );
buttonStop->winHide( TRUE );
enableOptionsGadgets(TRUE);
break;
}
}
break;
}
}
#ifdef PERF_TEST
// check performance
end = timeGetTime();
UnsignedInt frameTime = end-start;
if (frameTime > 100 || responses.size() > 20)
{
UnicodeString munkee;
munkee.format(L"inQM:%d %d ms, %d messages", s_inQM, frameTime, responses.size());
TheGameSpyInfo->addText(munkee, GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
PERF_LOG(("%ls\n", munkee.str()));
std::list::const_iterator it;
for (it = responses.begin(); it != responses.end(); ++it)
{
PERF_LOG((" %s\n", getMessageString(*it)));
}
}
#endif // PERF_TEST
}
}// WOLQuickMatchMenuUpdate
//-------------------------------------------------------------------------------------------------
/** WOL Quick Match Menu input callback */
//-------------------------------------------------------------------------------------------------
WindowMsgHandledType WOLQuickMatchMenuInput( 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 ) )
{
if(!buttonBack->winIsHidden())
TheWindowManager->winSendSystemMsg( window, GBM_SELECTED,
(WindowMsgData)buttonBack, buttonBackID );
} // 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;
}// WOLQuickMatchMenuInput
//-------------------------------------------------------------------------------------------------
/** WOL Quick Match Menu window system callback */
//-------------------------------------------------------------------------------------------------
WindowMsgHandledType WOLQuickMatchMenuSystem( GameWindow *window, UnsignedInt msg,
WindowMsgData mData1, WindowMsgData mData2 )
{
UnicodeString txtInput;
switch( msg )
{
case GWM_CREATE:
{
break;
} // case GWM_DESTROY:
case GWM_DESTROY:
{
break;
} // case GWM_DESTROY:
case GWM_INPUT_FOCUS:
{
// if we're givin the opportunity to take the keyboard focus we must say we want it
if( mData1 == TRUE )
*(Bool *)mData2 = TRUE;
return MSG_HANDLED;
}//case GWM_INPUT_FOCUS:
case GCM_SELECTED:
{
if (buttonPushed)
break;
GameWindow *control = (GameWindow *)mData1;
Int controlID = control->winGetWindowId();
Int pos = -1;
GadgetComboBoxGetSelectedPos(control, &pos);
saveQuickMatchOptions();
if (controlID == comboBoxLadderID && !isPopulatingLadderBox)
{
if (pos >= 0)
{
QuickMatchPreferences pref;
Int ladderID = (Int)GadgetComboBoxGetItemData(control, pos);
if (ladderID == 0)
{
// no ladder selected - enable buttons
GadgetComboBoxSetSelectedPos(comboBoxNumPlayers, max(0, pref.getNumPlayers()/2-1));
comboBoxNumPlayers->winEnable( TRUE );
populateQMSideComboBox(pref.getSide()); // this will set side to random and disable if necessary
}
else if (ladderID > 0)
{
// ladder selected - disable buttons
const LadderInfo *li = TheLadderList->findLadderByIndex(ladderID);
if (li)
GadgetComboBoxSetSelectedPos(comboBoxNumPlayers, li->playersPerTeam-1);
else
GadgetComboBoxSetSelectedPos(comboBoxNumPlayers, 0);
comboBoxNumPlayers->winEnable( FALSE );
populateQMSideComboBox(pref.getSide(), li); // this will set side to random and disable if necessary
}
else
{
// "Choose a ladder" selected - open overlay
PopulateQMLadderComboBox(); // this restores the non-"Choose a ladder" selection
GameSpyOpenOverlay( GSOVERLAY_LADDERSELECT );
}
}
}
if (!isInInit)
{
QuickMatchPreferences pref;
populateQuickMatchMapSelectListbox(pref);
UpdateStartButton();
}
break;
} // case GCM_SELECTED
case GBM_SELECTED:
{
if (buttonPushed)
break;
GameWindow *control = (GameWindow *)mData1;
Int controlID = control->winGetWindowId();
static NameKeyType buttonOptionsID = NAMEKEY("WOLQuickMatchMenu.wnd:ButtonOptions");
if ( controlID == buttonStopID )
{
PeerRequest req;
req.peerRequestType = PeerRequest::PEERREQUEST_STOPQUICKMATCH;
TheGameSpyPeerMessageQueue->addRequest(req);
buttonWiden->winEnable( FALSE );
buttonStart->winHide( FALSE );
buttonStop->winHide( TRUE );
enableOptionsGadgets(TRUE);
TheGameSpyInfo->addText(TheGameText->fetch("GUI:QMAborted"), GameSpyColor[GSCOLOR_DEFAULT], quickmatchTextWindow);
}
else if ( controlID == buttonOptionsID )
{
GameWindow *win =TheWindowManager->winGetWindowFromId(parentWOLQuickMatch,buttonOptionsID);
if (isInfoShown())
{
hideInfoGadgets(TRUE);
hideOptionsGadgets(FALSE);
GadgetButtonSetText(win, TheGameText->fetch("GUI:PlayerInfo"));
}
else
{
hideInfoGadgets(FALSE);
hideOptionsGadgets(TRUE);
GadgetButtonSetText(win, TheGameText->fetch("GUI:Setup"));
}
}
else if ( controlID == buttonWidenID )
{
PeerRequest req;
req.peerRequestType = PeerRequest::PEERREQUEST_WIDENQUICKMATCHSEARCH;
TheGameSpyPeerMessageQueue->addRequest(req);
buttonWiden->winEnable( FALSE );
}
else if ( controlID == buttonStartID )
{
PeerRequest req;
req.peerRequestType = PeerRequest::PEERREQUEST_STARTQUICKMATCH;
req.qmMaps.clear();
#if VARIABLE_NUMBER_OF_MAPS
for (MapListboxIndex::iterator idxIt = mapListboxIndex.begin(); idxIt != mapListboxIndex.end(); ++idxIt) {
Int index = (*idxIt);
if (index >= 0)
{
req.qmMaps.push_back(GadgetListBoxGetItemData(listboxMapSelect, index, 0));
}
else
{
req.qmMaps.push_back(false);
}
}
#else
Int numMaps = GadgetListBoxGetNumEntries(listboxMapSelect);
for ( Int i=0; i= maxPingEntries - 1)
{
req.QM.maxPing = TheGameSpyConfig->getPingTimeoutInMs();
}
else
req.QM.maxPing = (val+1)*100;
PSPlayerStats stats = TheGameSpyPSMessageQueue->findPlayerStatsByID(TheGameSpyInfo->getLocalProfileID());
req.QM.points = CalculateRank(stats);
Int ladderIndex, index, selected;
GadgetComboBoxGetSelectedPos( comboBoxLadder, &selected );
ladderIndex = (Int)GadgetComboBoxGetItemData( comboBoxLadder, selected );
const LadderInfo *ladderInfo = NULL;
if (ladderIndex < 0)
{
ladderIndex = 0;
}
if (ladderIndex)
{
ladderInfo = TheLadderList->findLadderByIndex( ladderIndex );
if (!ladderInfo)
{
ladderIndex = 0; // sanity
}
}
req.QM.ladderID = ladderIndex;
req.QM.ladderPassCRC = 0;
index = -1;
GadgetComboBoxGetSelectedPos( comboBoxSide, &selected );
if (selected >= 0)
index = (Int)GadgetComboBoxGetItemData( comboBoxSide, selected );
req.QM.side = index;
if (ladderInfo && ladderInfo->randomFactions)
{
Int sideNum = GameClientRandomValue(0, ladderInfo->validFactions.size()-1);
DEBUG_LOG(("Looking for %d out of %d random sides\n", sideNum, ladderInfo->validFactions.size()));
AsciiStringListConstIterator cit = ladderInfo->validFactions.begin();
while (sideNum)
{
++cit;
--sideNum;
}
if (cit != ladderInfo->validFactions.end())
{
Int numPlayerTemplates = ThePlayerTemplateStore->getPlayerTemplateCount();
AsciiString sideStr = *cit;
DEBUG_LOG(("Chose %s as our side... finding\n", sideStr.str()));
for (Int c=0; cgetNthPlayerTemplate(c);
if (fac && fac->getSide() == sideStr)
{
DEBUG_LOG(("Found %s in index %d\n", sideStr.str(), c));
req.QM.side = c;
break;
}
}
}
}
index = -1;
GadgetComboBoxGetSelectedPos( comboBoxColor, &selected );
if (selected >= 0)
index = (Int)GadgetComboBoxGetItemData( comboBoxColor, selected );
req.QM.color = index;
OptionPreferences natPref;
req.QM.NAT = natPref.getFirewallBehavior();
if (ladderIndex)
{
req.QM.numPlayers = (ladderInfo)?ladderInfo->playersPerTeam*2 : 2;
}
else
{
GadgetComboBoxGetSelectedPos(comboBoxNumPlayers, &val);
if (val < 0)
val = 0;
req.QM.numPlayers = (val+1)*2;
}
Int numDiscons = 0;
PerGeneralMap::iterator it;
for(it =stats.discons.begin(); it != stats.discons.end(); ++it)
{
numDiscons += it->second;
}
for(it =stats.desyncs.begin(); it != stats.desyncs.end(); ++it)
{
numDiscons += it->second;
}
req.QM.discons = numDiscons;
strncpy(req.QM.pings, TheGameSpyInfo->getPingString().str(), 17);
req.QM.pings[16] = 0;
req.QM.botID = TheGameSpyConfig->getQMBotID();
req.QM.roomID = TheGameSpyConfig->getQMChannel();
req.QM.exeCRC = TheGlobalData->m_exeCRC;
req.QM.iniCRC = TheGlobalData->m_iniCRC;
TheGameSpyPeerMessageQueue->addRequest(req);
buttonWiden->winEnable( FALSE );
buttonStart->winHide( TRUE );
buttonStop->winHide( FALSE );
enableOptionsGadgets(FALSE);
if (ladderIndex > 0)
{
// save the ladder as being played upon even if we cancel out of matching early...
LadderPreferences ladPref;
ladPref.loadProfile( TheGameSpyInfo->getLocalProfileID() );
LadderPref p;
p.lastPlayDate = time(NULL);
p.address = ladderInfo->address;
p.port = ladderInfo->port;
p.name = ladderInfo->name;
ladPref.addRecentLadder( p );
ladPref.write();
}
}
else if ( controlID == buttonBuddiesID )
{
GameSpyToggleOverlay( GSOVERLAY_BUDDY );
}
else if ( controlID == buttonBackID )
{
buttonPushed = true;
TheGameSpyInfo->leaveGroupRoom();
nextScreen = "Menus/WOLWelcomeMenu.wnd";
TheShell->pop();
} //if ( controlID == buttonBack )
else if ( controlID == buttonSelectAllMapsID )
{
Int numMaps = GadgetListBoxGetNumEntries(listboxMapSelect);
for ( Int i=0; iwinGetWindowId();
Int selected = (Int)mData2;
if ( controlID == listboxMapSelectID )
{
const LadderInfo *li = getLadderInfo();
if (selected >= 0 && (!li || !li->randomMaps))
{
Bool wasSelected = (Bool)GadgetListBoxGetItemData(control, selected, 0);
GadgetListBoxSetItemData(control, (void *)(!wasSelected), selected, 0);
Int width = 10;
Int height = 10;
const Image *img = (!wasSelected)?selectedImage:unselectedImage;
if ( img )
{
width = min(GadgetListBoxGetColumnWidth(control, 0), img->getImageWidth());
height = width;
}
GadgetListBoxAddEntryImage(control, img, selected, 0, height, width);
GadgetListBoxAddEntryText(control, GadgetListBoxGetText(control, selected, 1), GameSpyColor[(wasSelected)?GSCOLOR_MAP_UNSELECTED:GSCOLOR_MAP_SELECTED], selected, 1);
}
if (selected >= 0)
GadgetListBoxSetSelected(control, -1);
}
UpdateStartButton();
break;
}// case GLM_SELECTED
case GEM_EDIT_DONE:
{
break;
}
default:
return MSG_IGNORED;
}//Switch
return MSG_HANDLED;
}// WOLQuickMatchMenuSystem