This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/Commando/dlgmpwolgamelist.cpp

1517 lines
40 KiB
C++
Raw Permalink Normal View History

/*
** Command & Conquer Renegade(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 <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : Combat *
* *
* $Archive:: /Commando/Code/Commando/dlgmpwolgamelist.cpp $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 11/24/02 4:52p $*
* *
* $Revision:: 76 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "DlgMPWOLGameList.h"
#include "specialbuilds.h"
#include "cNetwork.h"
#include "BandwidthCheck.h"
#include "UserOptions.h"
#include "GameInitMgr.h"
#include "DlgMPWOLPageBuddy.h"
#include "DlgMessageBox.h"
#include "DlgPasswordPrompt.h"
#include <Combat\PlayerType.h>
#include "WOLJoinGame.h"
#include "WOLLogonMgr.h"
#include <WWOnline\WOLServer.h>
#include <WWOnline\WOLChannel.h>
#include <WWUI\DialogMgr.h>
#include <WWUI\ShortcutBarCtrl.h>
#include <WWUI\ButtonCtrl.h>
#include <WWUI\CheckBoxCtrl.h>
#include <WWUI\ListCtrl.h>
#include "String_IDs.h"
#include <WWTranslateDB\TranslateDB.h>
#include "dlgmplanhostoptions.h"
#include "debug.h"
#include "init.h"
using namespace WWOnline;
// Game List columns
static enum
{
COL_ICON = 0,
COL_HOST_NAME,
COL_GAME_TITLE,
COL_GAME_MAP,
COL_PLAYERS,
COL_PING
};
#define CLAN_ENTRY_MARKER 0x0C1A0C1A
#define PING_GREEN_THRESHOLD 655
#define PING_YELLOW_THRESHOLD 2621
// Sorting flags for game attributes
#define FLAGSORT_DEDICATED 0x01
#define FLAGSORT_LADDERED 0x02
#define FLAGSORT_PASSWORDED 0x04
#define FLAGSORT_CLAN 0x08
static void SetGameTypeFlags(ListCtrlClass* list, int itemIndex, const WOLGameInfo& gameInfo);
static void SetPingTimeIcon(ListCtrlClass* list, int itemIndex, long pingTime);
static int CALLBACK FlagsSortCallback(ListCtrlClass* list, int item1, int item2, uint32 param);
static int CALLBACK NumericSortCallback(ListCtrlClass* list, int item1, int item2, uint32 param);
static int CALLBACK AlphaSortCallback(ListCtrlClass* list, int index1, int index2, uint32 param);
MPWolGameListMenuClass* MPWolGameListMenuClass::_mInstance = NULL;
/******************************************************************************
*
* NAME
* MPWolGameListMenuClass::DoDialog
*
* DESCRIPTION
* Display the Westwood Online advanced game listings dialog.
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
******************************************************************************/
void MPWolGameListMenuClass::DoDialog(void)
{
// Create the dialog if necessary, otherwise simply bring it to the front
if (_mInstance == NULL)
{
MPWolGameListMenuClass* dialog = new MPWolGameListMenuClass;
WWASSERT(dialog != NULL && "Failed to create WOL GameList dialog");
if (dialog)
{
dialog->Start_Dialog();
dialog->Release_Ref();
}
}
else
{
if (_mInstance->Is_Active_Menu() == false)
{
DialogMgrClass::Rollback(_mInstance);
}
}
}
/******************************************************************************
*
* NAME
* MPWolGameListMenuClass::MPWolGameListMenuClass
*
* DESCRIPTION
* Constructor
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
******************************************************************************/
MPWolGameListMenuClass::MPWolGameListMenuClass(void) :
MenuDialogClass(IDD_MP_WOL_GAME_LIST),
mChannelListPending(false),
mSortColumn(COL_HOST_NAME),
mIsSortAscending(true),
mSortFlags(0)
{
WWDEBUG_SAY(("MPWolGameListMenuClass Instantiated\n"));
WWASSERT(_mInstance == NULL);
_mInstance = this;
mWOLSession = Session::GetInstance(false);
WWASSERT_PRINT(mWOLSession.IsValid(), "WOLSession not instantiated!");
}
/******************************************************************************
*
* NAME
* MPWolGameListMenuClass::~MPWolGameListMenuClass
*
* DESCRIPTION
* Destructor
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
******************************************************************************/
MPWolGameListMenuClass::~MPWolGameListMenuClass(void)
{
WWDEBUG_SAY(("MPWolGameListMenuClass Destroyed\n"));
_mInstance = NULL;
}
/******************************************************************************
*
* NAME
* MPWolGameListMenuClass::On_Init_Dialog
*
* DESCRIPTION
* One time dialog initialization
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
******************************************************************************/
void MPWolGameListMenuClass::On_Init_Dialog(void)
{
#ifdef FREEDEDICATEDSERVER
Enable_Dlg_Item(IDC_JOIN_GAME_BUTTON, false);
#endif // FREEDEDICATEDSERVER
//---------------------------------------------------------------------------
// Configure the shortcut bar
//---------------------------------------------------------------------------
ShortcutBarCtrlClass* bar = (ShortcutBarCtrlClass*)Get_Dlg_Item(IDC_SHORTCUT_BAR);
if (bar)
{
bar->Add_Button(IDC_MP_SHORTCUT_CHAT, TRANSLATE(IDS_MP_SHORTCUT_CHAT));
bar->Add_Button(IDC_MP_SHORTCUT_BUDDIES, TRANSLATE(IDS_MP_SHORTCUT_BUDDIES));
bar->Add_Button(IDC_MP_SHORTCUT_INTERNET_OPTIONS, TRANSLATE(IDS_INTERNET_OPTIONS));
bar->Add_Button(IDC_MP_SHORTCUT_NEWS, TRANSLATE(IDS_MP_SHORTCUT_NEWS));
bar->Add_Button(IDC_MP_SHORTCUT_CLANS, TRANSLATE(IDS_MP_SHORTCUT_CLANS));
bar->Add_Button(IDC_MP_SHORTCUT_RANKINGS, TRANSLATE(IDS_MP_SHORTCUT_RANKINGS));
bar->Add_Button(IDC_MP_SHORTCUT_NET_STATUS, TRANSLATE(IDS_MP_SHORTCUT_NET_STATUS));
}
//---------------------------------------------------------------------------
// Show the current login and server
//---------------------------------------------------------------------------
WideStringClass serverName(64, true);
WOLLogonMgr::GetServerName(serverName);
WideStringClass string(128, true);
string.Format(TRANSLATE (IDS_MENU_CONNECTED_TO_FORMAT), serverName);
Set_Dlg_Item_Text(IDC_SERVERNAME, string);
WideStringClass loginName(64, true);
WOLLogonMgr::GetLoginName(loginName);
string.Format(TRANSLATE (IDS_MENU_LOGIN_NAME_FORMAT), loginName);
Set_Dlg_Item_Text(IDC_LOGINNAME, string);
WideStringClass conn(BandwidthCheckerClass::Get_Bandwidth_As_String(), true);
string.Format(TRANSLATE (IDS_MENU_SPEED_FORMAT), conn);
Set_Dlg_Item_Text(IDC_CONNECTIONSPEED, string);
//---------------------------------------------------------------------------
// Configure sorting buttons
//---------------------------------------------------------------------------
ButtonCtrlClass* button = (ButtonCtrlClass*)Get_Dlg_Item(IDC_SORT_DEDICATED_BUTTON);
if (button)
{
button->Set_Bitmap("mul_spec.tga", "mul_nopts.tga");
button->Set_Tooltip_Text(TRANSLATE (IDS_MENU_TOOLTIP_SORT_01));
}
button = (ButtonCtrlClass*)Get_Dlg_Item(IDC_SORT_PASSWORD_BUTTON);
if (button)
{
button->Set_Bitmap("mul_pswrd.tga", "mul_nopts.tga");
button->Set_Tooltip_Text(TRANSLATE (IDS_MENU_TOOLTIP_SORT_02));
}
button = (ButtonCtrlClass*)Get_Dlg_Item(IDC_SORT_LADDERED_BUTTON);
if (button)
{
button->Set_Bitmap("mul_pts.tga", "mul_nopts.tga");
button->Set_Tooltip_Text(TRANSLATE (IDS_MENU_TOOLTIP_SORT_03));
}
button = (ButtonCtrlClass*)Get_Dlg_Item(IDC_SORT_CLAN_BUTTON);
if (button)
{
button->Set_Bitmap("mul_btlcln.tga", "mul_nopts.tga");
button->Set_Tooltip_Text(TRANSLATE (IDS_MENU_TOOLTIP_SORT_04));
}
//---------------------------------------------------------------------------
// Configure game listing control
//---------------------------------------------------------------------------
ListCtrlClass* list_ctrl = (ListCtrlClass*)Get_Dlg_Item(IDC_GAME_LIST_CTRL);
if (list_ctrl)
{
Vector3 color(1,1,1);
list_ctrl->Add_Column(TRANSLATE(IDS_MP_GAME_LIST_HEADER_ICON), 0.1F, color);
list_ctrl->Add_Column(TRANSLATE(IDS_MP_GAME_LIST_HEADER_HOST_NAME), 0.175F, color);
list_ctrl->Add_Column(TRANSLATE(IDS_MP_GAME_LIST_HEADER_GAME_NAME), 0.325F, color);
list_ctrl->Add_Column(TRANSLATE(IDS_MP_GAME_LIST_HEADER_GAME_MAP), 0.2F, color);
list_ctrl->Add_Column(TRANSLATE(IDS_MP_GAME_LIST_HEADER_PLAYERS), 0.1F, color);
list_ctrl->Add_Column(TRANSLATE(IDS_MP_GAME_LIST_HEADER_PING), 0.1F, color);
list_ctrl->Allow_NoSelection(true);
Observer<ChannelListEvent>::NotifyMe(*mWOLSession);
Observer<ServerError>::NotifyMe(*mWOLSession);
Observer<SquadEvent>::NotifyMe(*mWOLSession);
RequestGameList();
}
//---------------------------------------------------------------------------
// Configure game details list control
//---------------------------------------------------------------------------
list_ctrl = (ListCtrlClass*)Get_Dlg_Item(IDC_DETAILS_LIST);
if (list_ctrl)
{
list_ctrl->Allow_Selection(false);
list_ctrl->Set_Tabstop(20.0f);
}
MenuDialogClass::On_Init_Dialog();
}
/******************************************************************************
*
* NAME
* MPWolGameListMenuClass::On_Command
*
* DESCRIPTION
* Respond to command messages from UI controls
*
* INPUTS
* ID - ID of control originating the message
* Msg - Message identifier
* Param - Message specific parameter
*
* RESULT
* NONE
*
******************************************************************************/
void MPWolGameListMenuClass::On_Command(int id, int msg, DWORD param)
{
switch (id)
{
case IDC_PAGE_BUDDY_BUTTON:
START_DIALOG(MPWolPageBuddyPopupClass);
break;
case IDC_REFRESH_GAME_LIST:
RequestGameList();
break;
case IDC_SORT_DEDICATED_BUTTON:
SortGameChannels(COL_ICON, true, FLAGSORT_DEDICATED);
break;
case IDC_SORT_PASSWORD_BUTTON:
SortGameChannels(COL_ICON, true, FLAGSORT_PASSWORDED);
break;
case IDC_SORT_LADDERED_BUTTON:
SortGameChannels(COL_ICON, true, FLAGSORT_LADDERED);
break;
case IDC_SORT_CLAN_BUTTON:
SortGameChannels(COL_ICON, true, FLAGSORT_CLAN);
break;
case IDC_JOIN_GAME_BUTTON:
Join_Game();
break;
case IDC_MENU_MP_LAN_HOST_BUTTON:
// Return to the game list on exit
GameInitMgrClass::Set_WOL_Return_Dialog(RenegadeDialogMgrClass::LOC_INTERNET_GAME_LIST);
// Create the new game data
if ( PTheGameData != NULL ) {
delete PTheGameData;
PTheGameData = NULL;
}
PTheGameData = cGameData::Create_Game_Of_Type(cGameData::GAME_TYPE_CNC);
WWASSERT(PTheGameData != NULL);
// WOL games default as quickmatch servers
The_Game()->Set_QuickMatch_Server(true);
// Load server preferences.
The_Game()->Load_From_Server_Config();
// Move to the next menu
DialogBaseClass* dialog = new MPLanHostOptionsMenuClass;
dialog->Start_Dialog();
REF_PTR_RELEASE(dialog);
break;
}
MenuDialogClass::On_Command(id, msg, param);
}
/******************************************************************************
*
* NAME
* MPWolGameListMenuClass::On_Key_Down
*
* DESCRIPTION
*
* INPUTS
*
* RESULT
*
******************************************************************************/
bool MPWolGameListMenuClass::On_Key_Down(uint32 key_id, uint32 key_data)
{
if (VK_F5 == key_id)
{
RequestGameList();
return true;
}
return MenuDialogClass::On_Key_Down(key_id, key_data);
}
/******************************************************************************
*
* NAME
* MPWolGameListMenuClass::Join_Game
*
* DESCRIPTION
* Join the currently selected game
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
******************************************************************************/
void MPWolGameListMenuClass::Join_Game(void)
{
ListCtrlClass* list = (ListCtrlClass*)Get_Dlg_Item(IDC_GAME_LIST_CTRL);
if (list)
{
// Get the currently selected index
int index = list->Get_Curr_Sel();
if (index >= 0)
{
RefPtr<ChannelData> channel = (ChannelData*)list->Get_Entry_Data(index, COL_HOST_NAME);
// If the channel is invalid then do not proceed
if (!channel.IsValid())
{
return;
}
WOLGameInfo selectedGame(channel);
// If the channel data is invalid then do not proceed
if (!selectedGame.IsDataValid())
{
return;
}
// If the user cannot join this game then do not proceed.
if (!selectedGame.CanUserJoin(mWOLSession->GetCurrentUser()))
{
return;
}
// Return to the game list on exit
GameInitMgrClass::Set_WOL_Return_Dialog(RenegadeDialogMgrClass::LOC_INTERNET_GAME_LIST);
// If the game to join is passworded then it is necessary for the user to
// supply the correct password. Therefore prompt the user to enter the
// password. We will attempt to connect to the game when we receive a
// signal from the dialog that the user has entered a password.
if (channel->IsPassworded())
{
mGameToJoin = channel;
DlgPasswordPrompt::DoDialog(this);
}
else
{
// No password require
WOLJoinGame::JoinTheGame(channel->GetName(), L"", true);
}
}
}
}
/******************************************************************************
*
* NAME
* MPWolGameListMenuClass::ReceiveSignal
*
* DESCRIPTION
* Handle receipt of password entered signal from the password prompt dialog.
*
* INPUTS
* Signal - Signal that the user has entered a password.
*
* RESULT
* NONE
*
******************************************************************************/
void MPWolGameListMenuClass::ReceiveSignal(DlgPasswordPrompt& passwordDialog)
{
if (mGameToJoin.IsValid())
{
WOLJoinGame::JoinTheGame(mGameToJoin->GetName(), passwordDialog.GetPassword(), true);
mGameToJoin.Release();
}
}
/******************************************************************************
*
* NAME
* MPWolGameListMenuClass::RequestGameList
*
* DESCRIPTION
* Submit a request for an updated game list.
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
******************************************************************************/
void MPWolGameListMenuClass::RequestGameList(void)
{
if (mChannelListPending == false)
{
if (mWOLSession->RequestGameChannelList())
{
mChannelListPending = true;
Enable_Dlg_Item(IDC_REFRESH_GAME_LIST, false);
ListCtrlClass* list = (ListCtrlClass*)Get_Dlg_Item(IDC_DETAILS_LIST);
if (list)
{
list->Delete_All_Entries();
list->Add_Column(L"", 1.0F, Vector3(1, 1, 1));
list->Insert_Entry(0, TRANSLATE (IDS_MENU_REQUESTING_NEW_CHANNELS));
}
}
}
}
/******************************************************************************
*
* NAME
* MPWolGameListMenuClass::UpdateChannels
*
* DESCRIPTION
* Update the list of game channels.
*
* INPUTS
* List - List control to view channels.
* Channels - List of channels
*
* RESULT
* True if the list changed.
*
******************************************************************************/
void MPWolGameListMenuClass::UpdateChannels(ListCtrlClass* list, const ChannelList& chanList)
{
// Get current selection so we can reselect it.
WideStringClass selectedGameName(64, true);
int selIndex = list->Get_Curr_Sel();
if (selIndex >= 0)
{
selectedGameName = list->Get_Entry_Text(selIndex, COL_HOST_NAME);
}
selIndex = -1;
int listIndex = list->Get_Entry_Count();
ChannelList::const_iterator chanIter = chanList.begin();
while (chanIter != chanList.end())
{
RefPtr<ChannelData> channel = (*chanIter);
if (channel.IsValid())
{
WOLGameInfo gameInfo(channel);
// Only show the channel if it is a valid game channel
if (gameInfo.IsDataValid())
{
bool newlyAdded = false;
// Check if the channel entry is already in the listbox.
int itemIndex = list->Find_Entry(COL_HOST_NAME, channel->GetName());
// If the channel was not found then add it now.
if (itemIndex == -1)
{
itemIndex = list->Insert_Entry(listIndex, L"");
newlyAdded = true;
}
// Update the channel in the listbox
if (itemIndex >= 0)
{
// Associate the channel data inside with the entry
if (newlyAdded)
{
ChannelData* rawChannel = channel.ReferencedObject();
rawChannel->AddReference();
list->Set_Entry_Data(itemIndex, COL_HOST_NAME, (unsigned long)rawChannel);
// Show the game channel name
list->Set_Entry_Text(itemIndex, COL_HOST_NAME, channel->GetName());
}
WideStringClass title(0, true);
title.Convert_From(gameInfo.Title());
list->Set_Entry_Text(itemIndex, COL_GAME_TITLE, title);
// Show the game type icons
SetGameTypeFlags(list, itemIndex, gameInfo);
// Show the map name
WideStringClass mapName(64, true);
//
// If this is a mod'd game, then display the mod_name\map_name...
//
if (gameInfo.ModName() != NULL && gameInfo.ModName()[0] != 0) {
//
// Strip off the extension for both the map and the mod package
//
char map_name[_MAX_FNAME] = { 0 };
char mod_name[_MAX_FNAME] = { 0 };
::_splitpath (gameInfo.MapName(), NULL, NULL, map_name, NULL);
::_splitpath (gameInfo.ModName(), NULL, NULL, mod_name, NULL);
//
// Create the map name from the aggregate of the mod and map
//
StringClass ascii_map_name;
ascii_map_name.Format ("%s\\%s", mod_name, map_name);
mapName.Convert_From (ascii_map_name);
} else {
mapName = gameInfo.MapName();
}
list->Set_Entry_Text(itemIndex, COL_GAME_MAP, mapName);
// Show the number of current / max players
list->Set_Entry_Data(itemIndex, COL_PLAYERS, MAKELONG(gameInfo.NumPlayers(), gameInfo.MaxPlayers()));
WideStringClass playersString(64, true);
playersString.Format(L"%u/%u", gameInfo.NumPlayers(), gameInfo.MaxPlayers());
list->Set_Entry_Text(itemIndex, COL_PLAYERS, playersString);
// Show the ping time ranking
SetPingTimeIcon(list, itemIndex, gameInfo.PingTime());
if ((selIndex == -1) && (selectedGameName.Compare_No_Case(channel->GetName()) == 0))
{
selIndex = itemIndex;
}
if (gameInfo.Version() != (unsigned long)cNetwork::Get_Exe_Key() || !gameInfo.IsMapValid())
{
ChannelData* rawChannel = (ChannelData*)list->Get_Entry_Data(itemIndex, COL_HOST_NAME);
if (rawChannel)
{
rawChannel->ReleaseReference();
}
list->Set_Entry_Data(itemIndex, COL_HOST_NAME, 0);
if (gameInfo.IsMapValid()) {
list->Set_Entry_Text(itemIndex, COL_GAME_TITLE, TRANSLATE(IDS_MENU_VERSION_MISMATCH));
}
/*
WideStringClass diagnostic;
diagnostic.Format(L"Version Mismatch (v. %u.%u)",
HIWORD(gameInfo.Version()), LOWORD(gameInfo.Version()));
list->Set_Entry_Text(itemIndex, COL_GAME_TITLE, diagnostic);
*/
Vector3 grey(0.5F, 0.5F, 0.5F);
list->Set_Entry_Color(itemIndex, COL_ICON, grey);
list->Set_Entry_Color(itemIndex, COL_HOST_NAME, grey);
list->Set_Entry_Color(itemIndex, COL_GAME_TITLE, grey);
list->Set_Entry_Color(itemIndex, COL_GAME_MAP, grey);
list->Set_Entry_Color(itemIndex, COL_PLAYERS, grey);
list->Set_Entry_Color(itemIndex, COL_PING, grey);
list->Set_Entry_Data(itemIndex, 0, 0);
}
}
}
++listIndex;
}
++chanIter;
}
// Select the previously selected entry
if ((selIndex != -1) && (list->Get_Entry_Count() > 0))
{
list->Set_Curr_Sel(selIndex);
}
}
/******************************************************************************
*
* NAME
* MPWolGameListMenuClass::HandleNotification(ChannelListEvent)
*
* DESCRIPTION
* Process the receipt of a game channel list.
*
* INPUTS
* Channels - List of channels
*
* RESULT
* NONE
*
******************************************************************************/
void MPWolGameListMenuClass::HandleNotification(ChannelListEvent& event)
{
// Ignore chat channels
if (event.GetChannelType() == 0)
{
return;
}
// Update the channel listbox
ListCtrlClass* list = (ListCtrlClass*)Get_Dlg_Item(IDC_GAME_LIST_CTRL);
if (list)
{
bool sort = false;
switch (event.GetEvent())
{
default:
case ChannelListEvent::Error:
case ChannelListEvent::NewList:
{
ChannelList& chanList = event.Subject();
bool listEmpty = chanList.empty();
ListCtrlClass* details = (ListCtrlClass*)Get_Dlg_Item(IDC_DETAILS_LIST);
if (details)
{
details->Delete_All_Entries();
if (listEmpty)
{
details->Insert_Entry(0, TRANSLATE(IDS_MENU_NO_GAME_SERVERS_FOUND));
}
}
Enable_Dlg_Item(IDC_JOIN_GAME_BUTTON, !listEmpty);
Enable_Dlg_Item(IDC_REFRESH_GAME_LIST, true);
mChannelListPending = false;
}
break;
case ChannelListEvent::Update:
UpdateChannels(list, event.Subject());
sort = true;
break;
case ChannelListEvent::Remove:
{
ChannelList& chanList = event.Subject();
ChannelList::iterator chanIter = chanList.begin();
while (chanIter != chanList.end())
{
RefPtr<ChannelData> channel = (*chanIter);
int index = list->Find_Entry(COL_HOST_NAME, channel->GetName());
if (index >= 0)
{
list->Delete_Entry(index);
sort = true;
}
++chanIter;
}
}
break;
}
if (sort)
{
SortGameChannels(mSortColumn, mIsSortAscending, mSortFlags);
}
}
}
/******************************************************************************
*
* NAME
* MPWolGameListMenuClass::On_ListCtrl_Delete_Entry
*
* DESCRIPTION
* Handle the deletion of a listbox entry
*
* INPUTS
* List - Pointer to list control originating notification.
* ID - Identifier of list control originating notification.
* Index - Index of entry being deleted
*
* RESULT
* NONE
*
******************************************************************************/
void MPWolGameListMenuClass::On_ListCtrl_Delete_Entry(ListCtrlClass* list, int id, int index)
{
if (IDC_GAME_LIST_CTRL == id)
{
ChannelData* rawChannel = (ChannelData*)list->Get_Entry_Data(index, COL_HOST_NAME);
if (rawChannel)
{
rawChannel->ReleaseReference();
}
}
}
/******************************************************************************
*
* NAME
* MPWolGameListMenuClass::On_ListCtrl_Sel_Change
*
* DESCRIPTION
* Handle user selecting a game entry.
*
* INPUTS
* List - Pointer to list control originating notification.
* ID - Identifier of list control originating notification.
* OldSel - Index of previous selection
* NewSel - Index of new selection
*
* RESULT
* NONE
*
******************************************************************************/
void MPWolGameListMenuClass::On_ListCtrl_Sel_Change(ListCtrlClass* list, int id, int oldIndex, int newIndex)
{
if (IDC_GAME_LIST_CTRL == id)
{
Enable_Dlg_Item(IDC_JOIN_GAME_BUTTON, false);
mSelectedGame.Reset();
// Clear any detailed game information.
ListCtrlClass* detailsList = (ListCtrlClass*)Get_Dlg_Item(IDC_DETAILS_LIST);
detailsList->Delete_All_Entries();
if (newIndex >= 0)
{
RefPtr<ChannelData> channel = (ChannelData*)list->Get_Entry_Data(newIndex, COL_HOST_NAME);
if (channel.IsValid())
{
mSelectedGame.ImportFromChannel(channel);
if (mSelectedGame.IsDataValid())
{
WideStringClass text(255, true);
// Show host name and exe version
// Denzil 02/14/02 The version encoded in the channel is NOT the printable version.
// Just show the EXE version until we can work this out.
#if(0)
text.Format(TRANSLATE(IDS_MENU_HOST_INFO_FORMAT), (const WCHAR*)channel->GetName(),
mSelectedGame.Title(), HIWORD(mSelectedGame.Version()), LOWORD(mSelectedGame.Version()));
#else
unsigned long verMajor = 0;
unsigned long verMinor = 0;
Get_Version_Number(&verMajor,&verMinor);
text.Format(TRANSLATE(IDS_MENU_HOST_INFO_FORMAT), (const WCHAR*)channel->GetName(),
mSelectedGame.Title(), HIWORD(verMajor), LOWORD(verMajor));
#endif
detailsList->Insert_Entry(0, text);
// If this is a clan game then show the opposing clans
if (mSelectedGame.IsClanGame()) {
GetClanVSClanString(mSelectedGame, text);
int entryIndex = detailsList->Insert_Entry(detailsList->Get_Entry_Count(), text);
detailsList->Set_Entry_Data(entryIndex, 0, CLAN_ENTRY_MARKER);
}
// Show server settings
text.Format(TRANSLATE(IDS_MENU_HOST_OPTIONS_INFO_FORMAT),
mSelectedGame.IsDedicated() ? TRANSLATE(IDS_MENU_TEXT463) : TRANSLATE(IDS_MENU_TEXT464),
mSelectedGame.IsPassworded() ? TRANSLATE(IDS_MENU_TEXT463) : TRANSLATE(IDS_MENU_TEXT464),
mSelectedGame.IsLaddered() ? TRANSLATE(IDS_MENU_TEXT463) : TRANSLATE(IDS_MENU_TEXT464),
mSelectedGame.IsQuickmatch() ? TRANSLATE(IDS_MENU_TEXT463) : TRANSLATE(IDS_MENU_TEXT464));
detailsList->Insert_Entry(detailsList->Get_Entry_Count(), text);
text.Format(TRANSLATE(IDS_MENU_HOST_OPTIONS_INFO2_FORMAT),
mSelectedGame.IsClanGame() ? TRANSLATE (IDS_MENU_ON) : TRANSLATE(IDS_MENU_OFF),
mSelectedGame.IsTeamChange() ? TRANSLATE (IDS_MENU_ON) : TRANSLATE(IDS_MENU_OFF),
mSelectedGame.IsTeamRemix() ? TRANSLATE (IDS_MENU_ON) : TRANSLATE(IDS_MENU_OFF),
mSelectedGame.IsFriendlyFire() ? TRANSLATE (IDS_MENU_ON) : TRANSLATE(IDS_MENU_OFF));
detailsList->Insert_Entry(detailsList->Get_Entry_Count(), text);
text.Format(TRANSLATE(IDS_MENU_HOST_OPTIONS_INFO3_FORMAT),
mSelectedGame.IsDriverGunner() ? TRANSLATE(IDS_MENU_ON) : TRANSLATE(IDS_MENU_OFF),
mSelectedGame.IsSpawnWeapons() ? TRANSLATE(IDS_MENU_ON) : TRANSLATE(IDS_MENU_OFF),
mSelectedGame.IsRepairBuildings() ? TRANSLATE(IDS_MENU_ON) : TRANSLATE(IDS_MENU_OFF));
detailsList->Insert_Entry(detailsList->Get_Entry_Count(), text);
}
bool canJoin = mSelectedGame.CanUserJoin(mWOLSession->GetCurrentUser());
Enable_Dlg_Item(IDC_JOIN_GAME_BUTTON, canJoin);
}
}
}
}
/******************************************************************************
*
* NAME
* MPWolGameListMenuClass::
*
* DESCRIPTION
*
* INPUTS
*
* RESULT
* NONE
*
******************************************************************************/
void MPWolGameListMenuClass::GetClanVSClanString(WOLGameInfo& gameInfo, WideStringClass& text)
{
const char* clan1Name = "____";
const char* clan2Name = "____";
RefPtr<SquadData> clan = SquadData::FindByID(gameInfo.ClanID1());
if (clan.IsValid())
{
clan1Name = clan->GetName();
}
else if (gameInfo.ClanID1() != 0)
{
clan1Name = "????";
mWOLSession->RequestSquadInfoByID(gameInfo.ClanID1());
}
clan = SquadData::FindByID(gameInfo.ClanID2());
if (clan.IsValid())
{
clan2Name = clan->GetName();
}
else if (gameInfo.ClanID2() != 0)
{
clan2Name = "????";
mWOLSession->RequestSquadInfoByID(gameInfo.ClanID2());
}
text.Format(L"%S -VS- %S", clan1Name, clan2Name);
}
/******************************************************************************
*
* NAME
* MPWolGameListMenuClass::On_ListCtrl_Column_Click
*
* DESCRIPTION
* Respond to user clicking on a column header of a list control.
*
* INPUTS
* List - Pointer to list control originating notification.
* ID - Identifier of list control originating notification.
* Column - Number of column clicked.
*
* RESULT
* NONE
*
******************************************************************************/
void MPWolGameListMenuClass::On_ListCtrl_Column_Click(ListCtrlClass* list, int id, int column)
{
if (IDC_GAME_LIST_CTRL == id)
{
// If clicking the same column then toggle ascending / descending
if (mSortColumn == column)
{
SortGameChannels(column, !mIsSortAscending, mSortFlags);
}
else
{
SortGameChannels(column, true, mSortFlags);
}
}
}
/******************************************************************************
*
* NAME
* MPWolGameListMenuClass::On_ListCtrl_DblClk
*
* DESCRIPTION
* Automatically attempt to join a game if the user double-clicks on the
* game entry.
*
* INPUTS
* List - List control
* ID - Identifier of list control
* Index - Entry index double-clicked
*
* RESULT
* NONE
*
******************************************************************************/
void MPWolGameListMenuClass::On_ListCtrl_DblClk(ListCtrlClass* list, int id, int index)
{
if (IDC_GAME_LIST_CTRL == id)
{
Join_Game();
}
}
/******************************************************************************
*
* NAME
* On_Last_Menu_Ending
*
* DESCRIPTION
* Callback from menu class signifying the close of the last menu
*
* INPUTS
*
* RESULT
*
******************************************************************************/
void MPWolGameListMenuClass::On_Last_Menu_Ending (void)
{
RenegadeDialogMgrClass::Goto_Location(RenegadeDialogMgrClass::LOC_INTERNET_MAIN);
}
/******************************************************************************
*
* NAME
* MPWolGameListMenuClass::HandleNotification(ServerError)
*
* DESCRIPTION
* Handle server error notifications from WWOnline session.
*
* INPUTS
* Error - Server error
*
* RESULT
* NONE
*
******************************************************************************/
void MPWolGameListMenuClass::HandleNotification(ServerError& error)
{
if (CHAT_E_BANNED == error.GetErrorCode())
{
// DlgMsgBox::DoDialog(TRANSLATE(IDS_WOL_SERVERMESSAGE), TRANSLATE(IDS_WOL_BANNED));
DlgMsgBox::DoDialog(TRANSLATE (IDS_MENU_SERVER_MESSAGE_TITLE), TRANSLATE (IDS_MENU_WOL_BAN_USER_MESSAGE));
End_Dialog();
}
}
/******************************************************************************
*
* NAME
* MPWolGameListMenuClass::HandleNotification(SquadEvent)
*
* DESCRIPTION
*
* INPUTS
*
* RESULT
* NONE
*
******************************************************************************/
void MPWolGameListMenuClass::HandleNotification(WWOnline::SquadEvent& event)
{
if (!mSelectedGame.IsDataValid() && mSelectedGame.IsClanGame())
{
return;
}
RefPtr<SquadData> squad = event.GetSquadData();
if (squad.IsValid())
{
unsigned long squadID = squad->GetID();
if (mSelectedGame.ClanID1() == squadID || mSelectedGame.ClanID2() == squadID)
{
ListCtrlClass* detailsList = (ListCtrlClass*)Get_Dlg_Item(IDC_DETAILS_LIST);
if (detailsList)
{
// Find entry with marker indicating clan vs clan
const int count = detailsList->Get_Entry_Count();
for (int index = 0; index < count; ++index)
{
int marker = detailsList->Get_Entry_Data(index, 0);
if (CLAN_ENTRY_MARKER == marker)
{
WideStringClass text(0, true);
GetClanVSClanString(mSelectedGame, text);
detailsList->Set_Entry_Text(index, 0, text);
break;
}
}
}
}
}
}
/******************************************************************************
*
* NAME
* SetGameTypeFlags
*
* DESCRIPTION
* Setup the game type flags in the icon column of the game listings.
*
* INPUTS
* List - Pointer to list control to work with.
* Index - Entry index to set type flags for.
* GameInfo - Game information
*
* RESULT
*
******************************************************************************/
void SetGameTypeFlags(ListCtrlClass* list, int itemIndex, const WOLGameInfo& gameInfo)
{
WWASSERT(list != NULL);
list->Reset_Icons(itemIndex, COL_ICON);
uint32 flags = 0;
if (gameInfo.IsLaddered())
{
list->Add_Icon(itemIndex, COL_ICON, "MUL_pts.tga");
flags |= FLAGSORT_LADDERED;
}
else
{
list->Add_Icon(itemIndex, COL_ICON, "MUL_NoPts.tga");
}
if (gameInfo.IsDedicated())
{
list->Add_Icon(itemIndex, COL_ICON, "MUL_Spec.tga");
flags |= FLAGSORT_DEDICATED;
}
if (gameInfo.IsPassworded())
{
list->Add_Icon(itemIndex, COL_ICON, "MUL_Pswrd.tga");
flags |= FLAGSORT_PASSWORDED;
}
if (gameInfo.IsClanGame())
{
list->Add_Icon(itemIndex, COL_ICON, "MUL_Btlcln.tga");
flags |= FLAGSORT_CLAN;
}
list->Set_Entry_Data(itemIndex, COL_ICON, flags);
}
/******************************************************************************
*
* NAME
* SetPingTimeIcon
*
* DESCRIPTION
* Set an icon reflecting the latency for this game.
*
* INPUTS
* List - List control
* Index - Entry index
* Time - Latency
*
* RESULT
* NONE
*
******************************************************************************/
void SetPingTimeIcon(ListCtrlClass* list, int itemIndex, long pingTime)
{
const char* pingIcon = NULL;
if (pingTime <= PING_GREEN_THRESHOLD)
{
pingIcon = "mul_statg.tga";
}
else if (pingTime <= PING_YELLOW_THRESHOLD)
{
pingIcon = "mul_staty.tga";
}
else
{
pingIcon = "mul_statr.tga";
}
list->Reset_Icons(itemIndex, COL_PING);
list->Add_Icon(itemIndex, COL_PING, pingIcon);
unsigned long displayPing = (unsigned long)((1000.0 / 256.0) * (sqrt(double(pingTime))));
list->Set_Entry_Data(itemIndex, COL_PING, displayPing);
WideStringClass text(32, true);
text.Format(L" (%lu)", displayPing);
list->Set_Entry_Text(itemIndex, COL_PING, text);
}
/******************************************************************************
*
* NAME
* MPWolGameListMenuClass::SortGameChannels
*
* DESCRIPTION
* Sort the game channels
*
* INPUTS
* Column - Number of column to sort by.
* Ascending - True to sort ascending; false to sort descending
* Flags - Game flags to sort by if column is COL_ICON
*
* RESULT
* NONE
*
******************************************************************************/
void MPWolGameListMenuClass::SortGameChannels(int column, bool isAscending, unsigned long param)
{
mSortColumn = column;
mIsSortAscending = isAscending;
mSortFlags = param;
ListCtrlClass* list = (ListCtrlClass*)Get_Dlg_Item(IDC_GAME_LIST_CTRL);
if (list)
{
ListCtrlClass::SORT_TYPE sortDirection = ListCtrlClass::SORT_DESCENDING;
if (isAscending)
{
sortDirection = ListCtrlClass::SORT_ASCENDING;
}
if (COL_ICON == column)
{
list->Sort(FlagsSortCallback, mSortFlags);
}
else if (COL_PLAYERS == column || COL_PING == column)
{
list->Sort(NumericSortCallback, MAKELONG(column, sortDirection));
}
else
{
list->Sort(AlphaSortCallback, MAKELONG(column, sortDirection));
}
list->Set_Sort_Designator(column, sortDirection);
}
}
/******************************************************************************
*
* NAME
* FlagSortCallback
*
* DESCRIPTION
* Sort list entries by game type flags.
*
* INPUTS
* List - List control performing sort.
* Index1 - First entry to compare
* Index2 - Second entry to compare
*
* RESULT
* 0 - Items are equal
* -1 - Item1 is less than item2
* 1 - Item1 is greater than item2
*
******************************************************************************/
int CALLBACK FlagsSortCallback(ListCtrlClass* list, int index1, int index2, uint32 mask)
{
uint32 flags1 = list->Get_Entry_Data(index1, COL_ICON);
flags1 &= mask;
uint32 flags2 = list->Get_Entry_Data(index2, COL_ICON);
flags2 &= mask;
if (flags1 && !flags2)
{
return -1;
}
else if (!flags1 && flags2)
{
return 1;
}
else if (flags1 && flags2)
{
// Secondary sort by ping time
int ping1 = list->Get_Entry_Data(index1, COL_PING);
int ping2 = list->Get_Entry_Data(index2, COL_PING);
int delta = (ping1 - ping2);
if (delta < 0)
{
return -1;
}
else if (delta > 0)
{
return 1;
}
const WCHAR* name1 = list->Get_Entry_Text(index1, COL_HOST_NAME);
const WCHAR* name2 = list->Get_Entry_Text(index2, COL_HOST_NAME);
return wcsicmp(name1, name2);
}
return 0;
}
/******************************************************************************
*
* NAME
* NumericSortCallback
*
* DESCRIPTION
* Sort list entries numerically from the numbers contained in the entries
* data field.
*
* INPUTS
* List - List control performing sort.
* Index1 - First entry to compare
* Index2 - Second entry to compare
*
* RESULT
* -1 - Item1 is less than item2
* 0 - Items are equal
* 1 - Item1 is greater than item2
*
******************************************************************************/
int CALLBACK NumericSortCallback(ListCtrlClass* list, int index1, int index2, uint32 param)
{
// Sort by numeric value stored in entry data field
int column = LOWORD(param);
uint32 data1 = list->Get_Entry_Data(index1, column);
uint32 data2 = list->Get_Entry_Data(index2, column);
int retval = (data1 - data2);
if (retval < 0)
{
retval = -1;
}
else if (retval > 0)
{
retval = 1;
}
else
{
if (column != COL_PING)
{
// Secondary sort by ping time
data1 = list->Get_Entry_Data(index1, COL_PING);
data2 = list->Get_Entry_Data(index2, COL_PING);
retval = (data1 - data2);
if (retval < 0)
{
retval = -1;
}
else if (retval > 0)
{
retval = 1;
}
}
if (retval == 0)
{
const WCHAR* name1 = list->Get_Entry_Text(index1, COL_HOST_NAME);
const WCHAR* name2 = list->Get_Entry_Text(index2, COL_HOST_NAME);
retval = wcsicmp(name1, name2);
}
}
// Invert the return value if we are sorting descendingly
ListCtrlClass::SORT_TYPE sortType = (ListCtrlClass::SORT_TYPE)HIWORD(param);
if (ListCtrlClass::SORT_DESCENDING == sortType)
{
return -retval;
}
return retval;
}
/******************************************************************************
*
* NAME
* NumericSortCallback
*
* DESCRIPTION
* Sort list entries alphbetically then by ping time.
*
* INPUTS
* List - List control performing sort.
* Index1 - First entry to compare
* Index2 - Second entry to compare
*
* RESULT
* -1 - Item1 is less than item2
* 0 - Items are equal
* 1 - Item1 is greater than item2
*
******************************************************************************/
int CALLBACK AlphaSortCallback(ListCtrlClass* list, int index1, int index2, uint32 param)
{
// Sort by numeric value stored in entry data field
int column = LOWORD(param);
const WCHAR* text1 = list->Get_Entry_Text(index1, column);
const WCHAR* text2 = list->Get_Entry_Text(index2, column);
int retval = wcsicmp(text1, text2);
// If the strings match then secondary sort by ping time.
if (retval == 0)
{
uint32 data1 = list->Get_Entry_Data(index1, COL_PING);
uint32 data2 = list->Get_Entry_Data(index2, COL_PING);
retval = (data1 - data2);
if (retval < 0)
{
retval = -1;
}
else if (retval > 0)
{
retval = 1;
}
}
// Invert the return value if we are sorting descendingly
ListCtrlClass::SORT_TYPE sortType = (ListCtrlClass::SORT_TYPE)HIWORD(param);
if (ListCtrlClass::SORT_DESCENDING == sortType)
{
return -retval;
}
return retval;
}