/* ** 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 . */ /*********************************************************************************************** *** 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/dlgmplangamelist.cpp $* * * * Author:: Patrick Smith * * * * $Modtime:: 3/28/02 4:31p $* * * * $Revision:: 41 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "dlgmplangamelist.h" #include "win.h" #include "listctrl.h" #include "gamedata.h" #include "gamechannel.h" #include "gamechanlist.h" #include "gameinitmgr.h" #include "wolgmode.h" #include "dialogmgr.h" #include "cnetwork.h" #include "ww3d.h" #include "dialogresource.h" #include "renegadedialogmgr.h" #include "translatedb.h" #include "string_ids.h" #include "dialogcontrol.h" #include "specialbuilds.h" #include "editctrl.h" #include "dlgpasswordprompt.h" #include "registry.h" #include "_globals.h" #include "dlgmplanhostoptions.h" bool MPLanGameListMenuClass::UpdateNickname = false; //////////////////////////////////////////////////////////////// // Local constants //////////////////////////////////////////////////////////////// static enum { COL_ICON = 0, COL_HOST_NAME, COL_GAME_NAME, COL_GAME_MAP, COL_PLAYERS, COL_MAX }; //////////////////////////////////////////////////////////////// // Static member initialization //////////////////////////////////////////////////////////////// MPLanGameListMenuClass * MPLanGameListMenuClass::_TheInstance = NULL; //////////////////////////////////////////////////////////////// // // MPLanGameListMenuClass // //////////////////////////////////////////////////////////////// MPLanGameListMenuClass::MPLanGameListMenuClass (void) : UpdateTimer (0), MenuDialogClass (IDD_MP_LAN_GAME_LIST) { WWDEBUG_SAY(("MPLanGameListMenu instantiated\n")); } MPLanGameListMenuClass::~MPLanGameListMenuClass() { WWDEBUG_SAY(("MPLanGameListMenu destroyed\n")); } //////////////////////////////////////////////////////////////// // // On_Init_Dialog // //////////////////////////////////////////////////////////////// void MPLanGameListMenuClass::On_Init_Dialog (void) { /* #ifdef BETACLIENT Get_Dlg_Item(IDC_MENU_MP_LAN_HOST_BUTTON)->Enable(false); #endif // BETACLIENT */ #ifdef FREEDEDICATEDSERVER Get_Dlg_Item(IDC_JOIN_GAME_BUTTON)->Enable(false); #endif // FREEDEDICATEDSERVER // // Get a pointer to the list control // ListCtrlClass *list_ctrl = (ListCtrlClass *)Get_Dlg_Item (IDC_GAME_LIST_CTRL); if (list_ctrl != NULL) { WideStringClass col_name; // // Configure the columns // col_name = TRANSLATE (IDS_MP_GAME_LIST_HEADER_ICON); list_ctrl->Add_Column (col_name, 0.1F, Vector3 (1, 1, 1)); col_name = TRANSLATE(IDS_MP_GAME_LIST_HEADER_HOST_NAME); list_ctrl->Add_Column (col_name, 0.175F, Vector3 (1, 1, 1)); col_name = TRANSLATE (IDS_MP_GAME_LIST_HEADER_GAME_NAME); list_ctrl->Add_Column (col_name, 0.325F, Vector3 (1, 1, 1)); col_name = TRANSLATE (IDS_MP_GAME_LIST_HEADER_GAME_MAP); list_ctrl->Add_Column (col_name, 0.2F, Vector3 (1, 1, 1)); col_name = TRANSLATE (IDS_MP_GAME_LIST_HEADER_PLAYERS); list_ctrl->Add_Column (col_name, 0.1F, Vector3 (1, 1, 1)); col_name = TRANSLATE (IDS_MP_GAME_LIST_HEADER_PING); list_ctrl->Add_Column (col_name, 0.1F, Vector3 (1, 1, 1)); // // Refresh the game list in one second // UpdateTimer = 500; } // // Put the nickname into the nickname edit control // EditCtrlClass* nameEdit = (EditCtrlClass*)Get_Dlg_Item(IDC_NICKNAME_EDIT); assert(nameEdit != NULL); if (nameEdit) { //nameEdit->Set_Text_Limit(32); nameEdit->Set_Text_Limit(9); nameEdit->Set_Text(cNetInterface::Get_Nickname()); bool enable = (nameEdit->Get_Text_Length() > 0); Enable_Dlg_Item(IDC_JOIN_GAME_BUTTON, enable); Enable_Dlg_Item(IDC_MENU_MP_LAN_HOST_BUTTON, enable); } cGameChannelList::Remove_All(); MenuDialogClass::On_Init_Dialog (); return ; } //////////////////////////////////////////////////////////////// // // On_Destroy // //////////////////////////////////////////////////////////////// void MPLanGameListMenuClass::On_Destroy (void) { // // Set the new nickname // WideStringClass nickname = Get_Dlg_Item_Text (IDC_NICKNAME_EDIT); cNetInterface::Set_Nickname (nickname); return ; } //////////////////////////////////////////////////////////////// // // On_Command // //////////////////////////////////////////////////////////////// void MPLanGameListMenuClass::On_Command (int ctrl_id, int message_id, DWORD param) { switch (ctrl_id) { case IDC_REFRESH_GAME_LIST: Update_Game_List (); break; case IDC_JOIN_GAME_BUTTON: Join_Game (); break; case IDC_MENU_MP_LAN_HOST_BUTTON: { // // Set the new nickname // WideStringClass nickname = Get_Dlg_Item_Text (IDC_NICKNAME_EDIT); cNetInterface::Set_Nickname (nickname); /* // // Cache this value, don't know if we need to or not anymore... // RegistryClass registry (APPLICATION_SUB_KEY_NAME_WOLSETTINGS); if (registry.Is_Valid ()) { registry.Set_Int (REG_VALUE_LAST_GAME_TYPE, 0); } */ // // 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); // LAN games are NEVER quickmatch The_Game()->Set_QuickMatch_Server(false); The_Game()->Load_From_Server_Config (); // // Go to the host game options menu // START_DIALOG (MPLanHostOptionsMenuClass); break; } } MenuDialogClass::On_Command (ctrl_id, message_id, param); return ; } //////////////////////////////////////////////////////////////// // // On_Frame_Update // //////////////////////////////////////////////////////////////// void MPLanGameListMenuClass::On_Frame_Update (void) { // // Is it time to update the list? // UpdateTimer -= WW3D::Get_Frame_Time (); if (UpdateTimer <= 0) { // // Update the list and reset the timer // Update_Game_List (); if (GameInitMgrClass::Is_WOL_Initialized ()) { UpdateTimer = 20000; } else { UpdateTimer = 1000; } } if (UpdateNickname) { EditCtrlClass * nameEdit = (EditCtrlClass*)Get_Dlg_Item(IDC_NICKNAME_EDIT); WWASSERT(nameEdit != NULL); nameEdit->Set_Text(cNetInterface::Get_Nickname()); UpdateNickname = false; } MenuDialogClass::On_Frame_Update (); return ; } bool MPLanGameListMenuClass::On_Key_Down(uint32 key_id, uint32 key_data) { if (VK_F5 == key_id) { Update_Game_List(); return true; } return MenuDialogClass::On_Key_Down(key_id, key_data); } //////////////////////////////////////////////////////////////// // // Update_Game_List // //////////////////////////////////////////////////////////////// void MPLanGameListMenuClass::Update_Game_List (void) { ListCtrlClass *list_ctrl = (ListCtrlClass *)Get_Dlg_Item (IDC_GAME_LIST_CTRL); if (list_ctrl == NULL) { return ; } // // Remember the owner of the selected entry so we can select it again as necessary // WideStringClass selected_owner_name; int curr_sel = list_ctrl->Get_Curr_Sel (); if (curr_sel != -1) { cGameChannel *channel = (cGameChannel *)list_ctrl->Get_Entry_Data (curr_sel, 0); if (channel != NULL) { selected_owner_name = channel->Get_Game_Data ()->Get_Owner (); } } // // Start fresh // list_ctrl->Delete_All_Entries (); // // Build the game list // int index = 0; bool found_selected = false; SLNode *objnode = NULL; for (objnode = cGameChannelList::Get_Chan_List ()->Head(); objnode; objnode = objnode->Next ()) { // // Get a pointer to the channel for this game // cGameChannel *channel = objnode->Data (); WWASSERT (channel != NULL); // // Insert the entry // int item_index = list_ctrl->Insert_Entry (index ++, L""); if (item_index >= 0) { // // Get the owner and game name // WideStringClass wide_owner_name; WideStringClass wide_game_name; WideStringClass wide_map_name; wide_owner_name = channel->Get_Game_Data ()->Get_Owner(); //wide_game_name = channel->Get_Game_Data ()->Get_Game_Name (); wide_game_name = channel->Get_Game_Data()->Get_Game_Title (); wide_map_name.Convert_From (channel->Get_Game_Data ()->Get_Map_Name ()); // // Get the current and max players for this channel // int player_count = channel->Get_Game_Data()->Get_Current_Players (); int player_count_max = channel->Get_Game_Data()->Get_Max_Players (); // // Build a string that represents the player count // WideStringClass players_string; players_string.Format (L"%d/%d", player_count, player_count_max); // // Build a string that represents the connection (ping) speed // int ping = 0; WideStringClass ping_string; ping_string.Format (L"%d", ping); // // If this is a mod'd game, then display the mod_name\map_name... // if (channel->Get_Game_Data()->Get_Mod_Name ().Get_Length () > 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 (channel->Get_Game_Data ()->Get_Map_Name (), NULL, NULL, map_name, NULL); ::_splitpath (channel->Get_Game_Data ()->Get_Mod_Name (), 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); wide_map_name.Convert_From (ascii_map_name); } // // Update the pla // list_ctrl->Set_Entry_Text (item_index, COL_ICON, L""); list_ctrl->Set_Entry_Text (item_index, COL_HOST_NAME, wide_owner_name); list_ctrl->Set_Entry_Text (item_index, COL_GAME_NAME, wide_game_name); list_ctrl->Set_Entry_Text (item_index, COL_GAME_MAP, wide_map_name); list_ctrl->Set_Entry_Text (item_index, COL_PLAYERS, players_string); // // Associate the channel data inside with the entry // list_ctrl->Set_Entry_Data (item_index, 0, (uint32)channel); channel->Add_Ref (); // // Should we select this entry? // if (wide_owner_name.Compare_No_Case (selected_owner_name) == 0) { list_ctrl->Set_Curr_Sel (item_index); found_selected = true; } } // // Do we have a mismatched exe or missing map? // if (channel->Get_Game_Data()->Get_Version_Number() != cNetwork::Get_Exe_Key() || channel->Get_Game_Data()->Get_Map_Name().Is_Empty()) { WideStringClass error_string; if (channel->Get_Game_Data()->Do_Exe_Versions_Match() == false) { error_string = TRANSLATE (IDS_MENU_EXE_MISMATCH); } else if (channel->Get_Game_Data()->Do_String_Versions_Match() == false) { error_string = TRANSLATE (IDS_MENU_STRINGS_MISMATCH); } else if (channel->Get_Game_Data()->Get_Map_Name().Is_Empty()) { error_string = TRANSLATE (IDS_MENU_MISSING_MAP); } // // Notify the user that they can't join this game // if (item_index >= 0) { //list_ctrl->Set_Entry_Text (item_index, COL_HOST_NAME, error_string); list_ctrl->Set_Entry_Text (item_index, COL_GAME_NAME, error_string); list_ctrl->Set_Entry_Color (item_index, COL_ICON, Vector3 (0.5F, 0.5F, 0.5F)); list_ctrl->Set_Entry_Color (item_index, COL_HOST_NAME, Vector3 (0.5F, 0.5F, 0.5F)); list_ctrl->Set_Entry_Color (item_index, COL_GAME_NAME, Vector3 (0.5F, 0.5F, 0.5F)); list_ctrl->Set_Entry_Color (item_index, COL_GAME_MAP, Vector3 (0.5F, 0.5F, 0.5F)); list_ctrl->Set_Entry_Color (item_index, COL_PLAYERS, Vector3 (0.5F, 0.5F, 0.5F)); list_ctrl->Set_Entry_Data (item_index, 1, 1); } } } // // Select the first entry by default // if (found_selected == false && list_ctrl->Get_Entry_Count () > 0) { list_ctrl->Set_Curr_Sel (0); } return ; } //////////////////////////////////////////////////////////////// // // Join_Game // //////////////////////////////////////////////////////////////// void MPLanGameListMenuClass::Join_Game (void) { ListCtrlClass *list_ctrl = (ListCtrlClass *)Get_Dlg_Item (IDC_GAME_LIST_CTRL); // // Get the currently selected index // int index = list_ctrl->Get_Curr_Sel (); if (index >= 0) { // // Get the channel data from this entry // cGameChannel *channel = (cGameChannel *)list_ctrl->Get_Entry_Data (index, 0); if (channel != NULL && list_ctrl->Get_Entry_Data (index, 1) == 0) { // // Free the old game data (if necessary) // if (PTheGameData != NULL) { delete PTheGameData; PTheGameData = NULL; } // // Create a new game data object that matches the one on the server // PTheGameData = cGameData::Create_Game_Of_Type (channel->Get_Game_Data ()->Get_Game_Type ()); WWASSERT(PTheGameData != NULL); *PTheGameData = *channel->Get_Game_Data (); // 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 (PTheGameData->IsPassworded.Is_True()) { DlgPasswordPrompt::DoDialog(this); } else { Connect_To_Server(); } } } return ; } //////////////////////////////////////////////////////////////// // // Handle receipt of password entered signal from the password // prompt dialog. // //////////////////////////////////////////////////////////////// void MPLanGameListMenuClass::ReceiveSignal(DlgPasswordPrompt& passwordDialog) { WWASSERT(PTheGameData != NULL); PTheGameData->Set_Password(passwordDialog.GetPassword()); Connect_To_Server(); } //////////////////////////////////////////////////////////////// // // Connect_To_Server // //////////////////////////////////////////////////////////////// void MPLanGameListMenuClass::Connect_To_Server (void) { // // Set the new nickname // WideStringClass nickname = Get_Dlg_Item_Text (IDC_NICKNAME_EDIT); cNetInterface::Set_Nickname (nickname); // // Start the client // cNetwork::Init_Client (); return ; } //////////////////////////////////////////////////////////////// // // On_ListCtrl_Delete_Entry // //////////////////////////////////////////////////////////////// void MPLanGameListMenuClass::On_ListCtrl_Delete_Entry ( ListCtrlClass *list_ctrl, int ctrl_id, int item_index ) { // // Get the channel data from this entry // cGameChannel *channel = (cGameChannel *)list_ctrl->Get_Entry_Data (item_index, 0); list_ctrl->Set_Entry_Data (item_index, 0, NULL); if (channel != NULL) { channel->Release_Ref (); } return ; } //////////////////////////////////////////////////////////////// // // On_ListCtrl_DblClk // //////////////////////////////////////////////////////////////// void MPLanGameListMenuClass::On_ListCtrl_DblClk ( ListCtrlClass *list_ctrl, int ctrl_id, int item_index ) { Join_Game (); return ; } void MPLanGameListMenuClass::On_EditCtrl_Change(EditCtrlClass* edit, int id) { if (IDC_NICKNAME_EDIT == id) { // Do not allow leading or trailing whitespace WideStringClass text(0, true); text = edit->Get_Text(); text.Trim(); edit->Set_Text(text); bool enable = (edit->Get_Text_Length() > 0); Enable_Dlg_Item(IDC_JOIN_GAME_BUTTON, enable); Enable_Dlg_Item(IDC_MENU_MP_LAN_HOST_BUTTON, enable); } } //////////////////////////////////////////////////////////////// // // Display // //////////////////////////////////////////////////////////////// void MPLanGameListMenuClass::Display (void) { // // Create the dialog if necessary, otherwise simply bring it to the front // if (_TheInstance == NULL) { START_DIALOG (MPLanGameListMenuClass); } else { if (_TheInstance->Is_Active_Menu () == false) { DialogMgrClass::Rollback (_TheInstance); } } return ; } //////////////////////////////////////////////////////////////// // // On_Last_Menu_Ending // //////////////////////////////////////////////////////////////// void MPLanGameListMenuClass::On_Last_Menu_Ending (void) { RenegadeDialogMgrClass::Goto_Location (RenegadeDialogMgrClass::LOC_MAIN_MENU); return ; } /* if (UpdateNickname && !g_awaiting_edit) { ::MessageBeep(MB_OK);//XXX EditCtrlClass* nameEdit = (EditCtrlClass*)Get_Dlg_Item(IDC_NICKNAME_EDIT); assert(nameEdit != NULL); nameEdit->Set_Focus(); g_awaiting_edit = true; } */ //g_awaiting_edit = false; //UpdateNickname = false; //bool g_awaiting_edit = false;//XXX //#include "dlgmplangametype.h"