WOL_GSUP.CPP (131443B)
1 // 2 // Copyright 2020 Electronic Arts Inc. 3 // 4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 5 // software: you can redistribute it and/or modify it under the terms of 6 // the GNU General Public License as published by the Free Software Foundation, 7 // either version 3 of the License, or (at your option) any later version. 8 9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 10 // in the hope that it will be useful, but with permitted additional restrictions 11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 12 // distributed with this program. You should have received a copy of the 13 // GNU General Public License along with permitted additional restrictions 14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection 15 16 17 #ifdef WOLAPI_INTEGRATION // Now implies also WINSOCK_IPX, WIN32, and FIXIT_CSII must be true 18 19 #include "wol_gsup.h" 20 #include "function.h" 21 #include "IconList.h" 22 #include <time.h> 23 #include "WolStrng.h" 24 #include "wsproto.h" 25 #include "BigCheck.h" 26 #include "ToolTip.h" 27 28 extern char const* EngMisStr[]; 29 30 bool Is_Mission_126x126 (char *file_name); 31 bool Is_Mission_Aftermath (char *file_name); 32 bool Is_Mission_Counterstrike (char *file_name); 33 34 bool Force_Scenario_Available( const char* szName ); 35 36 int ScenarioIndex_From_Filename( const char* szScenarioFilename ); 37 38 bool bSpecialAftermathScenario( const char* szScenarioDescription ); 39 40 #include "WolDebug.h" 41 42 #define PARAMREFRESHWAIT 2000 43 44 #define PING_AND_DISPLAY_WAIT 5000 45 46 void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, PlayerColorType iColorRemap = PCOLOR_NONE ); 47 void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, RemapControlType* pColorRemap ); 48 void CC_Draw_DIB( const char* pDIB, int xDest, int yDest, int iWidth, WindowNumberType window ); 49 50 bool operator==( const GAMEPARAMS& gp1, const GAMEPARAMS& gp2 ); 51 bool operator==( const GlobalPacketType& gp1, const GlobalPacketType& gp2 ); 52 PlayerColorType PlayerColorTypeOf( RemapControlType* pColorRemap ); 53 54 extern unsigned long PlanetWestwoodStartTime; //Time that game was started 55 56 extern bool cancel_current_msgbox; 57 extern bool disable_current_msgbox; 58 59 void Debug_GlobalPacketType( const GlobalPacketType& gp1 ); 60 61 //*********************************************************************************************** 62 /* Game startup logic: 63 When a guest joins the channel, the host sends all current setup info. 64 When a guest changes house, he tells the host, who informs everyone. A house change can't be denied. 65 When a guest changes color, he asks the host for the new color, and assumes the request will go through. If it doesn't, the 66 host sends the guest a messages setting him back to the old color, otherwise, everyone gets the new color for the guest. 67 When the host changes a game param, the change will be noticed within PARAMREFRESHWAIT milliseconds and then transmitted to 68 all guests. 69 All player specific information is stored within the players list (IconListClass), and nowhere else. Though this storage 70 method got a bit inelegant, it gets the job done. 71 All "informs" (messages sent from host to guest) except color changes (WOL_GAMEOPT_INFCOLOR) and player "accept" status 72 changes (WOL_GAMEOPT_INFACCEPT) are assigned a ID, which 73 increments on each send. Color informs are: a) not a condition for removing a guest's "accept" status, and b) sent out to 74 individual guests in certain cases, and would thus complicate the ID tracking scheme. 75 When a guest "accepts" the game setup (with a WOL_GAMEOPT_REQACCEPT), the latest received ID is included in the message. 76 If the host receives a WOL_GAMEOPT_REQACCEPT with an out-of-date ID, the accept is ignored. Though the guest sets himself to 77 "accepted" when he sends the WOL_GAMEOPT_REQACCEPT, if it gets ignored by the host it is because a new inform is already on its 78 way, which will reset the guest's own status to "not accepted" when it arrives. 79 Host clears all accepteds when params are changed, player joins or leaves, or house change message arrives. Though a 80 WOL_GAMEOPT_INFACCEPT is sent out when a guest accepts, there is no corresponding "unaccept all" message sent out at this point, 81 as all guests will naturally clear their recorded accept status for everyone when the change arrives there. 82 Guest clears own accepted when an inform from the server other than color change arrives, player joins or leaves, or own 83 house is directly changed. 84 When all players are "accepted", and the host says "start", a WOL_GAMEOPT_INFSTART is sent to all guests and the host goes into 85 "waiting to start" mode. If a house change or other arrives in this phase, host clears waiting mode and (naturally) marks guests 86 unaccepted and sends out the house change to the guests. (The same applies to player joins/leaves.) 87 While in this mode, the host user is locked out of making any changes to game params. 88 If a change does come in during this phase, a WOL_GAMEOPT_INFCANCELSTART is sent to the guests, and the waiting mode cancelled. 89 When a guest receives WOL_GAMEOPT_INFSTART: If he does not see himself as accepted, it is because a change has just been sent 90 to the host, or some other event occurred that the host will learn about, so the guest can simply ignore the WOL_GAMEOPT_INFSTART. 91 If the guest does see himself as accepted when WOL_GAMEOPT_INFSTART arrives, he responds with WOL_GAMEOPT_REQSTART and goes into 92 "waiting to start" mode. When in this mode, the user is locked out of making any changes. 93 Note that the host is not really asking for any sort of "confirmation that it's ok" to start from the guest here. (Hence no 94 "cancel" response is possible.) It's really just a way of telling the guests not to make any more changes to the setup; it's 95 like we're making sure we've "flushed the queue" of game changes. 96 If a guest receives a WOL_GAMEOPT_INFCANCELSTART, he cancels out of "waiting to start" mode. Note that while in the waiting 97 mode, normal processing of messages is allowed to take place - the user is simply blocked from doing anything. 98 Presumably (hopefully) this phase will last less than a second. 99 When the host receives a WOL_GAMEOPT_REQSTART from everyone, he tells everyone to start with a WOL_GAMEOPT_INFGO. 100 Because there is a chance of color changes being out of sync, all player colors are sent to all guests at this point. 101 There is no chance of other data being out of sync. 102 */ 103 104 //-------------------------------------------------------------------------- 105 // Pieced together from Net_New_Dialog() and Net_Join_Dialog(). 106 //-------------------------------------------------------------------------- 107 //*********************************************************************************************** 108 WOL_GameSetupDialog::WOL_GameSetupDialog( WolapiObject* pWO, bool bHost ) : pWO( pWO ), bHost( bHost ), 109 HousePrevious( HOUSE_NONE ), 110 pILPlayers( NULL ), 111 pILScens( NULL ), 112 pILDisc( NULL ), 113 pEditSend( NULL ), 114 pGaugeCount( NULL ), 115 pGaugeLevel( NULL ), 116 pGaugeCredits( NULL ), 117 pGaugeAIPlayers( NULL ), 118 pCheckListOptions( NULL ), 119 // pTextBtnOk( NULL ), 120 pTextBtnCancel( NULL ), 121 pTextBtnAcceptStart( NULL ), 122 pTextBtnAction( NULL ), 123 pStaticDescrip( NULL ), 124 pStaticUnit( NULL ), 125 pStaticLevel( NULL ), 126 pStaticCredits( NULL ), 127 pStaticAIPlayers( NULL ), 128 pDropListHouse( NULL ), 129 pCheckAftermathUnits( NULL ), 130 nHostLastParamID( 0 ), 131 nGuestLastParamID( 0 ), 132 bWaitingToStart( false ), 133 bParamsReceived( false ), 134 bHostSayGo( false ), 135 bExitForGameTrigger( false ), 136 pToolTipHead( NULL ), 137 pToolTipHitLast( NULL ), 138 pTTipAcceptStart( NULL ), 139 pTTipCancel( NULL ), 140 pTTipAction( NULL ), 141 ScenKindCurrent( SCENARIO_UNINITIALIZED ), 142 pShpBtnScenarioRA( NULL ), 143 pShpBtnScenarioCS( NULL ), 144 pShpBtnScenarioAM( NULL ), 145 pShpBtnScenarioUser( NULL ), 146 bLeaveDueToRulesMismatchTrigger( false ), 147 bHostWaitingForGoTrigger( false ) 148 { 149 *szSendBuffer = 0; 150 *szHouseBuffer = 0; 151 memset( &GParamsLastSent, 0, sizeof( GAMEPARAMS ) ); 152 *szNameOfHostWhoJustBailedOnUs = 0; 153 *szTriggerGameStartInfo = 0; 154 } 155 156 //*********************************************************************************************** 157 WOL_GameSetupDialog::~WOL_GameSetupDialog() 158 { 159 delete pILPlayers; 160 delete pILScens; 161 delete pILDisc; 162 delete pEditSend; 163 delete pGaugeCount; 164 delete pGaugeLevel; 165 delete pGaugeCredits; 166 delete pGaugeAIPlayers; 167 delete pCheckListOptions; 168 // delete pTextBtnOk; 169 delete pTextBtnCancel; 170 delete pTextBtnAcceptStart; 171 delete pTextBtnAction; 172 // delete pStaticDescrip; 173 delete pStaticUnit; 174 delete pStaticLevel; 175 delete pStaticCredits; 176 delete pStaticAIPlayers; 177 delete pDropListHouse; 178 delete pCheckAftermathUnits; 179 delete pTTipAcceptStart; 180 delete pTTipCancel; 181 delete pTTipAction; 182 delete pShpBtnScenarioRA; 183 delete pShpBtnScenarioCS; 184 delete pShpBtnScenarioAM; 185 delete pShpBtnScenarioUser; 186 } 187 188 //*********************************************************************************************** 189 RESULT_WOLGSUP WOL_GameSetupDialog::Run() 190 { 191 Initialize(); 192 return Show(); 193 } 194 195 //*********************************************************************************************** 196 void WOL_GameSetupDialog::Initialize() 197 { 198 //------------------------------------------------------------------------ 199 // Dialog & button dimensions 200 //------------------------------------------------------------------------ 201 d_dialog_w = 640; // dialog width 202 d_dialog_h = 400; // dialog height 203 d_dialog_x = 0; 204 d_dialog_y = 0; 205 d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord 206 207 d_txt6_h = 6*RESFACTOR+1; // ht of 6-pt text 208 int d_text_h = 12; 209 d_margin1 = 34; // large margin 210 211 d_color_w = 10 *RESFACTOR; 212 d_color_h = 9 *RESFACTOR; 213 d_color_x = 294; //54; //d_dialog_x + ((d_dialog_w / 4) * 3) - (d_color_w * 3); 214 d_color_y = 89; //142; //d_house_y; 215 216 d_house_w = 60 *RESFACTOR; 217 d_house_h = (8 * 5 *RESFACTOR); 218 d_house_x = 466; //65; //d_color_x; //d_dialog_cx - (d_house_w / 2); 219 d_house_y = d_color_y; // + 36; //d_dialog_y + ( 7 * RESFACTOR ) + d_txt6_h + (2*RESFACTOR); 220 221 if( bHost ) 222 d_disc_w = 365; //d_dialog_w - (d_margin1 * 2) - 20*RESFACTOR; 223 else 224 d_disc_w = d_dialog_w - ( d_margin1 * 2 ); 225 d_disc_x = d_dialog_x + d_margin1; 226 d_disc_y = 205; //d_dialog_y + d_dialog_h - ( 65 + d_disc_h ); 227 d_disc_h = 337 - d_disc_y; 228 229 d_playerlist_w = 124*RESFACTOR; 230 d_playerlist_h = d_text_h * 4 + 4; 231 d_playerlist_x = d_dialog_x + d_margin1; 232 d_playerlist_y = 75; //d_dialog_y + d_margin1 + d_txt6_h + 3*RESFACTOR + 18 * RESFACTOR; 233 234 int d_tab_h = 19; 235 236 d_scenariolist_w = 200; 237 // d_scenariolist_h = (4 * d_txt6_h) + 3*RESFACTOR; // 4 rows high 238 d_scenariolist_x = d_dialog_x + d_dialog_w - d_margin1 - d_scenariolist_w; 239 d_scenariolist_y = d_disc_y + d_tab_h; 240 d_scenariolist_h = d_disc_y + d_disc_h - d_scenariolist_y; 241 242 d_gamekind_w = 182; 243 d_gamekind_h = 30; 244 d_gamekind_x = 52; // 370; 245 d_gamekind_y = 153; // d_playerlist_y + d_text_h + 3; // + d_playerlist_h - d_text_h; 246 247 d_count_w = 25*RESFACTOR; 248 d_count_h = d_txt6_h; 249 d_count_x = 310; 250 d_count_y = 138; //d_playerlist_y + d_playerlist_h + d_margin2 + 9*RESFACTOR; 251 252 d_level_w = 25*RESFACTOR; 253 d_level_h = d_txt6_h; 254 d_level_x = d_count_x; 255 d_level_y = d_count_y + d_count_h; 256 257 d_credits_w = 25*RESFACTOR; 258 d_credits_h = d_txt6_h; 259 d_credits_x = d_count_x; 260 d_credits_y = d_level_y + d_level_h; 261 262 d_aiplayers_w = 25*RESFACTOR; 263 d_aiplayers_h = d_txt6_h; 264 d_aiplayers_x = d_count_x; 265 d_aiplayers_y = d_credits_y + d_credits_h; 266 267 #ifdef GERMAN 268 d_options_w = 186; 269 #else 270 d_options_w = 180; 271 #endif 272 d_options_h = ((6 * 6) + 4)*RESFACTOR + 1; 273 d_options_x = d_dialog_x + d_dialog_w - d_options_w - d_margin1; 274 d_options_y = 127 - d_txt6_h + 2; 275 276 d_send_w = d_dialog_w - ( d_margin1 * 2 ); 277 d_send_h = 9*RESFACTOR; 278 d_send_x = d_dialog_x + d_margin1; 279 d_send_y = d_disc_y + d_disc_h + 5; 280 281 // d_ok_w = 50*RESFACTOR; 282 // d_ok_h = 9*RESFACTOR; 283 // d_ok_x = d_dialog_x + (d_dialog_w / 6) - (d_ok_w / 2); 284 // d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin1 - 3*RESFACTOR; 285 286 d_cancel_w = 100; 287 d_cancel_h = 9*RESFACTOR; 288 d_cancel_x = d_dialog_x + 100; //d_dialog_cx - (d_cancel_w / 2); 289 d_cancel_y = 365; //d_dialog_y + d_dialog_h - d_cancel_h - d_margin1 - 3*RESFACTOR; 290 291 d_accept_w = 100; 292 d_accept_h = 9 *RESFACTOR; 293 d_accept_x = d_dialog_x + 210; 294 d_accept_y = d_cancel_y; //d_dialog_y + d_dialog_h - 17*RESFACTOR; 295 296 d_action_w = 100; 297 d_action_h = 9 *RESFACTOR; 298 d_action_x = d_dialog_x + 500; 299 d_action_y = d_cancel_y; 300 301 /* int d_load_w = 50*RESFACTOR; 302 int d_load_h = 9*RESFACTOR; 303 int d_load_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_load_w / 2); 304 int d_load_y = d_dialog_y + d_dialog_h - d_load_h - d_margin1 - 3*RESFACTOR; 305 */ 306 d_amunits_w = 160; 307 d_amunits_h = 18; 308 d_amunits_x = d_dialog_x + (d_dialog_w / 6) - (d_amunits_w / 2); 309 d_amunits_y = 365; 310 311 ToolTipClass* pToolTip = pToolTipHead = pWO->pTTipDiscon; 312 pToolTip->next = pWO->pTTipLeave; 313 pToolTip = pToolTip->next; 314 pToolTip->next = pWO->pTTipRefresh; 315 pToolTip = pToolTip->next; 316 pToolTip->next = pWO->pTTipSquelch; 317 pToolTip = pToolTip->next; 318 pToolTip->next = pWO->pTTipBan; 319 pToolTip = pToolTip->next; 320 pToolTip->next = pWO->pTTipKick; 321 pToolTip = pToolTip->next; 322 pToolTip->next = pWO->pTTipFindpage; 323 pToolTip = pToolTip->next; 324 pToolTip->next = pWO->pTTipOptions; 325 pToolTip = pToolTip->next; 326 pToolTip->next = pWO->pTTipLadder; 327 pToolTip = pToolTip->next; 328 pToolTip->next = pWO->pTTipHelp; 329 330 pILPlayers = new IconListClass( BUTTON_PLAYERLIST, d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 2 ); 331 // ListClass scenariolist(BUTTON_SCENARIOLIST, d_scenariolist_x, d_scenariolist_y, d_scenariolist_w, d_scenariolist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); 332 pILScens = new IconListClass( BUTTON_SCENARIOLIST, d_scenariolist_x, d_scenariolist_y, d_scenariolist_w, d_scenariolist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 1 ); 333 pILDisc = new IconListClass( BUTTON_DISCLIST, d_disc_x, d_disc_y, d_disc_w, d_disc_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 0, 300 ); 334 335 pEditSend = new EditClass( BUTTON_SENDEDIT, szSendBuffer, MAXCHATSENDLENGTH, TPF_TEXT, d_send_x, d_send_y, d_send_w, d_send_h ); 336 337 // TextButtonClass rejectbtn( BUTTON_REJECT, TXT_REJECT, TPF_BUTTON, d_reject_x, d_reject_y ); 338 pGaugeCount = new GaugeClass( BUTTON_COUNT, d_count_x, d_count_y, d_count_w, d_count_h ); 339 pGaugeLevel = new GaugeClass( BUTTON_LEVEL, d_level_x, d_level_y, d_level_w, d_level_h ); 340 pGaugeCredits = new GaugeClass( BUTTON_CREDITS, d_credits_x, d_credits_y, d_credits_w, d_credits_h ); 341 pGaugeAIPlayers = new GaugeClass( BUTTON_AIPLAYERS, d_aiplayers_x, d_aiplayers_y, d_aiplayers_w, d_aiplayers_h ); 342 pCheckListOptions = new CheckListClass( BUTTON_PARAMS, d_options_x, d_options_y, d_options_w, d_options_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP") ); 343 // pTextBtnOk = new TextButtonClass( BUTTON_OK, TXT_OK, TPF_BUTTON, d_ok_x, d_ok_y, 60*RESFACTOR ); 344 // TextButtonClass loadbtn(BUTTON_LOAD, TXT_LOAD_BUTTON, TPF_BUTTON, d_load_x, d_load_y, 60*RESFACTOR); 345 pTextBtnCancel = new TextButtonClass( BUTTON_CANCEL, TXT_WOL_CANCELGAME, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w ); 346 pTTipCancel = new ToolTipClass( pTextBtnCancel, TXT_WOL_TTIP_CANCELGAME, d_cancel_x + d_cancel_w/2, d_cancel_y - 6 ); 347 348 if( bHost ) 349 { 350 pTextBtnAcceptStart = new TextButtonClass( BUTTON_ACCEPTSTART, TXT_WOL_STARTBUTTON, TPF_BUTTON, d_accept_x, d_accept_y, d_accept_w ); 351 pTTipAcceptStart = new ToolTipClass( pTextBtnAcceptStart, TXT_WOL_TTIP_START, d_accept_x + d_accept_w/2, d_accept_y - 6 ); 352 } 353 else 354 { 355 pTextBtnAcceptStart = new TextButtonClass( BUTTON_ACCEPTSTART, TXT_WOL_ACCEPTBUTTON, TPF_BUTTON, d_accept_x, d_accept_y, d_accept_w ); 356 pTTipAcceptStart = new ToolTipClass( pTextBtnAcceptStart, TXT_WOL_TTIP_ACCEPT, d_accept_x + d_accept_w/2, d_accept_y - 6 ); 357 } 358 359 pTextBtnAction = new TextButtonClass( BUTTON_ACTION, TXT_WOL_ACTION, TPF_BUTTON, d_action_x, d_action_y, d_action_w ); 360 pTTipAction = new ToolTipClass( pTextBtnAction, TXT_WOL_TTIP_ACTION, d_action_x + d_action_w/2, d_action_y - 6, true ); 361 362 pToolTip = pToolTip->next; 363 pToolTip->next = pTTipCancel; 364 pToolTip = pToolTip->next; 365 pToolTip->next = pTTipAcceptStart; 366 pToolTip = pToolTip->next; 367 pToolTip->next = pTTipAction; 368 pToolTip = pToolTip->next; 369 pToolTip->next = NULL; 370 371 if( bHost ) 372 pTextBtnAcceptStart->Disable(); 373 374 // pStaticDescrip is no longer used - can't get the bloody thing to clip text. You'd think a StaticButton control would. 375 // pStaticDescrip = new StaticButtonClass( 0, "", TPF_TYPE, d_gamekind_x, d_gamekind_y, d_gamekind_w, d_gamekind_h ); 376 pStaticUnit = new StaticButtonClass( 0, " ", TPF_TEXT, d_count_x + d_count_w + 2*RESFACTOR, d_count_y ); 377 pStaticLevel = new StaticButtonClass( 0, " ", TPF_TEXT, d_level_x + d_level_w + 2*RESFACTOR, d_level_y ); 378 pStaticCredits = new StaticButtonClass( 0, " ", TPF_TEXT, d_credits_x + d_credits_w + 2*RESFACTOR, d_credits_y ); 379 pStaticAIPlayers = new StaticButtonClass( 0, " ", TPF_TEXT, d_aiplayers_x + d_aiplayers_w + 2*RESFACTOR, d_aiplayers_y ); 380 381 Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT); 382 pDropListHouse = new DropListClass( BUTTON_HOUSE, szHouseBuffer, sizeof( szHouseBuffer ), 383 TPF_TEXT, 384 d_house_x, d_house_y, d_house_w, d_house_h, 385 MFCD::Retrieve("BTN-UP.SHP"), 386 MFCD::Retrieve("BTN-DN.SHP") ); 387 388 // ajw - This checkbox is not used. Could be turned on, though. 389 pCheckAftermathUnits = new BigCheckBoxClass( BUTTON_AFTERMATHUNITS, d_amunits_x, d_amunits_y, d_amunits_w, d_amunits_h, 390 "Aftermath units enabled", TPF_TEXT, false ); 391 392 if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME ) 393 { 394 bAftermathUnits = true; 395 int current_drive = CCFileClass::Get_CD_Drive(); 396 int cd_index = Get_CD_Index(current_drive, 1*60); 397 if( cd_index != 3 && cd_index != 5 ) 398 { 399 WOL_PrintMessage( *pILDisc, TXT_WOL_AMDISCNEEDED, WOLCOLORREMAP_LOCALMACHINEMESS ); 400 } 401 } 402 else 403 { 404 bAftermathUnits = false; 405 pCheckAftermathUnits->Disable(); 406 } 407 408 bSlowUnitBuildRate = true; 409 410 #define TABSPACING 38 411 pShpBtnScenarioRA = new ShapeButtonClass( BUTTON_SCENARIO_RA, MFCD::Retrieve( "tabra.shp" ), d_scenariolist_x, d_scenariolist_y - d_tab_h ); 412 pShpBtnScenarioCS = new ShapeButtonClass( BUTTON_SCENARIO_CS, MFCD::Retrieve( "tabcs.shp" ), d_scenariolist_x + TABSPACING, d_scenariolist_y - d_tab_h ); 413 pShpBtnScenarioAM = new ShapeButtonClass( BUTTON_SCENARIO_AM, MFCD::Retrieve( "tabam.shp" ), d_scenariolist_x + TABSPACING, d_scenariolist_y - d_tab_h ); 414 415 int iScenarioUserTabPos; 416 if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::CSGAME || pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME || 417 ( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::RAGAME && Is_Aftermath_Installed() && !pWO->GameInfoCurrent.bTournament ) ) 418 // Place user tab in the third tab position. (It may still not be present.) 419 iScenarioUserTabPos = d_scenariolist_x + TABSPACING * 2; 420 else 421 iScenarioUserTabPos = d_scenariolist_x + TABSPACING; 422 423 pShpBtnScenarioUser = new ShapeButtonClass( BUTTON_SCENARIO_USER, MFCD::Retrieve( "tabus.shp" ), iScenarioUserTabPos, d_scenariolist_y - d_tab_h ); 424 425 // Change draw behavior of tab buttons. 426 pShpBtnScenarioRA->ReflectButtonState = true; 427 pShpBtnScenarioCS->ReflectButtonState = true; 428 pShpBtnScenarioAM->ReflectButtonState = true; 429 pShpBtnScenarioUser->ReflectButtonState = true; 430 431 if( !bHost ) 432 { 433 pILScens->Disable(); 434 pGaugeCount->Disable(); 435 pGaugeLevel->Disable(); 436 pGaugeCredits->Disable(); 437 pGaugeAIPlayers->Disable(); 438 pCheckListOptions->Disable(); 439 pCheckAftermathUnits->Disable(); 440 } 441 442 pWO->LinkToGameDlg( pILDisc, pILPlayers ); 443 pWO->pGSupDlg = this; 444 445 dwTimeNextParamRefresh = ::timeGetTime(); 446 } 447 448 //*********************************************************************************************** 449 RESULT_WOLGSUP WOL_GameSetupDialog::Show() 450 { 451 // Returns: -1 == return to chat dialog. 452 453 //------------------------------------------------------------------------ 454 // Dialog variables 455 //------------------------------------------------------------------------ 456 bool bHackFocus = true; 457 458 display = REDRAW_ALL; // redraw level 459 bProcess = true; // process while true 460 KeyNumType input; 461 462 DWORD timeWaitingToStartTimeout; 463 464 char szScenarioNameDisplay[ 300 ]; 465 466 bool bInformParamChange; 467 468 long ok_timer = 0; // for timing OK button 469 int i; 470 int tabs[] = {77*RESFACTOR}; // tabs for player list box 471 int optiontabs[] = {8*RESFACTOR}; // tabs for option list box 472 473 CCFileClass loadfile ("SAVEGAME.NET"); 474 int load_game = 0; // 1 = load a saved game 475 RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); 476 477 int cbox_x[] = { 478 d_color_x, 479 d_color_x + d_color_w, 480 d_color_x + (d_color_w * 2), 481 d_color_x + (d_color_w * 3), 482 d_color_x + (d_color_w * 4), 483 d_color_x + (d_color_w * 5), 484 d_color_x + (d_color_w * 6), 485 d_color_x + (d_color_w * 7), 486 }; 487 488 bool bRetractHouseDropDown = false; 489 490 if( !pWO->OnEnteringGameSetup() ) // Gets a userlist setup, among other things. 491 strcpy( szNameOfHostWhoJustBailedOnUs, TXT_WOL_THEGAMEHOST ); // Will cause immediate exit. 492 493 // If I'm not already listed with a color, give myself a color. 494 // (I may have already received my assigned color from the game host.) 495 int iItem = pILPlayers->Find( pWO->szMyName ); // (I must be in the list I just got.) 496 _ASSERTE( iItem != -1 ); 497 RemapControlType* pColorRemap = pILPlayers->Get_Item_Color( iItem ); 498 PlayerColorType Color = PlayerColorTypeOf( pColorRemap ); 499 500 // debugprint( "Starting up, I see myself as color %i\n", Color ); 501 if( Color == PCOLOR_NONE ) 502 SetPlayerColor( pWO->szMyName, ColorNextAvailable() ); // Unless I'm host, this will be changed quite immediately, 503 // but nice to have it be a valid value until then. 504 505 DWORD dwTimeNextPlayerPing = ::timeGetTime() + PING_AND_DISPLAY_WAIT; 506 DWORD dwTimeNextPingDisplay = dwTimeNextPlayerPing + 1500; // Stagger ping and ping display periods. 507 508 //------------------------------------------------------------------------ 509 // Build the button list 510 //------------------------------------------------------------------------ 511 BindControls( true ); 512 513 pILPlayers->Set_Tabs(tabs); 514 515 // If we have not already received a param update from a host, set default param values. 516 if( !bParamsReceived ) 517 { 518 if( !bHost ) 519 // Accept button disabled until first params arrive. 520 pTextBtnAcceptStart->Disable(); 521 522 Special.IsCaptureTheFlag = Rule.IsMPCaptureTheFlag; // Ugh. Use of "Special" global. 523 if( bHost ) { 524 Session.Options.Credits = Rule.MPDefaultMoney; // init credits & credit buffer 525 Session.Options.Bases = Rule.IsMPBasesOn; // init scenario parameters 526 Session.Options.Tiberium = Rule.IsMPTiberiumGrow; 527 Session.Options.Goodies = Rule.IsMPCrates; 528 Session.Options.AIPlayers = 0; 529 Session.Options.UnitCount = (SessionClass::CountMax[Session.Options.Bases] + SessionClass::CountMin[Session.Options.Bases]) / 2; 530 //first_time = 0; 531 } 532 //------------------------------------------------------------------------ 533 // Init other scenario parameters 534 //------------------------------------------------------------------------ 535 Special.IsTGrowth = Session.Options.Tiberium; // Ugh. Use of "Special" global. 536 Rule.IsTGrowth = Session.Options.Tiberium; 537 Special.IsTSpread = Session.Options.Tiberium; // Ugh. Use of "Special" global. 538 Rule.IsTSpread = Session.Options.Tiberium; 539 540 if( bHost ) 541 { 542 //------------------------------------------------------------------------ 543 // Set up array of lists of available scenarios. 544 //------------------------------------------------------------------------ 545 for( i = 0; i < Session.Scenarios.Count(); i++ ) 546 { 547 // Reworking of the loop previously used for language translation. (What a hack I have inherited...) 548 MultiMission* pMMission = Session.Scenarios[ i ]; 549 const char* szScenarioNameShow = pMMission->Description(); 550 #if defined( GERMAN ) || defined( FRENCH ) 551 for( int j = 0; EngMisStr[j] != NULL; j++ ) 552 { 553 if( !strcmp( szScenarioNameShow, EngMisStr[j] ) ) 554 { 555 // Found description in translation array that matches mission. 556 szScenarioNameShow = EngMisStr[ j + 1 ]; 557 break; 558 } 559 } 560 // (If no match found, defaults to English description.) 561 #endif 562 // Place scenario name in a specific scenario list. 563 if( pMMission->Get_Official() ) 564 { 565 if( Is_Mission_Counterstrike( (char*)( Session.Scenarios[i]->Get_Filename() ) ) ) 566 { 567 // debugprint( " ---------------- Adding scenario %s as CS\n", szScenarioNameShow ); 568 ar_szScenarios[ SCENARIO_CS ].Add( szScenarioNameShow ); 569 ar_szScenIndexes[ SCENARIO_CS ].Add( (void*)i ); 570 } 571 else if( Is_Mission_Aftermath( (char*)( Session.Scenarios[i]->Get_Filename() ) ) ) 572 { 573 // debugprint( " ---------------- Adding scenario %s as AM\n", szScenarioNameShow ); 574 // If this is not an Aftermath game channel, we must filter out any AM maps that have 575 // special AM units on them. 576 if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME || 577 !bSpecialAftermathScenario( pMMission->Description() ) ) 578 { 579 ar_szScenarios[ SCENARIO_AM ].Add( szScenarioNameShow ); 580 ar_szScenIndexes[ SCENARIO_AM ].Add( (void*)i ); 581 } 582 } 583 else 584 { 585 // debugprint( " ---------------- Adding scenario %s as RA\n", szScenarioNameShow ); 586 ar_szScenarios[ SCENARIO_RA ].Add( szScenarioNameShow ); 587 ar_szScenIndexes[ SCENARIO_RA ].Add( (void*)i ); 588 } 589 } 590 else 591 { 592 // debugprint( " ---------------- Adding scenario %s as User\n", szScenarioNameShow ); 593 ar_szScenarios[ SCENARIO_USER ].Add( szScenarioNameShow ); 594 ar_szScenIndexes[ SCENARIO_USER ].Add( (void*)i ); 595 } 596 } 597 598 // Set default scenario list viewing mode and prepare tab buttons. 599 /* switch( pWO->GameInfoCurrent.GameKind ) 600 { 601 case CREATEGAMEINFO::RAGAME: 602 ScenarioDisplayMode( SCENARIO_RA ); 603 break; 604 case CREATEGAMEINFO::CSGAME: 605 ScenarioDisplayMode( SCENARIO_CS ); 606 break; 607 case CREATEGAMEINFO::AMGAME: 608 ScenarioDisplayMode( SCENARIO_AM ); 609 break; 610 default: 611 // debugprint( "Illegal GameInfoCurrent value." ); 612 Fatal( "Illegal GameInfoCurrent value." ); 613 } 614 */ 615 Session.Options.ScenarioIndex = 0; // 1st scenario is selected 616 617 ScenarioDisplayMode( SCENARIO_RA ); // Always start on RedAlert tab. Next line depends on selected item in 618 // list matching selected scenario. 619 // pStaticDescrip->Set_Text( pILScens->Get_Item( pILScens->Current_Index() ), false ); 620 strcpy( szScenarioNameDisplay, pILScens->Get_Item( pILScens->Current_Index() ) ); 621 } 622 623 Seed = rand(); 624 } 625 626 //------------------------------------------------------------------------ 627 // Init button states 628 //------------------------------------------------------------------------ 629 pCheckListOptions->Set_Tabs(optiontabs); 630 pCheckListOptions->Set_Read_Only(0); 631 632 pCheckListOptions->Add_Item(Text_String(TXT_BASES)); 633 pCheckListOptions->Add_Item(Text_String(TXT_ORE_SPREADS)); 634 pCheckListOptions->Add_Item(Text_String(TXT_CRATES)); 635 pCheckListOptions->Add_Item(Text_String(TXT_CAPTURE_THE_FLAG)); 636 pCheckListOptions->Add_Item(Text_String(TXT_SHADOW_REGROWS)); 637 pCheckListOptions->Add_Item(TXT_WOL_SLOWUNITBUILD); 638 639 SetSpecialControlStates(); 640 641 //........................................................................ 642 // House buttons 643 //........................................................................ 644 for (HousesType house = HOUSE_USSR; house <= HOUSE_FRANCE; house++) { 645 pDropListHouse->Add_Item(Text_String(HouseTypeClass::As_Reference(house).Full_Name())); 646 } 647 pDropListHouse->Set_Selected_Index(Session.House - HOUSE_USSR); 648 pDropListHouse->Set_Read_Only (true); 649 650 651 bInformParamChange = false; 652 653 // PlayingAgainstVersion = VerNum.Version_Number(); 654 655 //------------------------------------------------------------------------ 656 // Init random-number generator, & create a seed to be used for all random 657 // numbers from here on out 658 //------------------------------------------------------------------------ 659 srand(time(NULL)); 660 661 //------------------------------------------------------------------------ 662 // Init the version-clipping system 663 //------------------------------------------------------------------------ 664 VerNum.Init_Clipping(); 665 666 667 // Load_Title_Page(true); 668 // CCPalette.Set(); //GamePalette.Set(); 669 670 // 671 // Now init the max range of the AI players slider. 672 // 673 // pGaugeAIPlayers->Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); 674 // pGaugeAIPlayers->Set_Value(Session.Options.AIPlayers); 675 676 Sound_Effect( WOLSOUND_ENTERGAME ); 677 678 //------------------------------------------------------------------------ 679 // Processing loop 680 //------------------------------------------------------------------------ 681 while (bProcess) { 682 #if(SHOW_MONO) 683 Ipx.Mono_Debug_Print(-1,0); 684 #endif 685 686 // Check for change of house. Occurs on first loop and when user changes house. 687 if( HousePrevious != Session.House ) 688 { 689 if( bHost ) 690 { 691 // Host changed house. 692 // Do processing as if we'd received a message from a guest. 693 694 // Set house in our own list. 695 SetPlayerHouse( pWO->szMyName, Session.House ); 696 // Tell guests. 697 nHostLastParamID++; 698 InformAboutPlayerHouse( pWO->szMyName, Session.House, NULL ); 699 HousePrevious = Session.House; 700 ClearAllAccepts(); 701 } 702 else 703 { 704 User* pUserHost = pWO->pGameHost(); 705 if( pUserHost ) // Else we have not received the user list yet and don't know who the host is. 706 // We'll keep trying this until we get a host - HousePrevious keeps us triggering until then. 707 { 708 // debugprint( "Session.House changed.\n" ); 709 // Tell host we changed our house. 710 char szSend[ 20 ]; 711 sprintf( szSend, "%02u %02u", WOL_GAMEOPT_REQHOUSE, Session.House ); 712 pWO->SendGameOpt( szSend, pUserHost ); 713 // Set house in our own list. This is fine because we know that the change must be affirmed by the host. 714 SetPlayerHouse( pWO->szMyName, Session.House ); 715 HousePrevious = Session.House; 716 } 717 } 718 } 719 720 // Regularly ping other players to assess latencies. 721 // Display of ping results is on a parallel timer, slightly behind the ping so that we'll be likely to have 722 // a fresh average to show. Not too big a deal if OnPings haven't arrived by then (though they should have). 723 if( ::timeGetTime() > dwTimeNextPlayerPing ) 724 { 725 pWO->RequestPlayerPings(); 726 dwTimeNextPlayerPing = ::timeGetTime() + PING_AND_DISPLAY_WAIT; 727 } 728 if( ::timeGetTime() > dwTimeNextPingDisplay ) 729 { 730 pWO->ListChannelUsers(); 731 dwTimeNextPingDisplay = ::timeGetTime() + PING_AND_DISPLAY_WAIT; 732 } 733 734 if( pWO->bShowRankUpdated ) 735 { 736 pWO->bShowRankUpdated = false; 737 pWO->ListChannelUsers(); 738 } 739 740 // Regularly check for incoming messages from wolapi. 741 if( ::timeGetTime() > pWO->dwTimeNextWolapiPump ) 742 { 743 pWO->pChat->PumpMessages(); 744 pWO->pNetUtil->PumpMessages(); 745 // Special post-callback processing... 746 if( pWO->pChatSink->bGotKickedTrigger ) 747 { 748 // We got kicked out of the channel. 749 pWO->OnExitingGameChannel(); 750 pWO->RejoinLobbyAfterGame(); 751 bProcess = false; 752 ResultReturn = RESULT_WOLGSUP_BACKTOCHAT; // Return to chat. 753 // Leave the bGotKickedTrigger flag so we can react to it upon reentering the chat dialog. 754 //pWO->pChatSink->bGotKickedTrigger = false; 755 display = REDRAW_ALL; 756 } 757 pWO->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; 758 } 759 760 if( bHostSayGo ) 761 { 762 // debugprint( "bHostSayGo trigger\n" ); 763 // We are the host, now ready to tell everyone to GO and start our game. 764 HostSaysGo(); 765 /* Part of old method of game start. 766 bProcess = false; 767 ResultReturn = RESULT_WOLGSUP_STARTGAMEHOST; 768 // debugprint( "Host about to exit game setup dialog for game.\n" ); 769 if( !ExitGameChannel() ) 770 ResultReturn = RESULT_WOLGSUP_FATALERROR; // Return with an error value. 771 break; 772 */ 773 } 774 else if( *szNameOfHostWhoJustBailedOnUs ) // Host left channel - cancel setup. 775 { 776 // debugprint( "Guest about to exit game setup dialog because host bailed on us.\n" ); 777 if( ExitGameChannel() ) 778 { 779 pWO->RejoinLobbyAfterGame(); 780 bProcess = false; 781 ResultReturn = RESULT_WOLGSUP_HOSTLEFT; // Return to chat. 782 // Add a message explaining what happened to the saved chat that will be restored in the chat dialog. 783 pWO->AddHostLeftMessageToSavedChat( szNameOfHostWhoJustBailedOnUs ); 784 } 785 else 786 { 787 bProcess = false; 788 ResultReturn = RESULT_WOLGSUP_FATALERROR; // Return with an error value. 789 } 790 break; 791 } 792 else if( bLeaveDueToRulesMismatchTrigger ) 793 { 794 // debugprint( "Guest about to exit game setup dialog because of rules.ini mismatch.\n" ); 795 if( ExitGameChannel() ) 796 { 797 pWO->RejoinLobbyAfterGame(); 798 bProcess = false; 799 ResultReturn = RESULT_WOLGSUP_RULESMISMATCH; // Return to chat. 800 // Add a message explaining what happened to the saved chat that will be restored in the chat dialog. 801 pWO->AddMessageToSavedChat( TXT_WOL_RULESMISMATCH ); 802 } 803 else 804 { 805 bProcess = false; 806 ResultReturn = RESULT_WOLGSUP_FATALERROR; // Return with an error value. 807 } 808 break; 809 } 810 else if( *szTriggerGameStartInfo ) 811 { 812 // debugprint( "About to trigger game start.\n" ); 813 TriggerGameStart( szTriggerGameStartInfo ); 814 } 815 else if( pWO->bSelfDestruct ) 816 { 817 if( pWO->pChatSink->bConnected ) 818 pWO->Logout(); 819 bProcess = false; 820 ResultReturn = RESULT_WOLGSUP_LOGOUT; // As if the user logged himself out. 821 } 822 823 if( bExitForGameTrigger ) 824 { 825 // We are now exiting to go into a game. 826 bProcess = false; 827 if( bHost ) 828 ResultReturn = RESULT_WOLGSUP_STARTGAMEHOST; 829 else 830 ResultReturn = RESULT_WOLGSUP_STARTGAME; 831 // debugprint( "About to exit game setup dialog for game.\n" ); 832 if( !ExitGameChannel() ) 833 ResultReturn = RESULT_WOLGSUP_FATALERROR; // Return with an error value. 834 break; 835 } 836 837 if( bHost && bWaitingToStart && !bExitForGameTrigger && timeGetTime() > timeWaitingToStartTimeout ) 838 { 839 ClearAllAccepts(); // Results in all required steps to cancel game start. 840 WOL_PrintMessage( *pILDisc, TXT_WOL_STARTTIMEOUT, WOLCOLORREMAP_LOCALMACHINEMESS ); 841 Sound_Effect( WOLSOUND_ERROR ); 842 } 843 844 // Regularly send game param changes if I'm the host. 845 if( bHost && !bHostSayGo && !bExitForGameTrigger && ::timeGetTime() > dwTimeNextParamRefresh ) 846 { 847 if( bParamsUnfresh() ) 848 { 849 nHostLastParamID++; 850 SendParams(); 851 ClearAllAccepts(); 852 } 853 dwTimeNextParamRefresh = ::timeGetTime() + PARAMREFRESHWAIT; 854 } 855 856 if( !bProcess ) // Avoid redrawing if we're about to bail. 857 break; 858 859 // 860 // If we have just received input focus again after running in the background then 861 // we need to redraw. 862 // 863 if (AllSurfaces.SurfacesRestored) { 864 AllSurfaces.SurfacesRestored=FALSE; 865 display = REDRAW_ALL; 866 } 867 868 //..................................................................... 869 // Refresh display if needed 870 //..................................................................... 871 872 if( bRetractHouseDropDown ) //|| pCheckListOptions->Is_To_Redraw() ) 873 { 874 bRetractHouseDropDown = false; 875 if( pDropListHouse->IsDropped ) 876 { 877 pDropListHouse->Collapse(); 878 if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; 879 } 880 } 881 // if( pILDisc->Is_To_Redraw() ) 882 // { 883 // pDropListHouse->Flag_To_Redraw(); 884 // } 885 886 if( display ) 887 { 888 Hide_Mouse(); 889 890 /* 891 ** Collapse the country list if we are going to redraw the game list 892 */ 893 // if (pILScens->Is_To_Redraw() && pDropListHouse->IsDropped) { 894 // pDropListHouse->Collapse(); 895 // if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; 896 // } 897 898 //.................................................................. 899 // Redraw backgound & dialog box 900 //.................................................................. 901 if (display >= REDRAW_BACKGROUND) { 902 if( pToolTipHitLast && pToolTipHitLast->bShowing ) 903 pToolTipHitLast->Unshow(); 904 905 if( pDropListHouse->IsDropped ) 906 pDropListHouse->Collapse(); 907 908 Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); 909 910 //............................................................... 911 // Dialog & Field labels 912 //............................................................... 913 Fancy_Text_Print(TXT_PLAYERS, d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, scheme, TBLACK, TPF_TEXT | TPF_CENTER); 914 if( bHost ) 915 Fancy_Text_Print( TXT_SCENARIOS, d_scenariolist_x + d_scenariolist_w, d_scenariolist_y - 12, scheme, TBLACK, TPF_TYPE | TPF_RIGHT ); 916 // else 917 // Fancy_Text_Print( TXT_SCENARIO_COLON, d_scenariolist_x + (d_scenariolist_w / 2), d_scenariolist_y - d_txt6_h, scheme, TBLACK, TPF_TEXT | TPF_CENTER); 918 Fancy_Text_Print(TXT_COUNT, d_count_x - 2*RESFACTOR, d_count_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); 919 Fancy_Text_Print(TXT_LEVEL, d_level_x - 2*RESFACTOR, d_level_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); 920 Fancy_Text_Print(TXT_CREDITS_COLON, d_credits_x - 2*RESFACTOR, d_credits_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); 921 Fancy_Text_Print(TXT_AI_PLAYERS_COLON, d_aiplayers_x - 2*RESFACTOR, d_aiplayers_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); 922 Fancy_Text_Print(TXT_SIDE_COLON, 923 // d_house_x + (d_house_w / 2), 924 d_house_x + ( ( d_house_w + 16 ) / 2 ), 925 d_house_y - d_txt6_h, 926 scheme, TBLACK, 927 TPF_CENTER | TPF_TEXT); 928 Fancy_Text_Print(TXT_COLOR_COLON, 929 d_color_x + d_color_w * 4, 930 d_color_y - d_txt6_h, 931 scheme, TBLACK, 932 TPF_CENTER | TPF_TEXT); 933 934 const char* szGameKind; 935 const char* pDIB; 936 switch( pWO->GameInfoCurrent.GameKind ) 937 { 938 case CREATEGAMEINFO::RAGAME: 939 szGameKind = TXT_WOL_CG_RAGAME; 940 pDIB = pWO->OldRAGameTypeInfos[ 0 ].pDIB; 941 break; 942 case CREATEGAMEINFO::CSGAME: 943 szGameKind = TXT_WOL_CG_CSGAME; 944 pDIB = pWO->OldRAGameTypeInfos[ 1 ].pDIB; 945 break; 946 case CREATEGAMEINFO::AMGAME: 947 szGameKind = TXT_WOL_CG_AMGAME; 948 pDIB = pWO->OldRAGameTypeInfos[ 2 ].pDIB; 949 break; 950 default: 951 // debugprint( "Illegal GameInfoCurrent value." ); 952 //Fatal( "Illegal GameInfoCurrent value." ); 953 szGameKind = TXT_WOL_CG_AMGAME; 954 pDIB = NULL; 955 pWO->bSelfDestruct = true; 956 break; 957 } 958 int iGameInfoSpacingY = 14; 959 int iGameInfoSecondColumnX = 0; //170; 960 // Game kind. 961 Fancy_Text_Print( szGameKind, d_gamekind_x, d_gamekind_y - iGameInfoSpacingY * 1, scheme, TBLACK, TPF_TYPE ); 962 // Game kind icon. 963 CC_Draw_DIB( pDIB, d_gamekind_x - 16, d_gamekind_y - iGameInfoSpacingY * 1 - 2, 100, WINDOW_MAIN ); 964 // "Tournament." 965 if( pWO->GameInfoCurrent.bTournament ) 966 { 967 Fancy_Text_Print( TXT_WOL_CG_TOURNAMENT, d_gamekind_x + iGameInfoSecondColumnX, 968 d_gamekind_y + iGameInfoSpacingY * 1, scheme, TBLACK, TPF_TYPE ); 969 CC_Draw_DIB( pWO->DibIconInfos[ DIBICON_TOURNAMENT ].pDIB, d_gamekind_x + iGameInfoSecondColumnX - 16, 970 d_gamekind_y + iGameInfoSpacingY * 1 - 2, 100, WINDOW_MAIN ); 971 } 972 // "Password: ..." 973 if( pWO->GameInfoCurrent.bPrivate ) 974 { 975 char szPrivatePassword[ 100 ]; 976 sprintf( szPrivatePassword, TXT_WOL_PRIVATEPASSWORD, pWO->GameInfoCurrent.szPassword ); 977 Fancy_Text_Print( szPrivatePassword, d_gamekind_x + iGameInfoSecondColumnX, 978 d_gamekind_y + iGameInfoSpacingY * 2, scheme, TBLACK, TPF_TYPE ); 979 CC_Draw_DIB( pWO->DibIconInfos[ DIBICON_PRIVATE ].pDIB, d_gamekind_x + iGameInfoSecondColumnX - 16, 980 d_gamekind_y + iGameInfoSpacingY * 2 - 2, 100, WINDOW_MAIN ); 981 } 982 // "Scenario:" - scenario name is drawn separately. 983 //Fancy_Text_Print( TXT_SCENARIO_COLON, d_gamekind_x, d_gamekind_y - iGameInfoSpacingY, scheme, TBLACK, TPF_TYPE ); 984 } 985 986 //.................................................................. 987 // Redraw buttons 988 //.................................................................. 989 if (display >= REDRAW_BUTTONS) { 990 991 commands->Draw_All(); 992 } 993 994 //.................................................................. 995 // Draw the color boxes 996 //.................................................................. 997 if (display >= REDRAW_COLORS ) { 998 for (i = 0; i < MAX_MPLAYER_COLORS; i++) { 999 LogicPage->Fill_Rect (cbox_x[i] + 1, d_color_y + 1, 1000 cbox_x[i] + 1 + d_color_w - 2 *RESFACTOR, d_color_y + 1 + d_color_h - 2, 1001 ColorRemaps[i].Box); 1002 1003 if (i == Session.ColorIdx) { 1004 Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_DOWN, false); 1005 } else { 1006 Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_RAISED, false); 1007 } 1008 } 1009 } 1010 1011 //.................................................................. 1012 // Draw the messages: 1013 // - Erase an old message first 1014 // - If we're in a game, print the game options (if they've been 1015 // received) 1016 // - If we've been rejected from a game, print that message 1017 //.................................................................. 1018 if (display >= REDRAW_MESSAGE) { 1019 // Draw_Box(d_disc_x, d_disc_y, d_disc_w, d_disc_h, BOXSTYLE_BOX, true); 1020 // Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, BOXSTYLE_BOX, true); 1021 // Session.Messages.Draw(); 1022 } 1023 1024 //.................................................................. 1025 // Update game parameter labels 1026 //.................................................................. 1027 if (display >= REDRAW_PARMS) 1028 { 1029 char txt[80]; 1030 1031 char* szScenarioDesc; 1032 bool bOfficial; 1033 char* szScenarioFileName; 1034 if( !bHost ) 1035 { 1036 szScenarioDesc = Session.Options.ScenarioDescription; 1037 bOfficial = Session.ScenarioIsOfficial; 1038 szScenarioFileName = Session.ScenarioFileName; 1039 } 1040 else 1041 { 1042 szScenarioDesc = (char*)Session.Scenarios[ Session.Options.ScenarioIndex ]->Description(); 1043 bOfficial = Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Official(); 1044 szScenarioFileName = (char*)Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename(); 1045 } 1046 1047 if( *szScenarioDesc ) 1048 { 1049 // Language translation. 1050 for (int ii = 0; EngMisStr[ii] != NULL; ii++) { 1051 if (!strcmp( szScenarioDesc, EngMisStr[ii]) ) { 1052 #if defined(GERMAN) || defined(FRENCH) 1053 //sprintf(txt, "%s %s", Text_String(TXT_SCENARIO_COLON), EngMisStr[ii+1]); 1054 sprintf(txt, "%s", EngMisStr[ii+1]); 1055 #else 1056 //sprintf(txt, "%s %s", Text_String(TXT_SCENARIO_COLON), Session.Options.ScenarioDescription); 1057 sprintf(txt, "%s", szScenarioDesc); 1058 #endif 1059 break; 1060 } 1061 } 1062 if (EngMisStr[ii] == NULL) { 1063 sprintf(txt, "%s", szScenarioDesc); 1064 } 1065 // pStaticDescrip->Set_Text( txt, false ); 1066 strcpy( szScenarioNameDisplay, txt ); 1067 1068 // Show icon for gamekind of scenario. 1069 const char* pDIB; 1070 1071 if( bOfficial ) 1072 { 1073 if( Is_Mission_Counterstrike( szScenarioFileName ) ) 1074 pDIB = pWO->OldRAGameTypeInfos[ 1 ].pDIB; 1075 else if( Is_Mission_Aftermath( szScenarioFileName ) ) 1076 pDIB = pWO->OldRAGameTypeInfos[ 2 ].pDIB; 1077 else 1078 pDIB = pWO->OldRAGameTypeInfos[ 0 ].pDIB; 1079 } 1080 else 1081 pDIB = pWO->DibIconInfos[ DIBICON_USER ].pDIB; 1082 1083 DrawScenarioDescripIcon( pDIB ); 1084 } 1085 else 1086 { 1087 //sprintf(txt, "%s %s", Text_String(TXT_SCENARIO_COLON), Text_String(TXT_NOT_FOUND)); 1088 sprintf(txt, "%s", TXT_WOL_SCENARIONAMEWAIT ); 1089 // pStaticDescrip->Set_Text( txt, false ); 1090 strcpy( szScenarioNameDisplay, txt ); 1091 } 1092 1093 // Print scenario name. 1094 Conquer_Clip_Text_Print( szScenarioNameDisplay, d_gamekind_x, d_gamekind_y, scheme, TBLACK, TPF_TYPE, d_gamekind_w ); 1095 // pStaticDescrip->Draw_Me(); 1096 1097 sprintf(txt,"%d",Session.Options.UnitCount); 1098 pStaticUnit->Set_Text(txt); 1099 pStaticUnit->Draw_Me(); 1100 1101 if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { 1102 sprintf(txt,"%d",BuildLevel); 1103 } else { 1104 sprintf(txt, "**"); 1105 } 1106 pStaticLevel->Set_Text(txt); 1107 pStaticLevel->Draw_Me(); 1108 1109 sprintf(txt,"%d",Session.Options.Credits); 1110 pStaticCredits->Set_Text(txt); 1111 pStaticCredits->Draw_Me(); 1112 1113 sprintf(txt,"%d",Session.Options.AIPlayers); 1114 pStaticAIPlayers->Set_Text(txt); 1115 pStaticAIPlayers->Draw_Me(); 1116 } 1117 1118 Show_Mouse(); 1119 display = REDRAW_NONE; 1120 } 1121 1122 // Force mouse visible, as some beta testers report unexplicable disappearing cursors. 1123 while( Get_Mouse_State() ) 1124 Show_Mouse(); 1125 // Be nice to other apps. 1126 Sleep( 50 ); 1127 1128 //..................................................................... 1129 // Get user input 1130 //..................................................................... 1131 if( ( ::GetAsyncKeyState( KN_LMOUSE ) & 0x8000 ) || ( ::GetAsyncKeyState( KN_RMOUSE ) & 0x8000 ) ) 1132 { 1133 timeToolTipAppear = ::timeGetTime() + TOOLTIPDELAY; 1134 if( pToolTipHitLast && pToolTipHitLast->bShowing ) 1135 pToolTipHitLast->Unshow(); 1136 } 1137 1138 input = commands->Input(); 1139 1140 if( bHackFocus ) 1141 { 1142 pEditSend->Set_Focus(); 1143 pEditSend->Flag_To_Redraw(); 1144 input = commands->Input(); 1145 bHackFocus = false; 1146 } 1147 1148 // Tooltips... 1149 if( pToolTipHead ) 1150 { 1151 ToolTipClass* pToolTipHit = pToolTipHead->GetToolTipHit(); 1152 if( pToolTipHit == pToolTipHitLast ) 1153 { 1154 if( pToolTipHit && !pToolTipHit->bShowing && ::timeGetTime() > timeToolTipAppear && !( ( ::GetAsyncKeyState( KN_LMOUSE ) & 0x8000 ) || ( ::GetAsyncKeyState( KN_RMOUSE ) & 0x8000 ) ) ) 1155 { 1156 pToolTipHit->Show(); 1157 } 1158 } 1159 else 1160 { 1161 if( pToolTipHitLast && pToolTipHitLast->bShowing ) 1162 pToolTipHitLast->Unshow(); 1163 pToolTipHitLast = pToolTipHit; 1164 timeToolTipAppear = ::timeGetTime() + TOOLTIPDELAY; 1165 } 1166 } 1167 1168 // Yummy special hack. Luv' that UI system. 1169 if( input == 2049 ) // Left mouse released, not on control that captured it. 1170 { 1171 // Redraw the tabs in case they were what were pressed down on. 1172 pShpBtnScenarioRA->Flag_To_Redraw(); 1173 pShpBtnScenarioCS->Flag_To_Redraw(); 1174 pShpBtnScenarioAM->Flag_To_Redraw(); 1175 pShpBtnScenarioUser->Flag_To_Redraw(); 1176 } 1177 1178 //..................................................................... 1179 // Process input 1180 //..................................................................... 1181 switch( input ) 1182 { 1183 case KN_LMOUSE: 1184 if( !bWaitingToStart ) 1185 { 1186 // Check for mouse down on a control when player is not host. 1187 if( !bHost ) 1188 { 1189 if ( ( 1190 Get_Mouse_X() >= d_count_x && 1191 Get_Mouse_X() <= d_count_x + d_count_w && 1192 Get_Mouse_Y() >= d_count_y && 1193 Get_Mouse_Y() <= d_aiplayers_y + d_aiplayers_h 1194 ) || ( 1195 Get_Mouse_X() >= d_options_x && 1196 Get_Mouse_X() <= d_options_x + d_options_w && 1197 Get_Mouse_Y() >= d_options_y && 1198 Get_Mouse_Y() <= d_options_y + d_options_h 1199 ) || ( 1200 Get_Mouse_X() >= d_scenariolist_x && 1201 Get_Mouse_X() <= d_scenariolist_x + d_scenariolist_w && 1202 Get_Mouse_Y() >= d_scenariolist_y && 1203 Get_Mouse_Y() <= d_scenariolist_y + d_scenariolist_h 1204 ) ) 1205 { 1206 //Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_ONLY_HOST_CAN_MODIFY), PCOLOR_BROWN, TPF_TEXT, 1200); 1207 WOL_PrintMessage( *pILDisc, Text_String( TXT_ONLY_HOST_CAN_MODIFY ), WOLCOLORREMAP_LOCALMACHINEMESS ); 1208 Sound_Effect( WOLSOUND_ERROR ); 1209 if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; 1210 break; 1211 } 1212 } 1213 1214 if (Keyboard->MouseQX > cbox_x[0] && 1215 Keyboard->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && 1216 Keyboard->MouseQY > d_color_y && 1217 Keyboard->MouseQY < (d_color_y + d_color_h)) 1218 { 1219 Session.PrefColor = (PlayerColorType) 1220 ((Keyboard->MouseQX - cbox_x[0]) / d_color_w); 1221 1222 // Ensure that no one is using this color (to our knowledge). 1223 if( pILPlayers->FindColor( &ColorRemaps[ Session.PrefColor == PCOLOR_DIALOG_BLUE ? PCOLOR_REALLY_BLUE : Session.PrefColor ] ) == -1 ) 1224 { 1225 // Show me as the new color. 1226 // debugprint( "Color box pressed - " ); 1227 SetPlayerColor( pWO->szMyName, Session.PrefColor ); 1228 if( bHost ) 1229 { 1230 // Tell all guests about the color change. 1231 InformAboutPlayerColor( pWO->szMyName, Session.PrefColor, NULL ); 1232 } 1233 else 1234 { 1235 RequestPlayerColor( Session.PrefColor ); 1236 } 1237 } 1238 1239 } 1240 } 1241 break; 1242 1243 case ( BUTTON_DISCONNECT | KN_BUTTON ): 1244 if( WWMessageBox().Process( TXT_WOL_CONFIRMLOGOUT, TXT_YES, TXT_NO ) == 0 ) 1245 { 1246 // debugprint( "Logging out from gsup.\n" ); 1247 ExitGameChannel(); 1248 pWO->Logout(); 1249 bProcess = false; 1250 ResultReturn = RESULT_WOLGSUP_LOGOUT; 1251 } 1252 display = REDRAW_ALL; 1253 bHackFocus = true; 1254 break; 1255 1256 case ( BUTTON_REFRESH | KN_BUTTON ): // Always disabled. 1257 break; 1258 1259 case ( BUTTON_SQUELCH | KN_BUTTON ): 1260 pWO->DoSquelch( pILPlayers ); 1261 break; 1262 1263 case ( BUTTON_BAN | KN_BUTTON ): 1264 pWO->DoKick( pILPlayers, true ); 1265 // display = REDRAW_ALL; 1266 break; 1267 1268 case ( BUTTON_KICK | KN_BUTTON ): 1269 pWO->DoKick( pILPlayers, false ); 1270 // display = REDRAW_ALL; 1271 break; 1272 1273 case ( BUTTON_FINDPAGE | KN_BUTTON ): 1274 pWO->DoFindPage(); 1275 display = REDRAW_ALL; 1276 bHackFocus = true; 1277 break; 1278 1279 case ( BUTTON_OPTIONS | KN_BUTTON ): 1280 pWO->DoOptions(); 1281 display = REDRAW_ALL; 1282 bHackFocus = true; 1283 break; 1284 1285 case ( BUTTON_LADDER | KN_BUTTON ): 1286 pWO->DoLadder(); 1287 display = REDRAW_ALL; 1288 bHackFocus = true; 1289 break; 1290 1291 case ( BUTTON_HELP | KN_BUTTON ): 1292 pWO->DoHelp(); 1293 display = REDRAW_ALL; 1294 bHackFocus = true; 1295 break; 1296 1297 case ( BUTTON_HOUSE | KN_BUTTON ): 1298 Session.House = (HousesType)( pDropListHouse->Current_Index() + HOUSE_USSR ); 1299 /* 1300 // Bloody bloody hell I can't believe there are bugs in RA like the one I deal with here... 1301 if( strcmp( pDropListHouse->Current_Item(), "Russia" ) == 0 ) 1302 Session.House = HOUSE_USSR; 1303 else 1304 { 1305 Session.House = HouseTypeClass::From_Name( pDropListHouse->Current_Item() ); // Fails on "Russia". (Thinks "USSR".) 1306 if( Session.House == HOUSE_NONE ) 1307 { 1308 // debugprint( "Couldn't find house from selected '%s'.\n", pDropListHouse->Current_Item() ); 1309 } 1310 } 1311 */ 1312 if( pDropListHouse->IsDropped ) 1313 bRetractHouseDropDown = true; 1314 else 1315 if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; // Droplist already got contracted. 1316 1317 Sound_Effect(VOC_OPTIONS_CHANGED); 1318 break; 1319 1320 case ( BUTTON_AFTERMATHUNITS | KN_BUTTON ): 1321 bAftermathUnits = pCheckAftermathUnits->IsOn; 1322 break; 1323 1324 case ( BUTTON_SENDEDIT | KN_BUTTON ): 1325 // Enter has been pressed - was caught by pEditSend control. 1326 pWO->SendMessage( pEditSend->Get_Text(), *pILPlayers, false ); 1327 // Clear pEditSend, reset focus. 1328 szSendBuffer[0] = 0; 1329 pEditSend->Set_Focus(); 1330 // Mark for redraw. 1331 pEditSend->Flag_To_Redraw(); 1332 break; 1333 1334 case ( BUTTON_ACTION | KN_BUTTON ): 1335 // Enter has been pressed - was caught by pEditSend control. 1336 pWO->SendMessage( pEditSend->Get_Text(), *pILPlayers, true ); 1337 // Clear pEditSend, reset focus. 1338 szSendBuffer[0] = 0; 1339 pEditSend->Set_Focus(); 1340 // Mark for redraw. 1341 pEditSend->Flag_To_Redraw(); 1342 break; 1343 1344 //.................................................................. 1345 // New Scenario selected. 1346 //.................................................................. 1347 case (BUTTON_SCENARIOLIST | KN_BUTTON): 1348 { 1349 if( pILScens->Count() ) 1350 { 1351 int iSelectedScenIndex = (int)pILScens->Get_Item_ExtraDataPtr( pILScens->Current_Index() ); 1352 if( iSelectedScenIndex != Session.Options.ScenarioIndex ) 1353 { 1354 Session.Options.ScenarioIndex = iSelectedScenIndex; 1355 bInformParamChange = true; 1356 if( !pILScens->SetSelectType( 1 ) ) // Hack to deal with ListClass "highlighting nothing" problem. 1357 // SelectType was 0 before call. 1358 pILScens->Flag_To_Redraw(); 1359 // pStaticDescrip->Set_Text( pILScens->Get_Item( pILScens->Current_Index() ), false ); 1360 strcpy( szScenarioNameDisplay, pILScens->Get_Item( pILScens->Current_Index() ) ); 1361 //if (display < REDRAW_PARMS) display = REDRAW_PARMS; 1362 display = REDRAW_ALL; 1363 Sound_Effect(VOC_OPTIONS_CHANGED); 1364 } 1365 } 1366 break; 1367 } 1368 //.................................................................. 1369 // User adjusts max # units 1370 //.................................................................. 1371 case (BUTTON_COUNT | KN_BUTTON): 1372 Session.Options.UnitCount = pGaugeCount->Get_Value() + 1373 SessionClass::CountMin[Session.Options.Bases]; 1374 bInformParamChange = true; 1375 if (display < REDRAW_PARMS) display = REDRAW_PARMS; 1376 Sound_Effect(VOC_OPTIONS_CHANGED); 1377 break; 1378 1379 //.................................................................. 1380 // User adjusts build level 1381 //.................................................................. 1382 case (BUTTON_LEVEL | KN_BUTTON): 1383 BuildLevel = pGaugeLevel->Get_Value() + 1; 1384 if (BuildLevel > MPLAYER_BUILD_LEVEL_MAX) // if it's pegged, max it out 1385 BuildLevel = MPLAYER_BUILD_LEVEL_MAX; 1386 bInformParamChange = true; 1387 if (display < REDRAW_PARMS) display = REDRAW_PARMS; 1388 Sound_Effect(VOC_OPTIONS_CHANGED); 1389 break; 1390 1391 //.................................................................. 1392 // User edits the credits value; retransmit new game options 1393 // Round the credits to the nearest 500. 1394 //.................................................................. 1395 case (BUTTON_CREDITS | KN_BUTTON): 1396 Session.Options.Credits = pGaugeCredits->Get_Value(); 1397 Session.Options.Credits = 1398 ((Session.Options.Credits + 250) / 500) * 500; 1399 bInformParamChange = true; 1400 if (display < REDRAW_PARMS) display = REDRAW_PARMS; 1401 Sound_Effect(VOC_OPTIONS_CHANGED); 1402 break; 1403 1404 //.................................................................. 1405 // User adjusts # of AI players 1406 //.................................................................. 1407 case (BUTTON_AIPLAYERS | KN_BUTTON): 1408 Session.Options.AIPlayers = pGaugeAIPlayers->Get_Value(); 1409 // if (Session.Options.AIPlayers+Session.Players.Count() > Rule.MaxPlayers) { // if it's pegged, max it out 1410 if (Session.Options.AIPlayers + pWO->GameInfoCurrent.iPlayerMax > Rule.MaxPlayers) { // if it's pegged, max it out 1411 Session.Options.AIPlayers = Rule.MaxPlayers - pWO->GameInfoCurrent.iPlayerMax; 1412 pGaugeAIPlayers->Set_Value(Session.Options.AIPlayers); 1413 } 1414 bInformParamChange = true; 1415 if (display < REDRAW_PARMS) display = REDRAW_PARMS; 1416 Sound_Effect(VOC_OPTIONS_CHANGED); 1417 break; 1418 1419 //.................................................................. 1420 // Toggle-able options: 1421 // If 'Bases' gets toggled, we have to change the range of the 1422 // UnitCount slider. 1423 // Also, if Tiberium gets toggled, we have to set the flags 1424 // in SpecialClass. 1425 //.................................................................. 1426 case ( BUTTON_PARAMS | KN_BUTTON ): 1427 bRetractHouseDropDown = true; 1428 if (Special.IsCaptureTheFlag != pCheckListOptions->Is_Checked(3) && !Special.IsCaptureTheFlag) { 1429 pCheckListOptions->Check_Item(0, true); 1430 } 1431 if (Session.Options.Bases != pCheckListOptions->Is_Checked(0)) { 1432 Session.Options.Bases = pCheckListOptions->Is_Checked(0); 1433 if (Session.Options.Bases) { 1434 Session.Options.UnitCount = Fixed_To_Cardinal ( 1435 SessionClass::CountMax[1] - 1436 SessionClass::CountMin[1], 1437 Cardinal_To_Fixed( 1438 SessionClass::CountMax[0]-SessionClass::CountMin[0], 1439 Session.Options.UnitCount-SessionClass::CountMin[0])) + 1440 SessionClass::CountMin[1]; 1441 } else { 1442 pCheckListOptions->Check_Item(3, false); 1443 Session.Options.UnitCount = Fixed_To_Cardinal ( 1444 SessionClass::CountMax[0] - 1445 SessionClass::CountMin[0], 1446 Cardinal_To_Fixed( 1447 SessionClass::CountMax[1]-SessionClass::CountMin[1], 1448 Session.Options.UnitCount - SessionClass::CountMin[1])) + 1449 SessionClass::CountMin[0]; 1450 } 1451 pGaugeCount->Set_Maximum( 1452 SessionClass::CountMax[Session.Options.Bases] - 1453 SessionClass::CountMin[Session.Options.Bases]); 1454 pGaugeCount->Set_Value(Session.Options.UnitCount - 1455 SessionClass::CountMin[Session.Options.Bases]); 1456 } 1457 Session.Options.Tiberium = pCheckListOptions->Is_Checked(1); 1458 Special.IsTGrowth = Session.Options.Tiberium; // Ugh. Use of "Special" global. 1459 Rule.IsTGrowth = Session.Options.Tiberium; 1460 Special.IsTSpread = Session.Options.Tiberium; // Ugh. Use of "Special" global. 1461 Rule.IsTSpread = Session.Options.Tiberium; 1462 1463 Session.Options.Goodies = pCheckListOptions->Is_Checked(2); 1464 Special.IsCaptureTheFlag = pCheckListOptions->Is_Checked(3); // Ugh. Use of "Special" global. 1465 Special.IsShadowGrow = pCheckListOptions->Is_Checked(4); // Ugh. Use of "Special" global. 1466 1467 bSlowUnitBuildRate = pCheckListOptions->Is_Checked(5); 1468 1469 bInformParamChange = true; 1470 if (display < REDRAW_PARMS) display = REDRAW_PARMS; 1471 Sound_Effect(VOC_OPTIONS_CHANGED); 1472 break; 1473 1474 case ( BUTTON_ACCEPTSTART | KN_BUTTON ): // 'Accept' or 'Start Game' button. 1475 if( !bHost ) 1476 { 1477 // Guest wishes to accept game params. 1478 User* pUserHost = pWO->pGameHost(); 1479 if( pUserHost ) // Else it's too early to even be thinking about accepting, anyway. 1480 { 1481 // Set ourself as accepted. We want to do this immediately so the user has feedback for pressing button. 1482 // Besides, the host is guaranteed to allow this and set us to "accepted", unless it is simultaneously 1483 // sending us a new update we haven't received yet. If this is the case, we will get that update in a 1484 // second and will set ourselves to unaccepted, which will match the unaccepted state on the server. 1485 // Upshot - We can ignore messages from the server telling us that we, ourself, accepted. 1486 if( pToolTipHitLast && pToolTipHitLast->bShowing ) 1487 pToolTipHitLast->Unshow(); 1488 pTextBtnAcceptStart->Disable(); 1489 if( SetPlayerAccepted( pWO->szMyName, true ) ) // Else we didn't find ourself in list! 1490 { 1491 // debugprint( "Sending accept.\n" ); 1492 // Tell host we accept. 1493 char szSend[ 20 ]; 1494 sprintf( szSend, "%02u %06u", WOL_GAMEOPT_REQACCEPT, nGuestLastParamID ); 1495 pWO->SendGameOpt( szSend, pUserHost ); 1496 } 1497 } 1498 } 1499 else 1500 { 1501 // Host says start the game. 1502 // If we have changes just made and not yet sent, don't say start, because we're about to send changes 1503 // that will unaccept everyone. 1504 if( !bParamsUnfresh() ) 1505 { 1506 // Force user to put the correct disk in before proceeding. (Not crucial, but can lead to ugly 1507 // timeouts if the scenario has to be downloaded before game start.) 1508 if( !Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Official() || 1509 Force_Scenario_Available( Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename() ) ) 1510 { 1511 // Go into "waiting to start" mode, tell guests to, and wait for responses. 1512 bWaitingToStart = true; 1513 timeWaitingToStartTimeout = ::timeGetTime() + 30000; 1514 nHostLastParamID++; 1515 InformAboutStart(); 1516 WWMessageBox().Process( TXT_WOL_WAITINGTOSTART, TXT_NONE ); 1517 BindControls( false ); 1518 SetPlayerReadyToGo( pWO->szMyName, "ready" ); 1519 Sound_Effect( VOC_GAME_CLOSED ); 1520 } 1521 } 1522 } 1523 break; 1524 1525 case ( BUTTON_SCENARIO_RA | KN_BUTTON ): 1526 ScenarioDisplayMode( SCENARIO_RA ); 1527 break; 1528 case ( BUTTON_SCENARIO_CS | KN_BUTTON ): 1529 ScenarioDisplayMode( SCENARIO_CS ); 1530 break; 1531 case ( BUTTON_SCENARIO_AM | KN_BUTTON ): 1532 ScenarioDisplayMode( SCENARIO_AM ); 1533 break; 1534 case ( BUTTON_SCENARIO_USER | KN_BUTTON ): 1535 ScenarioDisplayMode( SCENARIO_USER ); 1536 break; 1537 1538 // case (BUTTON_LOAD | KN_BUTTON): 1539 // case (BUTTON_OK | KN_BUTTON): 1540 // break; 1541 1542 case (KN_ESC): 1543 if( pDropListHouse->IsDropped ) 1544 bRetractHouseDropDown = true; 1545 break; 1546 1547 case ( BUTTON_LEAVE | KN_BUTTON ): 1548 case ( BUTTON_CANCEL | KN_BUTTON ): 1549 if( ExitGameChannel() ) 1550 { 1551 pWO->RejoinLobbyAfterGame(); 1552 bProcess = false; 1553 ResultReturn = RESULT_WOLGSUP_BACKTOCHAT; // Return to chat. 1554 } 1555 else 1556 { 1557 bProcess = false; 1558 ResultReturn = RESULT_WOLGSUP_FATALERROR; // Return with an error value. 1559 } 1560 break; 1561 } 1562 1563 Call_Back(); 1564 } 1565 1566 if( pToolTipHitLast && pToolTipHitLast->bShowing ) 1567 pToolTipHitLast->Unshow(); 1568 1569 pWO->ClearListPtrs(); 1570 pWO->pGSupDlg = NULL; 1571 1572 return ResultReturn; 1573 } 1574 1575 //*********************************************************************************************** 1576 void WOL_GameSetupDialog::SetSpecialControlStates() 1577 { 1578 // Set gauges and checklist. 1579 1580 pCheckListOptions->Check_Item(0, Session.Options.Bases); 1581 pCheckListOptions->Check_Item(1, Session.Options.Tiberium); 1582 pCheckListOptions->Check_Item(2, Session.Options.Goodies); 1583 pCheckListOptions->Check_Item(3, Special.IsCaptureTheFlag); // Ugh. Use of "Special" global. 1584 pCheckListOptions->Check_Item(4, Special.IsShadowGrow); // Ugh. Use of "Special" global. 1585 1586 pCheckListOptions->Check_Item(5, bSlowUnitBuildRate); // Ugh. Use of "Special" global. 1587 1588 pGaugeCount->Set_Maximum(SessionClass::CountMax[Session.Options.Bases] - SessionClass::CountMin[Session.Options.Bases]); 1589 pGaugeCount->Set_Value(Session.Options.UnitCount - SessionClass::CountMin[Session.Options.Bases]); 1590 1591 pGaugeLevel->Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); 1592 pGaugeLevel->Set_Value(BuildLevel - 1); 1593 1594 pGaugeCredits->Set_Maximum(Rule.MPMaxMoney); 1595 pGaugeCredits->Set_Value(Session.Options.Credits); 1596 1597 if( pWO->GameInfoCurrent.bTournament ) 1598 { 1599 pGaugeAIPlayers->Set_Maximum( 0 ); 1600 } 1601 else 1602 { 1603 // Note dependency of AIPlayers on number of human players. 1604 // pGaugeAIPlayers->Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); 1605 pGaugeAIPlayers->Set_Maximum( Rule.MaxPlayers - pWO->GameInfoCurrent.iPlayerMax ); 1606 } 1607 pGaugeAIPlayers->Set_Value(Session.Options.AIPlayers); 1608 1609 if( bAftermathUnits ) 1610 pCheckAftermathUnits->Turn_On(); 1611 else 1612 pCheckAftermathUnits->Turn_Off(); 1613 } 1614 1615 #define DRAWTABDOWN Turn_On() 1616 #define DRAWTABUP Turn_Off() 1617 1618 //*********************************************************************************************** 1619 void WOL_GameSetupDialog::BindControls( bool bBind ) 1620 { 1621 if( bBind ) 1622 { 1623 commands = pWO->pShpBtnDiscon; 1624 pWO->pShpBtnLeave->Add_Tail(*commands); 1625 pWO->pShpBtnRefresh->Add_Tail(*commands); 1626 pWO->pShpBtnSquelch->Add_Tail(*commands); 1627 pWO->pShpBtnBan->Add_Tail(*commands); 1628 pWO->pShpBtnKick->Add_Tail(*commands); 1629 pWO->pShpBtnFindpage->Add_Tail(*commands); 1630 pWO->pShpBtnOptions->Add_Tail(*commands); 1631 pWO->pShpBtnLadder->Add_Tail(*commands); 1632 pWO->pShpBtnHelp->Add_Tail(*commands); 1633 pILPlayers->Add_Tail(*commands); 1634 if( bHost ) 1635 { 1636 // Draw order of tabs depends on which one is selected, as we want them to overlap appropriately. 1637 // Also, the selected tab must appear over the scenario list, while the unselected tabs are below it. 1638 // This assures that that bottom of the tab overlaps the scenario list border, making it look connected. 1639 1640 // ajw I could make all maps always available now that they're downloadable. Playing CS map with AM rules 1641 // would mean switching CDs, though. 1642 // Would mean no difference between RA and CS games. 1643 1644 switch( ScenKindCurrent ) 1645 { 1646 case SCENARIO_RA: 1647 if( !pWO->GameInfoCurrent.bTournament ) 1648 { 1649 pShpBtnScenarioUser->Add_Tail(*commands); 1650 pShpBtnScenarioUser->DRAWTABDOWN; 1651 } 1652 if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::CSGAME ) 1653 { 1654 pShpBtnScenarioCS->Add_Tail(*commands); 1655 pShpBtnScenarioCS->DRAWTABDOWN; 1656 } 1657 else if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME || 1658 ( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::RAGAME && Is_Aftermath_Installed() && !pWO->GameInfoCurrent.bTournament ) ) 1659 { 1660 pShpBtnScenarioAM->Add_Tail(*commands); 1661 pShpBtnScenarioAM->DRAWTABDOWN; 1662 } 1663 pILScens->Add_Tail(*commands); 1664 pShpBtnScenarioRA->Add_Tail(*commands); 1665 pShpBtnScenarioRA->DRAWTABUP; 1666 break; 1667 case SCENARIO_CS: // pWO->GameInfoCurrent.GameKind must be CREATEGAMEINFO::CSGAME. 1668 if( !pWO->GameInfoCurrent.bTournament ) 1669 { 1670 pShpBtnScenarioUser->Add_Tail(*commands); 1671 pShpBtnScenarioUser->DRAWTABDOWN; 1672 } 1673 pShpBtnScenarioRA->Add_Tail(*commands); 1674 pShpBtnScenarioRA->DRAWTABDOWN; 1675 pILScens->Add_Tail(*commands); 1676 pShpBtnScenarioCS->Add_Tail(*commands); 1677 pShpBtnScenarioCS->DRAWTABUP; 1678 break; 1679 case SCENARIO_AM: // pWO->GameInfoCurrent.GameKind must be CREATEGAMEINFO::AMGAME, or RAGAME with AM installed. 1680 if( !pWO->GameInfoCurrent.bTournament ) 1681 { 1682 pShpBtnScenarioUser->Add_Tail(*commands); 1683 pShpBtnScenarioUser->DRAWTABDOWN; 1684 } 1685 pShpBtnScenarioRA->Add_Tail(*commands); 1686 pShpBtnScenarioRA->DRAWTABDOWN; 1687 pILScens->Add_Tail(*commands); 1688 pShpBtnScenarioAM->Add_Tail(*commands); 1689 pShpBtnScenarioAM->DRAWTABUP; 1690 break; 1691 case SCENARIO_USER: // pWO->GameInfoCurrent.bTournament must be false. 1692 pShpBtnScenarioRA->Add_Tail(*commands); 1693 pShpBtnScenarioRA->DRAWTABDOWN; 1694 if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::CSGAME ) 1695 { 1696 pShpBtnScenarioCS->Add_Tail(*commands); 1697 pShpBtnScenarioCS->DRAWTABDOWN; 1698 } 1699 else if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME || 1700 ( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::RAGAME && Is_Aftermath_Installed() && !pWO->GameInfoCurrent.bTournament ) ) 1701 { 1702 pShpBtnScenarioAM->Add_Tail(*commands); 1703 pShpBtnScenarioAM->DRAWTABDOWN; 1704 } 1705 pILScens->Add_Tail(*commands); 1706 pShpBtnScenarioUser->Add_Tail(*commands); 1707 pShpBtnScenarioUser->DRAWTABUP; 1708 break; 1709 } 1710 } 1711 // pStaticDescrip->Add_Tail(*commands); 1712 pEditSend->Add_Tail(*commands); 1713 pStaticUnit->Add_Tail(*commands); 1714 pStaticLevel->Add_Tail(*commands); 1715 pStaticCredits->Add_Tail(*commands); 1716 pStaticAIPlayers->Add_Tail(*commands); 1717 pGaugeCount->Add_Tail(*commands); 1718 pGaugeCredits->Add_Tail(*commands); 1719 pGaugeAIPlayers->Add_Tail(*commands); 1720 pGaugeLevel->Add_Tail(*commands); 1721 pCheckListOptions->Add_Tail(*commands); 1722 pTextBtnCancel->Add_Tail(*commands); 1723 pTextBtnAcceptStart->Add_Tail(*commands); 1724 pTextBtnAction->Add_Tail(*commands); 1725 pILDisc->Add_Tail(*commands); 1726 pDropListHouse->Add_Tail(*commands); 1727 // pCheckAftermathUnits->Add_Tail(*commands); 1728 } 1729 else 1730 { 1731 pWO->pShpBtnDiscon->Zap(); 1732 pWO->pShpBtnLeave->Zap(); 1733 pWO->pShpBtnRefresh->Zap(); 1734 pWO->pShpBtnSquelch->Zap(); 1735 pWO->pShpBtnBan->Zap(); 1736 pWO->pShpBtnKick->Zap(); 1737 pWO->pShpBtnFindpage->Zap(); 1738 pWO->pShpBtnOptions->Zap(); 1739 pWO->pShpBtnLadder->Zap(); 1740 pWO->pShpBtnHelp->Zap(); 1741 pILPlayers->Zap(); 1742 if( bHost ) 1743 { 1744 pShpBtnScenarioRA->Zap(); 1745 if( !pWO->GameInfoCurrent.bTournament ) 1746 { 1747 pShpBtnScenarioUser->Zap(); 1748 } 1749 if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::CSGAME ) 1750 pShpBtnScenarioCS->Zap(); 1751 else if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME || 1752 ( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::RAGAME && Is_Aftermath_Installed() && !pWO->GameInfoCurrent.bTournament ) ) 1753 pShpBtnScenarioAM->Zap(); 1754 pILScens->Zap(); 1755 } 1756 // pStaticDescrip->Zap(); 1757 pEditSend->Zap(); 1758 pStaticUnit->Zap(); 1759 pStaticLevel->Zap(); 1760 pStaticCredits->Zap(); 1761 pStaticAIPlayers->Zap(); 1762 pGaugeCount->Zap(); 1763 pGaugeCredits->Zap(); 1764 pGaugeAIPlayers->Zap(); 1765 pGaugeLevel->Zap(); 1766 pCheckListOptions->Zap(); 1767 pTextBtnCancel->Zap(); 1768 pTextBtnAcceptStart->Zap(); 1769 pTextBtnAction->Zap(); 1770 pILDisc->Zap(); 1771 pDropListHouse->Zap(); 1772 // pCheckAftermathUnits->Zap(); 1773 } 1774 } 1775 1776 //*********************************************************************************************** 1777 void WOL_GameSetupDialog::ScenarioDisplayMode( SCENARIO_GAMEKIND ScenKind ) 1778 { 1779 // debugprint( "ScenarioDisplayMode, from %i going into %i mode.\n", ScenKindCurrent, ScenKind ); 1780 1781 // Puts us into mode where we are viewing a particular gamekind of scenario list. 1782 if( ScenKindCurrent == ScenKind ) 1783 return; 1784 1785 // Reorder tab buttons. 1786 BindControls( false ); 1787 ScenKindCurrent = ScenKind; 1788 BindControls( true ); 1789 1790 // Reset list items. 1791 pILScens->Clear(); 1792 // debugprint( " * * * * * * ScenKind %i has %i items.\n", ScenKind, ar_szScenarios[ ScenKind ].Count() ); 1793 // Check for the currently selected scenario. 1794 bool bFoundCurrentSelection = false; 1795 int iSelect; 1796 for( int i = 0; i != ar_szScenarios[ ScenKind ].Count(); i++ ) 1797 { 1798 // Put ScenarioIndex in as extradata to list item. 1799 int iScenIndex = (int)ar_szScenIndexes[ ScenKind ][ i ]; 1800 int iIndexNew = pILScens->Add_Item( ar_szScenarios[ ScenKind ][ i ], NULL, NULL, ICON_DIB, NULL, (void*)iScenIndex ); 1801 if( iScenIndex == Session.Options.ScenarioIndex && !bFoundCurrentSelection ) 1802 { 1803 // (Choose first line of what can be multiline description of currently selected scenario.) 1804 bFoundCurrentSelection = true; 1805 iSelect = i; 1806 } 1807 } 1808 // If the current scenario selection is in this list, enable the list to show the selection, otherwise, 1809 // make the listclass selection invisible, because it doesn't indicate the real scenario selection. 1810 // Basically, problem is that I can't have no selection in ListClass, and I don't want to risk changing 1811 // it as I'll affect the rest of the code. So I do this horrible hack. 1812 if( bFoundCurrentSelection ) 1813 { 1814 pILScens->SetSelectType( 1 ); // Regular selection. 1815 pILScens->Set_Selected_Index( iSelect ); 1816 } 1817 else 1818 pILScens->SetSelectType( 0 ); // Invisible selection. 1819 1820 // display = REDRAW_ALL; 1821 pILScens->Flag_To_Redraw(); 1822 pShpBtnScenarioRA->Flag_To_Redraw(); 1823 pShpBtnScenarioCS->Flag_To_Redraw(); 1824 pShpBtnScenarioAM->Flag_To_Redraw(); 1825 pShpBtnScenarioUser->Flag_To_Redraw(); 1826 } 1827 1828 //*********************************************************************************************** 1829 bool WOL_GameSetupDialog::ExitGameChannel() 1830 { 1831 // debugprint( "ExitGameChannel \n" ); 1832 if( pDropListHouse->IsDropped ) 1833 { 1834 pDropListHouse->Collapse(); 1835 if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; 1836 } 1837 if( !pWO->ChannelLeave() ) 1838 { 1839 pWO->GenericErrorMessage(); 1840 return false; 1841 } 1842 1843 pWO->OnExitingGameChannel(); 1844 1845 return true; 1846 } 1847 1848 //*********************************************************************************************** 1849 void WOL_GameSetupDialog::DrawScenarioDescripIcon( const char* pDIB ) const 1850 { 1851 CC_Draw_DIB( pDIB, d_gamekind_x - 16, d_gamekind_y - 2, 100, WINDOW_MAIN ); 1852 } 1853 1854 //*********************************************************************************************** 1855 void WOL_GameSetupDialog::SetPlayerColor( const char* szName, PlayerColorType Color ) 1856 { 1857 // Sets player color - does not verify if it is "okay" to do so. 1858 // debugprint( "SetPlayerColor %s to %i\n", szName, Color ); 1859 1860 int iItem = pILPlayers->Find( szName ); 1861 if( iItem == -1 ) 1862 { 1863 // Player name was not found in list. 1864 // This can happen when player color Informs arrive from the host before I have gotten a userlist. 1865 // Insert an entry for user - color will be maintained when the userlist arrives. 1866 // "early insertion" 1867 // debugprint( "SetPlayerColor could not find name '%s'! Inserting...\n", szName ); 1868 iItem = pILPlayers->Add_Item( szName ); 1869 } 1870 1871 if( strcmp( pWO->szMyName, szName ) == 0 ) 1872 { 1873 // I am the player involved. 1874 Session.ColorIdx = Color; 1875 if( display < REDRAW_COLORS ) display = REDRAW_COLORS; 1876 } 1877 pILPlayers->Set_Item_Color( iItem, &ColorRemaps[ Color == PCOLOR_DIALOG_BLUE ? PCOLOR_REALLY_BLUE : Color ] ); 1878 pILPlayers->Flag_To_Redraw(); 1879 1880 } 1881 1882 //*********************************************************************************************** 1883 PlayerColorType WOL_GameSetupDialog::GetPlayerColor( const char* szName ) 1884 { 1885 // Returns player color, if player is found in list, else PCOLOR_NONE. 1886 int iItem = pILPlayers->Find( szName ); 1887 if( iItem == -1 ) 1888 { 1889 // Player name was not found in list. 1890 return PCOLOR_NONE; 1891 } 1892 RemapControlType* pRemap = pILPlayers->Get_Item_Color( iItem ); 1893 return PlayerColorTypeOf( pRemap ); 1894 } 1895 1896 //*********************************************************************************************** 1897 void WOL_GameSetupDialog::SetPlayerHouse( const char* szName, HousesType House ) 1898 { 1899 // Sets player house - does not verify if it is "okay" to do so. 1900 // debugprint( "SetPlayerHouse %s to %i\n", szName, House ); 1901 1902 int iItem = pILPlayers->Find( szName ); 1903 if( iItem == -1 ) 1904 { 1905 // Player name was not found in list. 1906 // This can happen when player house Informs arrive from the host before I have gotten a userlist. 1907 // Insert an entry for user - house will be maintained when the userlist arrives. 1908 // "early insertion" 1909 // debugprint( "SetPlayerHouse could not find name '%s'! Inserting...\n", szName ); 1910 iItem = pILPlayers->Add_Item( szName ); 1911 } 1912 1913 // Reset item text. 1914 char szItem[ 100 ]; 1915 pWO->WritePlayerListItem( szItem, szName, House ); 1916 // debugprint ( "%i, %s\n", iItem, szItem ); 1917 pILPlayers->Set_Item( iItem, szItem ); 1918 pILPlayers->Flag_To_Redraw(); 1919 } 1920 1921 //*********************************************************************************************** 1922 HousesType WOL_GameSetupDialog::GetPlayerHouse( const char* szName ) 1923 { 1924 // Returns player house for user if found in list, else HOUSE_NONE. 1925 int iItem = pILPlayers->Find( szName ); 1926 if( iItem == -1 ) 1927 { 1928 // Player name was not found in list. 1929 return HOUSE_NONE; 1930 } 1931 1932 return pWO->PullPlayerHouse_From( pILPlayers->Get_Item( iItem ) ); 1933 } 1934 1935 //*********************************************************************************************** 1936 bool WOL_GameSetupDialog::SetPlayerAccepted( const char* szName, bool bAccepted ) 1937 { 1938 // Sets player's 'accepted' state to true or false. 1939 // Value is stored in the player list: if there is an accepted icon, player has accepted. 1940 int iItem = pILPlayers->Find( szName ); 1941 if( iItem == -1 ) 1942 { 1943 // Player name was not found in list. 1944 // debugprint( "SetPlayerAccepted() - could not find '%s'.\n", szName ); 1945 return false; 1946 } 1947 // debugprint( "SetPlayerAccepted() - set '%s' to %s.\n", szName, bAccepted ? "accepted" : "NOT accepted" ); 1948 return pWO->MarkItemAccepted( iItem, bAccepted ); 1949 } 1950 1951 //*********************************************************************************************** 1952 bool WOL_GameSetupDialog::IveAccepted() 1953 { 1954 // Returns true if I am marked as "accepted". 1955 int iItem = pILPlayers->Find( pWO->szMyName ); 1956 if( iItem == -1 ) 1957 return false; 1958 return pWO->bItemMarkedAccepted( iItem ); 1959 } 1960 1961 //*********************************************************************************************** 1962 bool WOL_GameSetupDialog::SetPlayerReadyToGo( const char* szName, const char* szReadyState ) 1963 { 1964 // Sets player's 'ready to go' state. 1965 // Set szReadyState to "ready", "need scenario", or NULL. 1966 // First two cases are regarded as player being ready to go. 1967 // Value is stored in the player list in the hidden string field. 1968 int iItem = pILPlayers->Find( szName ); 1969 if( iItem == -1 ) 1970 { 1971 // Player name was not found in list. 1972 // debugprint( "SetPlayerReadyToGo() - could not find '%s'.\n", szName ); 1973 return false; 1974 } 1975 // debugprint( "SetPlayerReadyToGo() - set '%s' to %s.\n", szName, szReadyState ); 1976 pWO->MarkItemReadyToGo( iItem, szReadyState ); 1977 1978 return true; 1979 } 1980 1981 //*********************************************************************************************** 1982 void WOL_GameSetupDialog::ResetReadyToGo() 1983 { 1984 // Resets all users to "not ready to go". 1985 for( int i = 0; i < pILPlayers->Count(); i++ ) 1986 { 1987 pWO->MarkItemReadyToGo( i, NULL ); 1988 } 1989 } 1990 1991 //*********************************************************************************************** 1992 bool WOL_GameSetupDialog::bPlayerReadyToGo( const char* szName ) 1993 { 1994 // Returns true if player is marked as "ready to go". 1995 int iItem = pILPlayers->Find( szName ); 1996 if( iItem == -1 ) 1997 return false; 1998 return pWO->bItemMarkedReadyToGo( iItem ); 1999 } 2000 2001 //*********************************************************************************************** 2002 bool WOL_GameSetupDialog::bAllPlayersReadyToGo() 2003 { 2004 // Returns true if all players are marked as "ready to go". 2005 // debugprint( "Checking for all players ready - there are %i\n", pILPlayers->Count() ); 2006 for( int i = 0; i < pILPlayers->Count(); i++ ) 2007 { 2008 if( !pWO->bItemMarkedReadyToGo( i ) ) 2009 { 2010 // debugprint( "Item %i NOT ready\n", i ); 2011 return false; 2012 } 2013 // debugprint( "Item %i is ready\n", i ); 2014 } 2015 // debugprint( "Return true\n" ); 2016 return true; 2017 } 2018 2019 //*********************************************************************************************** 2020 void WOL_GameSetupDialog::ProcessGuestRequest( User* pUser, const char* szRequest ) 2021 { 2022 // Game host processes a request that arrived as a privategameopt from one of the guests. 2023 // WOL_GAMEOPT_REQCOLOR format: 2024 // 2 WOL_GAMEOPT 2025 // 1 space 2026 // 2 color 2027 // 1 null-terminator 2028 // debugprint( "ProcessGuestRequest. szRequest is '%s', len %i.\n", szRequest, strlen( szRequest ) ); 2029 WOL_GAMEOPT opt = (WOL_GAMEOPT)atoi( szRequest ); 2030 szRequest += 3; 2031 2032 switch( opt ) 2033 { 2034 case WOL_GAMEOPT_REQCOLOR: 2035 { 2036 PlayerColorType ColorDesired = (PlayerColorType)( atoi( szRequest ) ); 2037 if( pILPlayers->FindColor( &ColorRemaps[ ColorDesired == PCOLOR_DIALOG_BLUE ? PCOLOR_REALLY_BLUE : ColorDesired ] ) == -1 ) 2038 { 2039 // Color is available. 2040 SetPlayerColor( (char*)pUser->name, ColorDesired ); 2041 // Tell all guests about the color change. 2042 InformAboutPlayerColor( (char*)pUser->name, ColorDesired, NULL ); 2043 } 2044 else 2045 { 2046 // Color is not available. 2047 // debugprint( "Color %i denied to %s\n", ColorDesired, (char*)pUser->name ); 2048 // Tell requestor that his color is still the same. 2049 RemapControlType* pColorRemapCurrent = pILPlayers->Get_Item_Color( pILPlayers->Find( (char*)pUser->name ) ); 2050 InformAboutPlayerColor( (char*)pUser->name, PlayerColorTypeOf( pColorRemapCurrent ), pUser ); 2051 } 2052 break; 2053 } 2054 case WOL_GAMEOPT_REQHOUSE: 2055 { 2056 HousesType HouseChoice = (HousesType)( atoi( szRequest ) ); 2057 // debugprint( "Host received: '%s' changed house to %u.\n", (char*)pUser->name, HouseChoice ); 2058 SetPlayerHouse( (char*)pUser->name, HouseChoice ); 2059 nHostLastParamID++; 2060 InformAboutPlayerHouse( (char*)pUser->name, HouseChoice, NULL ); 2061 ClearAllAccepts(); 2062 break; 2063 } 2064 case WOL_GAMEOPT_REQACCEPT: 2065 // Does Param ID of accept request match the last param change ID sent? See notes at top. 2066 if( atoi( szRequest ) == nHostLastParamID ) 2067 { 2068 // debugprint( "Host received valid accept from '%s'.\n", (char*)pUser->name ); 2069 SetPlayerAccepted( (char*)pUser->name, true ); 2070 InformAboutPlayerAccept( (char*)pUser->name, NULL ); 2071 // We may be ready to start a game now. 2072 if( bAllGuestsAccept() ) 2073 { 2074 if( pToolTipHitLast && pToolTipHitLast->bShowing ) 2075 pToolTipHitLast->Unshow(); 2076 pTextBtnAcceptStart->Enable(); 2077 } 2078 } 2079 else 2080 { 2081 // debugprint( "______________Host received invalid accept from '%s'. ID = %i when it should be %i.\n", (char*)pUser->name, 2082 // atoi( szRequest ), nHostLastParamID ); 2083 } 2084 break; 2085 case WOL_GAMEOPT_REQSTART: 2086 // Does Param ID of accept request match the last param change ID sent? See notes at top. 2087 if( atoi( szRequest ) == nHostLastParamID ) // Otherwise ignore - it's old and we don't care. (Incredibly unlikely to happen, actually.) 2088 { 2089 // debugprint( "Host received valid WOL_GAMEOPT_REQSTART from '%s'.\n", (char*)pUser->name ); 2090 // WOL_PrintMessage( *pILDisc, "WOL_GAMEOPT_REQSTART response", WOLCOLORREMAP_LOCALMACHINEMESS ); 2091 // WOL_PrintMessage( *pILDisc, (char*)pUser->name, WOLCOLORREMAP_LOCALMACHINEMESS ); 2092 if( bWaitingToStart ) 2093 // If all responses are in, start the game! 2094 GuestIsReadyToPlay( (char*)pUser->name, "ready" ); 2095 // else 2096 // debugprint( "Ignoring - I am no longer waiting to start a game.\n" ); 2097 } 2098 break; 2099 case WOL_GAMEOPT_REQSTART_BUTNEEDSCENARIO: 2100 // Does Param ID of accept request match the last param change ID sent? See notes at top. 2101 if( atoi( szRequest ) == nHostLastParamID ) // Otherwise ignore - it's old and we don't care. (Incredibly unlikely to happen, actually.) 2102 { 2103 // debugprint( "Host received valid WOL_GAMEOPT_REQSTART_BUTNEEDSCENARIO from '%s'.\n", (char*)pUser->name ); 2104 if( bWaitingToStart ) 2105 // If all responses are in, start the game! 2106 GuestIsReadyToPlay( (char*)pUser->name, "need scenario" ); 2107 // else 2108 // debugprint( "Ignoring - I am no longer waiting to start a game.\n" ); 2109 } 2110 break; 2111 case WOL_GAMEOPT_INFGO: 2112 // I have told myself to start game right now. 2113 // This is the new method. Avoids apparent "private messages/gameopts are getting delayed" problem in chatserver... 2114 // (I don't want to end up leaving the channel before guests get my go message.) 2115 strcpy( szTriggerGameStartInfo, szRequest ); 2116 break; 2117 default: 2118 // debugprint( "Unhandled value of %i in ProcessGuestRequest!!!\n", opt ); 2119 break; 2120 } 2121 } 2122 2123 //*********************************************************************************************** 2124 void WOL_GameSetupDialog::ProcessInform( char* szInform ) 2125 { 2126 // Process inform message arriving from game host. 2127 // debugprint( "ProcessInform: '%s'\n", szInform ); 2128 if( !bHost ) 2129 { 2130 WOL_GAMEOPT opt = (WOL_GAMEOPT)atoi( szInform ); 2131 szInform += 3; 2132 switch( opt ) 2133 { 2134 case WOL_GAMEOPT_INFCOLOR: 2135 { 2136 // WOL_GAMEOPT_INFCOLOR format: 2137 // 2 WOL_GAMEOPT 2138 // 1 space 2139 // 2 color 2140 // 1 space 2141 // string name of player 2142 PlayerColorType Color = (PlayerColorType)( atoi( szInform ) ); 2143 szInform += 3; 2144 SetPlayerColor( szInform, Color ); // (szInform is now sitting at the start of the name string.) 2145 Sound_Effect(VOC_OPTIONS_CHANGED); 2146 break; 2147 } 2148 case WOL_GAMEOPT_INFHOUSE: // Note: In theory, I could ignore this if it refers to me. I've already set my own house. 2149 { 2150 nGuestLastParamID = atoi( szInform ); 2151 szInform += 7; 2152 HousesType House = (HousesType)( atoi( szInform ) ); 2153 szInform += 3; 2154 SetPlayerHouse( szInform, House ); // (szInform is now sitting at the start of the name string.) 2155 ClearAllAccepts(); 2156 Sound_Effect(VOC_OPTIONS_CHANGED); 2157 break; 2158 } 2159 case WOL_GAMEOPT_INFACCEPT: // Note: In theory, I could ignore this if it refers to me. 2160 // A guest has accepted. 2161 SetPlayerAccepted( szInform, true ); // (szInform is now sitting at the start of the name string.) 2162 break; 2163 case WOL_GAMEOPT_INFPARAMS: 2164 // Game params have changed. 2165 bParamsReceived = true; 2166 if( !AcceptParams( szInform ) ) 2167 bLeaveDueToRulesMismatchTrigger = true; 2168 SetSpecialControlStates(); 2169 //pILScens->Set_Selected_Index( Session.Options.ScenarioIndex ); 2170 display = REDRAW_ALL; 2171 ClearAllAccepts(); 2172 Sound_Effect(VOC_OPTIONS_CHANGED); 2173 break; 2174 case WOL_GAMEOPT_INFNEWGUESTPLAYERINFO: 2175 // I have just joined and have received a message with info on all players in game. 2176 AcceptNewGuestPlayerInfo( szInform ); 2177 Sound_Effect(VOC_OPTIONS_CHANGED); 2178 break; 2179 case WOL_GAMEOPT_INFSTART: 2180 { 2181 // Host tells us to wait for start of game. 2182 // debugprint( "Guest received WOL_GAMEOPT_INFSTART.\n" ); 2183 nGuestLastParamID = atoi( szInform ); 2184 // The following check is not necessary. Rules.ini, if manually replaced by a cheater, is not reloaded. 2185 // So prior checks (that occur on game params receives) are sufficient. 2186 // szInform += 7; 2187 // // Check rules.ini compatibility. 2188 // int iRulesID = atoi( szInform ); 2189 // if( RuleINI.Get_Unique_ID() != iRulesID ) 2190 // { 2191 // // Rules.ini incompatible. Don't respond to call for start. 2192 // bLeaveDueToRulesMismatchTrigger = true; 2193 // break; 2194 // } 2195 User* pUserHost = pWO->pGameHost(); 2196 if( pUserHost ) // This better'd be true. 2197 { 2198 // Send ack back to host. 2199 char szSend[ 20 ]; 2200 // Make sure we have the scenario. 2201 if( !bNeedScenarioDownload() ) 2202 sprintf( szSend, "%02u %06u", WOL_GAMEOPT_REQSTART, nGuestLastParamID ); 2203 else 2204 sprintf( szSend, "%02u %06u", WOL_GAMEOPT_REQSTART_BUTNEEDSCENARIO, nGuestLastParamID ); 2205 pWO->SendGameOpt( szSend, pUserHost ); 2206 // Enter waiting mode. 2207 bWaitingToStart = true; 2208 WWMessageBox().Process( TXT_WOL_WAITINGTOSTART, TXT_NONE ); 2209 BindControls( false ); 2210 // If we are in a modal dialog, we must have arrived here through Call_Back()'s PumpMessages. 2211 // Set global that will force edit dialogs to stop accepting characters. 2212 // This is to fix a minor glitch: guests can keep typing into a "page" dialog editbox after the 2213 // "Launching game..." message has appeared on top of it. 2214 if( pWO->bPump_In_Call_Back ) 2215 disable_current_msgbox = true; 2216 } 2217 else 2218 { 2219 // debugprint( "Impossible arose on WOL_GAMEOPT_INFSTART.\n" ); 2220 Fatal( "Impossible arose on WOL_GAMEOPT_INFSTART.\n" ); 2221 } 2222 Sound_Effect( VOC_GAME_CLOSED ); 2223 break; 2224 } 2225 case WOL_GAMEOPT_INFCANCELSTART: 2226 // Host tells us to cancel waiting for start of game. 2227 bWaitingToStart = false; 2228 ClearAllAccepts(); 2229 BindControls( true ); 2230 WOL_PrintMessage( *pILDisc, TXT_WOL_STARTCANCELLED, WOLCOLORREMAP_LOCALMACHINEMESS ); 2231 Sound_Effect( VOC_SYS_ERROR ); 2232 display = REDRAW_ALL; 2233 // If we are in a modal dialog, we must have arrived here through Call_Back()'s PumpMessages. Set global that will 2234 // force a cancel out of the dialog. 2235 if( pWO->bPump_In_Call_Back ) 2236 cancel_current_msgbox = true; 2237 break; 2238 case WOL_GAMEOPT_INFGO: 2239 // Host says start game right now. 2240 strcpy( szTriggerGameStartInfo, szInform ); 2241 // If we are in a modal dialog, we must have arrived here through Call_Back()'s PumpMessages. Set global that will 2242 // force a cancel out of the dialog. 2243 if( pWO->bPump_In_Call_Back ) 2244 cancel_current_msgbox = true; 2245 break; 2246 default: 2247 // debugprint( "Unhandled value of %i in ProcessInform!!!\n", opt ); 2248 //WOL_PrintMessage( *pILDisc, "Error - Unhandled value in ProcessInform!!!", WOLCOLORREMAP_LOCALMACHINEMESS ); 2249 Fatal( "Error - Unhandled value in ProcessInform!" ); 2250 break; 2251 } 2252 } 2253 // debugprint( "* END of ProcessInform: '%s'\n", szInform ); 2254 } 2255 2256 //*********************************************************************************************** 2257 bool WOL_GameSetupDialog::bParamsUnfresh() 2258 { 2259 // Returns true if game setup parameters do not match what they were last time they were sent. 2260 GAMEPARAMS GParamsNow; 2261 SetGParamsToCurrent( GParamsNow ); 2262 2263 // if( !( GParamsNow == GParamsLastSent ) ) 2264 // { 2265 // debugprint( "---- Params Unfresh: OLD...\n" ); 2266 // Debug_GlobalPacketType( GParamsLastSent.GPacket ); 2267 // debugprint( "------------------- NEW...\n" ); 2268 // Debug_GlobalPacketType( GParamsNow.GPacket ); 2269 // debugprint( "old bAftermathUnits = %i\n", GParamsLastSent.bAftermathUnits ); 2270 // debugprint( "new bAftermathUnits = %i\n", GParamsNow.bAftermathUnits ); 2271 // } 2272 2273 return !( GParamsNow == GParamsLastSent ); 2274 } 2275 2276 //*********************************************************************************************** 2277 void WOL_GameSetupDialog::SendParams() 2278 { 2279 // Host only. 2280 2281 SetGParamsToCurrent( GParamsLastSent ); 2282 2283 char szSend[ 130 + DESCRIP_MAX + 12 + 32 + 100 ]; 2284 sprintf( szSend, 2285 "%01u " 2286 "%06u " 2287 "%03u " 2288 "%s " 2289 "%u " 2290 "%s " 2291 "%01u " 2292 "%.32s " // No null-terminator on digest. There may be nothing at all inserted here. 2293 "%u " 2294 "%u " 2295 "%u " 2296 "%u " 2297 "%u " 2298 "%u " 2299 "%u " 2300 "%u " 2301 "%u " 2302 "%u " 2303 "%u " 2304 "%u " 2305 "%u " 2306 "%u " 2307 "%u " 2308 "%u " 2309 "%u " 2310 "%u " 2311 "%u " 2312 "%u " 2313 "%u " 2314 "%u " 2315 , 2316 WOL_GAMEOPT_INFPARAMS, 2317 nHostLastParamID, 2318 strlen( GParamsLastSent.GPacket.ScenarioInfo.Scenario ), 2319 GParamsLastSent.GPacket.ScenarioInfo.Scenario, 2320 GParamsLastSent.GPacket.ScenarioInfo.FileLength, 2321 GParamsLastSent.GPacket.ScenarioInfo.ShortFileName, 2322 // strlen( (char*)GParamsLastSent.GPacket.ScenarioInfo.FileDigest ), not null-terminated! 2323 GParamsLastSent.GPacket.ScenarioInfo.FileDigest[0] ? 1 : 0, 2324 GParamsLastSent.GPacket.ScenarioInfo.FileDigest, 2325 GParamsLastSent.GPacket.ScenarioInfo.OfficialScenario, 2326 GParamsLastSent.GPacket.ScenarioInfo.Credits, 2327 GParamsLastSent.GPacket.ScenarioInfo.IsBases, 2328 GParamsLastSent.GPacket.ScenarioInfo.IsTiberium, 2329 GParamsLastSent.GPacket.ScenarioInfo.IsGoodies, 2330 GParamsLastSent.GPacket.ScenarioInfo.BuildLevel, 2331 GParamsLastSent.GPacket.ScenarioInfo.UnitCount, 2332 GParamsLastSent.GPacket.ScenarioInfo.AIPlayers, 2333 GParamsLastSent.GPacket.ScenarioInfo.Seed, 2334 GParamsLastSent.GPacket.ScenarioInfo.Special.IsShadowGrow, 2335 GParamsLastSent.GPacket.ScenarioInfo.Special.IsSpeedBuild, 2336 GParamsLastSent.GPacket.ScenarioInfo.Special.IsFromInstall, 2337 GParamsLastSent.GPacket.ScenarioInfo.Special.IsCaptureTheFlag, 2338 GParamsLastSent.GPacket.ScenarioInfo.Special.IsInert, 2339 GParamsLastSent.GPacket.ScenarioInfo.Special.IsThreePoint, 2340 GParamsLastSent.GPacket.ScenarioInfo.Special.IsTGrowth, 2341 GParamsLastSent.GPacket.ScenarioInfo.Special.IsTSpread, 2342 GParamsLastSent.GPacket.ScenarioInfo.GameSpeed, 2343 GParamsLastSent.GPacket.ScenarioInfo.Version, 2344 GParamsLastSent.bAftermathUnits, // Not currently used. 2345 GParamsLastSent.bSlowUnitBuildRate, 2346 RuleINI.Get_Unique_ID() // Used to verify rules.ini files match. 2347 ); 2348 2349 pWO->SendGameOpt( szSend, NULL ); 2350 } 2351 2352 //*********************************************************************************************** 2353 bool WOL_GameSetupDialog::AcceptParams( char* szParams ) 2354 { 2355 // Reverse of SendParams() process. szParams has already been stripped of 2 bytes header. Guest only. 2356 2357 // Returns false if rules.ini doesn't match that of the host. 2358 // (Or if an error occurs due to the packet being incorrect - which happened once in test...) 2359 2360 char szDelimiter[] = " "; 2361 char* szToken; 2362 char* szRemaining; 2363 2364 szToken = strtok( szParams, szDelimiter ); 2365 nGuestLastParamID = atoi( szToken ); 2366 2367 // Read in length of following string. 2368 szToken = strtok( NULL, szDelimiter ); 2369 if( !szToken ) return false; 2370 int iLen = atoi( szToken ); 2371 // Set string pointer to start of string (length is 3 digits). 2372 szRemaining = szToken + 4; 2373 // Read in string. 2374 memcpy( Session.Options.ScenarioDescription, szRemaining, iLen ); 2375 // Null-terminate. 2376 Session.Options.ScenarioDescription[ iLen ] = 0; 2377 // Advance string pointer to next param. 2378 szRemaining += iLen + 1; 2379 2380 //debugprint( "scenario description is '%s'\n", Session.Options.ScenarioDescription ); 2381 //debugprint( "remaining: '%s'\n", szRemaining ); 2382 2383 szToken = strtok( szRemaining, szDelimiter ); 2384 if( !szToken ) return false; 2385 Session.ScenarioFileLength = atoi( szToken ); 2386 2387 szToken = strtok( NULL, szDelimiter ); 2388 if( !szToken ) return false; 2389 strcpy( Session.ScenarioFileName, szToken ); 2390 2391 // // Read in length of following string. 2392 // szToken = strtok( NULL, szDelimiter ); 2393 // iLen = atoi( szToken ); 2394 // // Set string pointer to start of string (length is 3 digits). 2395 // szRemaining = szToken + 4; 2396 // Method changed. 2397 // Check if there is a digest. 2398 szToken = strtok( NULL, szDelimiter ); 2399 if( !szToken ) return false; 2400 iLen = atoi( szToken ); // 1 or 0, indicating if there is a digest following. 2401 if( iLen ) 2402 { 2403 // // Set string pointer to start of string (previous field is 1 digit). 2404 // szRemaining = szToken + 2; 2405 // iLen = sizeof( Session.ScenarioDigest ); 2406 // // Read in string. 2407 // memcpy( Session.ScenarioDigest, szRemaining, iLen ); 2408 // // // Null-terminate. 2409 // // Session.ScenarioDigest[ iLen ] = 0; Digest has no null-terminator! 2410 // // Advance string pointer to next param. 2411 // szRemaining += iLen + 1; 2412 // There is a digest. 2413 szToken = strtok( NULL, szDelimiter ); // (Digests can't have spaces in the them.) 2414 //debugprint( "digest: '%s'\n", szToken ); 2415 if( !szToken ) return false; 2416 strncpy( Session.ScenarioDigest, szToken, sizeof( Session.ScenarioDigest ) ); 2417 } 2418 2419 szToken = strtok( NULL, szDelimiter ); 2420 if( !szToken ) return false; 2421 Session.ScenarioIsOfficial = (bool)atoi( szToken ); 2422 2423 szToken = strtok( NULL, szDelimiter ); 2424 if( !szToken ) return false; 2425 Session.Options.Credits = atoi( szToken ); 2426 2427 szToken = strtok( NULL, szDelimiter ); 2428 if( !szToken ) return false; 2429 Session.Options.Bases = atoi( szToken ); 2430 2431 szToken = strtok( NULL, szDelimiter ); 2432 if( !szToken ) return false; 2433 Session.Options.Tiberium = atoi( szToken ); 2434 2435 szToken = strtok( NULL, szDelimiter ); 2436 if( !szToken ) return false; 2437 Session.Options.Goodies = atoi( szToken ); 2438 2439 szToken = strtok( NULL, szDelimiter ); 2440 if( !szToken ) return false; 2441 BuildLevel = atoi( szToken ); 2442 2443 szToken = strtok( NULL, szDelimiter ); 2444 if( !szToken ) return false; 2445 Session.Options.UnitCount = atoi( szToken ); 2446 2447 szToken = strtok( NULL, szDelimiter ); 2448 if( !szToken ) return false; 2449 Session.Options.AIPlayers = atoi( szToken ); 2450 2451 szToken = strtok( NULL, szDelimiter ); 2452 if( !szToken ) return false; 2453 Seed = atoi( szToken ); 2454 2455 szToken = strtok( NULL, szDelimiter ); 2456 if( !szToken ) return false; 2457 Special.IsShadowGrow = ( atoi( szToken ) == 0 ) ? 0 : 1; 2458 2459 szToken = strtok( NULL, szDelimiter ); 2460 if( !szToken ) return false; 2461 Special.IsSpeedBuild = ( atoi( szToken ) == 0 ) ? 0 : 1; 2462 2463 szToken = strtok( NULL, szDelimiter ); 2464 if( !szToken ) return false; 2465 Special.IsFromInstall = ( atoi( szToken ) == 0 ) ? 0 : 1; 2466 2467 szToken = strtok( NULL, szDelimiter ); 2468 if( !szToken ) return false; 2469 Special.IsCaptureTheFlag = ( atoi( szToken ) == 0 ) ? 0 : 1; 2470 2471 szToken = strtok( NULL, szDelimiter ); 2472 if( !szToken ) return false; 2473 Special.IsInert = ( atoi( szToken ) == 0 ) ? 0 : 1; 2474 2475 szToken = strtok( NULL, szDelimiter ); 2476 if( !szToken ) return false; 2477 Special.IsThreePoint = ( atoi( szToken ) == 0 ) ? 0 : 1; 2478 2479 szToken = strtok( NULL, szDelimiter ); 2480 if( !szToken ) return false; 2481 Special.IsTGrowth = ( atoi( szToken ) == 0 ) ? 0 : 1; 2482 2483 szToken = strtok( NULL, szDelimiter ); 2484 if( !szToken ) return false; 2485 Special.IsTSpread = ( atoi( szToken ) == 0 ) ? 0 : 1; 2486 2487 szToken = strtok( NULL, szDelimiter ); 2488 if( !szToken ) return false; 2489 Options.GameSpeed = atoi( szToken ); 2490 2491 szToken = strtok( NULL, szDelimiter ); 2492 if( !szToken ) return false; 2493 // "Version" = atoi( szToken ); 2494 2495 szToken = strtok( NULL, szDelimiter ); 2496 if( !szToken ) return false; 2497 bAftermathUnits = (bool)atoi( szToken ); 2498 2499 szToken = strtok( NULL, szDelimiter ); 2500 if( !szToken ) return false; 2501 bSlowUnitBuildRate = (bool)atoi( szToken ); 2502 2503 szToken = strtok( NULL, szDelimiter ); 2504 if( !szToken ) return false; 2505 int iRulesID = atoi( szToken ); 2506 2507 szToken = strtok( NULL, szDelimiter ); 2508 // if( szToken ) 2509 // debugprint( "szToken should be NULL!!!!!!!!\n" ); 2510 2511 return ( RuleINI.Get_Unique_ID() == iRulesID ); 2512 } 2513 2514 //*********************************************************************************************** 2515 void WOL_GameSetupDialog::SetGParamsToCurrent( GAMEPARAMS& GParams ) 2516 { 2517 // Sets values in a GAMEPARAMS to the current game settings. 2518 2519 strcpy( GParams.GPacket.ScenarioInfo.Scenario, Session.Scenarios[ Session.Options.ScenarioIndex ]->Description() ); 2520 CCFileClass file( Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename() ); 2521 GParams.GPacket.ScenarioInfo.FileLength = file.Size(); 2522 strcpy( GParams.GPacket.ScenarioInfo.ShortFileName, Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename() ); 2523 // Digest is not null-terminated. 2524 strncpy( (char*)GParams.GPacket.ScenarioInfo.FileDigest, Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Digest(), sizeof( GParams.GPacket.ScenarioInfo.FileDigest ) ); 2525 GParams.GPacket.ScenarioInfo.OfficialScenario = Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Official(); 2526 GParams.GPacket.ScenarioInfo.Credits = Session.Options.Credits; 2527 GParams.GPacket.ScenarioInfo.IsBases = Session.Options.Bases; 2528 GParams.GPacket.ScenarioInfo.IsTiberium = Session.Options.Tiberium; 2529 GParams.GPacket.ScenarioInfo.IsGoodies = Session.Options.Goodies; 2530 GParams.GPacket.ScenarioInfo.BuildLevel = BuildLevel; 2531 GParams.GPacket.ScenarioInfo.UnitCount = Session.Options.UnitCount; 2532 GParams.GPacket.ScenarioInfo.AIPlayers = Session.Options.AIPlayers; 2533 GParams.GPacket.ScenarioInfo.Seed = Seed; 2534 GParams.GPacket.ScenarioInfo.Special = Special; 2535 GParams.GPacket.ScenarioInfo.GameSpeed = Options.GameSpeed; 2536 GParams.GPacket.ScenarioInfo.Version = VerNum.Get_Clipped_Version(); 2537 2538 GParams.bAftermathUnits = bAftermathUnits; 2539 GParams.bSlowUnitBuildRate = bSlowUnitBuildRate; 2540 } 2541 2542 //*********************************************************************************************** 2543 bool operator==( const GAMEPARAMS& gp1, const GAMEPARAMS& gp2 ) 2544 { 2545 return gp1.GPacket == gp2.GPacket && 2546 gp1.bAftermathUnits == gp2.bAftermathUnits && gp1.bSlowUnitBuildRate == gp2.bSlowUnitBuildRate; 2547 } 2548 2549 //*********************************************************************************************** 2550 bool operator==( const GlobalPacketType& gp1, const GlobalPacketType& gp2 ) 2551 { 2552 if( strcmp( gp1.ScenarioInfo.Scenario, gp2.ScenarioInfo.Scenario ) != 0 ) return false; 2553 if( strcmp( gp1.ScenarioInfo.ShortFileName, gp2.ScenarioInfo.ShortFileName ) != 0 ) return false; 2554 // Digest is not null-terminated... 2555 if( strncmp( (const char*)gp1.ScenarioInfo.FileDigest, (const char*)gp2.ScenarioInfo.FileDigest, sizeof( gp1.ScenarioInfo.FileDigest ) ) != 0 ) return false; 2556 2557 if( gp1.ScenarioInfo.FileLength == gp2.ScenarioInfo.FileLength && 2558 gp1.ScenarioInfo.OfficialScenario == gp2.ScenarioInfo.OfficialScenario && 2559 gp1.ScenarioInfo.Credits == gp2.ScenarioInfo.Credits && 2560 gp1.ScenarioInfo.IsBases == gp2.ScenarioInfo.IsBases && 2561 gp1.ScenarioInfo.IsTiberium == gp2.ScenarioInfo.IsTiberium && 2562 gp1.ScenarioInfo.IsGoodies == gp2.ScenarioInfo.IsGoodies && 2563 gp1.ScenarioInfo.BuildLevel == gp2.ScenarioInfo.BuildLevel && 2564 gp1.ScenarioInfo.UnitCount == gp2.ScenarioInfo.UnitCount && 2565 gp1.ScenarioInfo.AIPlayers == gp2.ScenarioInfo.AIPlayers && 2566 gp1.ScenarioInfo.Seed == gp2.ScenarioInfo.Seed && 2567 gp1.ScenarioInfo.Special.IsShadowGrow == gp2.ScenarioInfo.Special.IsShadowGrow && 2568 gp1.ScenarioInfo.Special.IsSpeedBuild == gp2.ScenarioInfo.Special.IsSpeedBuild && 2569 gp1.ScenarioInfo.Special.IsFromInstall == gp2.ScenarioInfo.Special.IsFromInstall && 2570 gp1.ScenarioInfo.Special.IsCaptureTheFlag == gp2.ScenarioInfo.Special.IsCaptureTheFlag && 2571 gp1.ScenarioInfo.Special.IsInert == gp2.ScenarioInfo.Special.IsInert && 2572 gp1.ScenarioInfo.Special.IsThreePoint == gp2.ScenarioInfo.Special.IsThreePoint && 2573 gp1.ScenarioInfo.Special.IsTGrowth == gp2.ScenarioInfo.Special.IsTGrowth && 2574 gp1.ScenarioInfo.Special.IsTSpread == gp2.ScenarioInfo.Special.IsTSpread && 2575 gp1.ScenarioInfo.GameSpeed == gp2.ScenarioInfo.GameSpeed && 2576 gp1.ScenarioInfo.Version == gp2.ScenarioInfo.Version ) 2577 return true; 2578 2579 return false; 2580 } 2581 2582 //*********************************************************************************************** 2583 /* 2584 void Debug_GlobalPacketType( const GlobalPacketType& gp1 ) // ajw debugging 2585 { 2586 if( *gp1.ScenarioInfo.Scenario ) 2587 // debugprint( "Scenario = %s\n", (char*)gp1.ScenarioInfo.Scenario ); 2588 else 2589 // debugprint( "!Scenario string is null\n" ); 2590 if( *gp1.ScenarioInfo.ShortFileName ) 2591 // debugprint( "ShortFileName = %s\n", (char*)gp1.ScenarioInfo.ShortFileName ); 2592 else 2593 // debugprint( "!ShortFileName string is null\n" ); 2594 // Remember ShortFileName is not null-terminated... 2595 if( *gp1.ScenarioInfo.FileDigest ) 2596 // debugprint( "FileDigest = %.32s\n", (char*)gp1.ScenarioInfo.FileDigest ); 2597 else 2598 // debugprint( "!FileDigest string is null\n" ); 2599 2600 // debugprint( "FileLength = %i\n" 2601 "OfficialScenario = %i\n" 2602 "Credits = %i\n" 2603 "IsBases = %i\n" 2604 "IsTiberium = %i\n" 2605 "IsGoodies = %i\n" 2606 "BuildLevel = %i\n" 2607 "UnitCount = %i\n" 2608 "AIPlayers = %i\n" 2609 "Seed = %i\n" 2610 "Special.IsShadowGrow = %i\n" 2611 "Special.IsSpeedBuild = %i\n" 2612 "Special.IsFromInstall = %i\n" 2613 "Special.IsCaptureTheFlag = %i\n" 2614 "Special.IsInert = %i\n" 2615 "Special.IsThreePoint = %i\n" 2616 "Special.IsTGrowth = %i\n" 2617 "Special.IsTSpread = %i\n" 2618 "GameSpeed = %i\n" 2619 "Version = %i\n", 2620 gp1.ScenarioInfo.FileLength, 2621 gp1.ScenarioInfo.OfficialScenario, 2622 gp1.ScenarioInfo.Credits, 2623 gp1.ScenarioInfo.IsBases, 2624 gp1.ScenarioInfo.IsTiberium, 2625 gp1.ScenarioInfo.IsGoodies, 2626 gp1.ScenarioInfo.BuildLevel, 2627 gp1.ScenarioInfo.UnitCount, 2628 gp1.ScenarioInfo.AIPlayers, 2629 gp1.ScenarioInfo.Seed, 2630 gp1.ScenarioInfo.Special.IsShadowGrow, 2631 gp1.ScenarioInfo.Special.IsSpeedBuild, 2632 gp1.ScenarioInfo.Special.IsFromInstall, 2633 gp1.ScenarioInfo.Special.IsCaptureTheFlag, 2634 gp1.ScenarioInfo.Special.IsInert, 2635 gp1.ScenarioInfo.Special.IsThreePoint, 2636 gp1.ScenarioInfo.Special.IsTGrowth, 2637 gp1.ScenarioInfo.Special.IsTSpread, 2638 gp1.ScenarioInfo.GameSpeed, 2639 gp1.ScenarioInfo.Version ); 2640 } 2641 */ 2642 2643 //*********************************************************************************************** 2644 PlayerColorType PlayerColorTypeOf( RemapControlType* pColorRemap ) 2645 { 2646 for( PlayerColorType pcolor = PCOLOR_FIRST; pcolor < PCOLOR_COUNT; pcolor++ ) 2647 { 2648 if( &ColorRemaps[ pcolor ] == pColorRemap ) 2649 return pcolor; 2650 } 2651 return PCOLOR_NONE; 2652 } 2653 2654 //*********************************************************************************************** 2655 bool WOL_GameSetupDialog::RequestPlayerColor( PlayerColorType Color ) 2656 { 2657 // Local player sends a request to the game host asking for a particular color. 2658 char szSend[ 20 ]; 2659 2660 // WOL_GAMEOPT_REQCOLOR format: 2661 // 2 WOL_GAMEOPT 2662 // 1 space 2663 // 2 color 2664 sprintf( szSend, "%02u %02u", WOL_GAMEOPT_REQCOLOR, Color ); 2665 2666 _ASSERTE( pWO->pGameHost() ); 2667 2668 return pWO->SendGameOpt( szSend, pWO->pGameHost() ); 2669 } 2670 2671 //*********************************************************************************************** 2672 bool WOL_GameSetupDialog::InformAboutPlayerColor( const char* szName, PlayerColorType Color, User* pUserPriv ) 2673 { 2674 // Game host tells guests about a player color assignment. 2675 // If pUserPriv is not null, indicates user to send message as private to. 2676 char szSend[ 20 + WOL_NAME_LEN_MAX ]; 2677 2678 // WOL_GAMEOPT_INFCOLOR format: 2679 // 2 WOL_GAMEOPT 2680 // 1 space 2681 // 2 color 2682 // 1 space 2683 // string name of player 2684 2685 if( Color == PCOLOR_NONE ) 2686 { 2687 // debugprint( "Bad Color for %s in InformAboutPlayerColor.\n", szName ); 2688 *szSend = 0; 2689 } 2690 else 2691 sprintf( szSend, "%02u %02u %s", WOL_GAMEOPT_INFCOLOR, Color, szName ); 2692 2693 return pWO->SendGameOpt( szSend, pUserPriv ); 2694 } 2695 2696 //*********************************************************************************************** 2697 bool WOL_GameSetupDialog::InformAboutPlayerHouse( const char* szName, HousesType House, User* pUserPriv ) 2698 { 2699 // Game host tells guests about a player house assignment. 2700 // If pUserPriv is not null, indicates user to send message as private to. 2701 char szSend[ 28 + WOL_NAME_LEN_MAX ]; 2702 // WOL_GAMEOPT_INFHOUSE format: 2703 // 2 WOL_GAMEOPT 2704 // 1 space 2705 // 6 param ID 2706 // 1 space 2707 // 2 house 2708 // 1 space 2709 // string name of player 2710 2711 if( House == HOUSE_NONE ) 2712 { 2713 // debugprint( "Bad House for %s in InformAboutPlayerHouse.\n", szName ); 2714 *szSend = 0; 2715 } 2716 else 2717 sprintf( szSend, "%02u %06u %02u %s", WOL_GAMEOPT_INFHOUSE, nHostLastParamID, (short)House, szName ); 2718 2719 return pWO->SendGameOpt( szSend, pUserPriv ); 2720 } 2721 2722 //*********************************************************************************************** 2723 bool WOL_GameSetupDialog::InformAboutPlayerAccept( const char* szName, User* pUserPriv ) 2724 { 2725 // Game host tells guests about player accepting game params. 2726 // If pUserPriv is not null, indicates user to send message as private to. 2727 char szSend[ 6 + WOL_NAME_LEN_MAX ]; 2728 sprintf( szSend, "%02u %s", WOL_GAMEOPT_INFACCEPT, szName ); 2729 2730 return pWO->SendGameOpt( szSend, pUserPriv ); 2731 } 2732 2733 //*********************************************************************************************** 2734 bool WOL_GameSetupDialog::InformAboutStart() 2735 { 2736 // Game host tells all guests that he wants to start the game. 2737 // Note that nHostLastParamID is involved here. We want to make sure that guest responses apply 2738 // to the latest WOL_GAMEOPT_INFSTART, and not to an earlier one we canceled out of. 2739 char szSend[ 10 ]; 2740 2741 sprintf( szSend, "%02u %06u", WOL_GAMEOPT_INFSTART, nHostLastParamID ); 2742 2743 return pWO->SendGameOpt( szSend, NULL ); 2744 } 2745 2746 //*********************************************************************************************** 2747 bool WOL_GameSetupDialog::InformAboutCancelStart() 2748 { 2749 // Game host tells all guests that he wants to start the game. 2750 // Note that nHostLastParamID is involved here. We want to make sure that guest responses apply 2751 // to the latest WOL_GAMEOPT_INFSTART, and not to an earlier one we canceled out of. 2752 // debugprint( "InformAboutCancelStart!\n" ); 2753 2754 char szSend[ 10 ]; 2755 2756 sprintf( szSend, "%02u", WOL_GAMEOPT_INFCANCELSTART ); 2757 2758 return pWO->SendGameOpt( szSend, NULL ); 2759 } 2760 2761 //*********************************************************************************************** 2762 void WOL_GameSetupDialog::OnGuestJoin( User* pUser ) 2763 { 2764 // A guest (not myself) has entered the game channel. 2765 // debugprint( "OnGuestJoin()\n" ); 2766 char* szPrint = new char[ strlen( TXT_WOL_PLAYERJOINEDGAME ) + strlen( (char*)pUser->name ) + 5 ]; 2767 sprintf( szPrint, TXT_WOL_PLAYERJOINEDGAME, (char*)pUser->name ); 2768 WOL_PrintMessage( *pILDisc, szPrint, WOLCOLORREMAP_LOCALMACHINEMESS ); 2769 delete [] szPrint; 2770 2771 ClearAllAccepts(); 2772 2773 if( bHost ) 2774 { 2775 // Send the new guest the current setup. Note that nHostLastParamID doesn't change here. 2776 2777 // Assign color to new guest. 2778 PlayerColorType Color = ColorNextAvailable(); 2779 SetPlayerColor( (char*)pUser->name, Color ); 2780 2781 // Previously, I was sending an individual color, house, and acceptedstate message for each other guest. 2782 // Though convenient code-wise, this causes the initial info to arrive at the new guest in a very slow 2783 // manner. This is because of the wonderful "anti-flood" feature of the chat server, which prevents a 2784 // series of messages from a client from being passed on faster than a certain rate. 2785 // For this reason, a new message that contains all of the info about all of the other guests has been 2786 // created (WOL_GAMEOPT_INFNEWGUESTPLAYERINFO). 2787 2788 // WOL_GAMEOPT_INFNEWGUESTPLAYERINFO format (items separated by spaces): 2789 // WOL_GAMEOPT 2790 // number of players 2791 // for each player { 2792 // length of player name string 2793 // player name 2794 // color 2795 // bool - is there a house field following? 2796 // (house) 2797 // // (removed) acceptedness - true for set player accepted, false for do nothing 2798 // } 2799 2800 // Build up a big WOL_GAMEOPT_INFNEWGUESTPLAYERINFO message. 2801 char szSend[ 500 ]; 2802 sprintf( szSend, "%02u %02u", WOL_GAMEOPT_INFNEWGUESTPLAYERINFO, pILPlayers->Count() ); 2803 // Send color and house of all players (including himself) to the new guest. 2804 for( int i = 0; i < pILPlayers->Count(); i++ ) 2805 { 2806 char szSendPiece[ 100 ]; 2807 char szPlayerName[ WOL_NAME_LEN_MAX ]; 2808 pWO->PullPlayerName_Into_From( szPlayerName, pILPlayers->Get_Item( i ) ); 2809 // InformAboutPlayerColor( szPlayerName, PlayerColorTypeOf( pILPlayers->Get_Item_Color( i ) ), pUser ); 2810 sprintf( szSendPiece, " %02u %s %02u", strlen( szPlayerName ), szPlayerName, 2811 PlayerColorTypeOf( pILPlayers->Get_Item_Color( i ) ) ); 2812 2813 if( strcmp( szPlayerName, (char*)pUser->name ) != 0 ) 2814 { 2815 HousesType House = pWO->PullPlayerHouse_From( pILPlayers->Get_Item( i ) ); 2816 if( House != HOUSE_NONE ) 2817 { 2818 // InformAboutPlayerHouse( szPlayerName, House, pUser ); 2819 char szSendHouse[ 50 ]; 2820 sprintf( szSendHouse, " 1 %02u", (short)House ); 2821 strcat( szSendPiece, szSendHouse ); 2822 } 2823 else 2824 { 2825 // Player must not have told me what house he is yet. Don't send house value. 2826 strcat( szSendPiece, " 0" ); 2827 } 2828 } 2829 else 2830 { 2831 // Player is the new guest himself. Don't send house value. 2832 strcat( szSendPiece, " 0" ); 2833 } 2834 2835 // Acceptedness must be false! No need to send. 2836 // // Send "accepted" status of player. Ignore myself, as I'm the host. 2837 // if( strcmp( szPlayerName, pWO->szMyName ) != 0 && pWO->bItemMarkedAccepted( i ) ) 2838 // strcat( szSendPiece, " 1" ); 2839 // else 2840 // strcat( szSendPiece, " 0" ); 2841 2842 strcat( szSend, szSendPiece ); 2843 } 2844 pWO->SendGameOpt( szSend, pUser ); 2845 2846 // Send everyone the color of the new guest. 2847 InformAboutPlayerColor( (char*)pUser->name, Color, NULL ); 2848 2849 // Send game params. 2850 // This is done last because it contains a param ID value, and we need to ensure that the new guest has 2851 // received everything we are sending him here before he tries to send me an accept. 2852 // If game params were sent first, he could theoretically receive them, then send an accept, even though he 2853 // has not received the WOL_GAMEOPT_INFNEWGUESTPLAYERINFO. 2854 // By doing this I avoid having to have a param ID in WOL_GAMEOPT_INFNEWGUESTPLAYERINFO, which would be hard, 2855 // since it is a private message. 2856 // For simplicity, send public. 2857 SendParams(); 2858 } 2859 } 2860 2861 //*********************************************************************************************** 2862 void WOL_GameSetupDialog::AcceptNewGuestPlayerInfo( char* szMsg ) 2863 { 2864 // Process a received WOL_GAMEOPT_INFNEWGUESTPLAYERINFO message. 2865 // szMsg has already been stripped of 2 bytes header. 2866 char szDelimiter[] = " "; 2867 char* szToken; 2868 char* szRemaining; 2869 2870 szToken = strtok( szMsg, szDelimiter ); 2871 unsigned int nPlayers = atoi( szToken ); 2872 2873 // We have to assist strtok a bit because of calls below that may also call strtok()... 2874 szRemaining = szMsg + 3; 2875 2876 for( unsigned int nPlayer = 0; nPlayer != nPlayers; ++nPlayer ) 2877 { 2878 // Read in length of following string. 2879 szToken = strtok( szRemaining, szDelimiter ); 2880 int iLen = atoi( szToken ); 2881 2882 // Set string pointer to start of string (length is 2 digits). 2883 szRemaining = szToken + 3; 2884 // Read in string. 2885 char szPlayerName[ 50 ]; 2886 memcpy( szPlayerName, szRemaining, iLen ); 2887 // Null-terminate. 2888 szPlayerName[ iLen ] = 0; 2889 2890 // Advance string pointer to next param. 2891 szRemaining += iLen + 1; 2892 2893 // Read color. 2894 szToken = strtok( szRemaining, szDelimiter ); 2895 PlayerColorType Color = (PlayerColorType)atoi( szToken ); 2896 SetPlayerColor( szPlayerName, Color ); 2897 2898 // SetPlayerColor may call strtok, so we can't use the strtok( NULL, option... in the next call. 2899 szRemaining += 3; 2900 2901 // Read whether there is a house field. 2902 szToken = strtok( szRemaining, szDelimiter ); 2903 bool bHouseField = (bool)atoi( szToken ); 2904 2905 if( bHouseField ) 2906 { 2907 // Read house. 2908 szToken = strtok( NULL, szDelimiter ); 2909 HousesType House = (HousesType)atoi( szToken ); 2910 SetPlayerHouse( szPlayerName, House ); 2911 // SetPlayerHouse may call strtok, so we can't use the strtok( NULL, option... in the next call. 2912 szRemaining += 5; 2913 } 2914 else 2915 szRemaining += 2; // Advance past "0 ". 2916 2917 // Acceptedness must be false. No need for it in message. 2918 // // Read acceptedness. 2919 // szToken = strtok( NULL, szDelimiter ); 2920 // bool bAccepted = (bool)atoi( szToken ); 2921 // if( bAccepted ) 2922 // SetPlayerAccepted( szPlayerName, true ); 2923 } 2924 2925 szToken = strtok( NULL, szDelimiter ); 2926 // if( szToken ) 2927 // debugprint( "szToken should be NULL!!!!!!!!\n" ); 2928 2929 ClearAllAccepts(); // Most likely a pointless precaution. 2930 } 2931 2932 //*********************************************************************************************** 2933 void WOL_GameSetupDialog::OnGuestLeave( User* pUser ) 2934 { 2935 // pUser is about to leave but is still in our player list. 2936 if( pUser->flags & CHAT_USER_CHANNELOWNER ) 2937 { 2938 // Host is leaving the channel. We must be a guest, and so must leave also. This will trigger exit. 2939 strcpy( szNameOfHostWhoJustBailedOnUs, (char*)pUser->name ); 2940 } 2941 else 2942 { 2943 ClearAllAccepts(); 2944 } 2945 } 2946 2947 //*********************************************************************************************** 2948 void WOL_GameSetupDialog::ClearAllAccepts() 2949 { 2950 // Clears all "player has accepted" marks. 2951 //debugprint( "ClearAllAccepts()\n" ); 2952 for( int i = 0; i < pILPlayers->Count(); i++ ) 2953 { 2954 User* pUser = (User*)pILPlayers->Get_Item_ExtraDataPtr( i ); 2955 if( pUser && !( pUser->flags & CHAT_USER_CHANNELOWNER ) ) // pUser null if this is an "early insertion" entry on startup 2956 pWO->MarkItemAccepted( i, false ); 2957 } 2958 2959 if( pToolTipHitLast && pToolTipHitLast->bShowing ) 2960 pToolTipHitLast->Unshow(); 2961 2962 if( bHost ) 2963 { 2964 pTextBtnAcceptStart->Disable(); 2965 if( bWaitingToStart ) 2966 { 2967 // Something has happened that makes starting a game not possible now. 2968 // Cancel out of waiting mode and tell guests to do the same. 2969 bWaitingToStart = false; 2970 InformAboutCancelStart(); 2971 BindControls( true ); 2972 ResetReadyToGo(); 2973 display = REDRAW_ALL; 2974 } 2975 else 2976 pTextBtnAcceptStart->Flag_To_Redraw(); 2977 } 2978 else 2979 { 2980 pTextBtnAcceptStart->Enable(); 2981 pTextBtnAcceptStart->Flag_To_Redraw(); 2982 } 2983 } 2984 2985 //*********************************************************************************************** 2986 bool WOL_GameSetupDialog::bAllGuestsAccept() 2987 { 2988 for( int i = 0; i < pILPlayers->Count(); i++ ) 2989 { 2990 if( !pWO->bItemMarkedAccepted( i ) ) 2991 return false; 2992 } 2993 return true; 2994 } 2995 2996 //*********************************************************************************************** 2997 PlayerColorType WOL_GameSetupDialog::ColorNextAvailable() 2998 { 2999 // Returns the first free player color available. 3000 3001 // (Totally unoptimized, but hardly ever called.) 3002 3003 for( int i = 0; i < MAX_MPLAYER_COLORS; i++ ) 3004 { 3005 if( pILPlayers->FindColor( &ColorRemaps[ i ] ) == -1 ) 3006 { 3007 return (PlayerColorType)i; 3008 } 3009 } 3010 // debugprint( "ColorNextAvailable is NONE!\n" ); 3011 return PCOLOR_NONE; 3012 } 3013 3014 //*********************************************************************************************** 3015 void WOL_GameSetupDialog::GuestIsReadyToPlay( const char* szName, const char* szReadyState ) 3016 { 3017 // A guest has responded to a game start request. 3018 SetPlayerReadyToGo( szName, szReadyState ); 3019 3020 if( bAllPlayersReadyToGo() ) 3021 { 3022 // debugprint( "All players ready to go.\n" ); 3023 // We can start the game. 3024 bHostSayGo = true; // Set trigger to fire function after we're out of callback. 3025 } 3026 } 3027 3028 //*********************************************************************************************** 3029 bool WOL_GameSetupDialog::bNeedScenarioDownload() 3030 { 3031 // Returns true if we don't have the scenario and it is allowable as a download. 3032 if( !bHost ) 3033 { 3034 if( Find_Local_Scenario( Session.Options.ScenarioDescription, 3035 Session.ScenarioFileName, 3036 Session.ScenarioFileLength, 3037 Session.ScenarioDigest, 3038 Session.ScenarioIsOfficial ) ) 3039 { 3040 // debugprint( "bNeedScenarioDownload() returning false.\n" ); 3041 bRequestedScenarioDownload = false; 3042 return false; 3043 } 3044 else 3045 { 3046 /* if( !Session.ScenarioIsOfficial ) 3047 { 3048 bRequestedScenarioDownload = true; 3049 return true; 3050 } 3051 else 3052 { 3053 // debugprint( "bNeedScenarioDownload fatal\n" ); 3054 Fatal( "" ); 3055 } 3056 */ 3057 // debugprint( "Requesting download\n" ); // ajw Shouldn't be happening with am maps when i have am...? 3058 bRequestedScenarioDownload = true; // All maps are downloadable. 3059 return true; 3060 } 3061 } 3062 3063 bRequestedScenarioDownload = false; 3064 return false; 3065 } 3066 3067 //*********************************************************************************************** 3068 void WOL_GameSetupDialog::HostSaysGo() 3069 { 3070 bHostSayGo = false; 3071 bHostWaitingForGoTrigger = true; 3072 3073 // debugprint( "HostSaysGo()\n" ); 3074 3075 if( !pWO->RequestGameStart() ) 3076 { 3077 // debugprint( "Call to RequestGameStart() failed.\n" ); 3078 //Fatal( "Call to RequestGameStart() failed.\n" ); 3079 pWO->bSelfDestruct = true; 3080 return; 3081 } 3082 3083 // Tell guests to start game. 3084 // debugprint( "Telling guests to start game.\n" ); 3085 3086 // Create WOL_GAMEOPT_INFGO message. 3087 // This contains the color for each player, which can change about haphazardly at the end of setup, 3088 // without causing "unacceptedness". This means that the colors everyone thinks everyone else is might not 3089 // be sync'ed. Host sets everyone straight here. 3090 char szSend[ ( WOL_NAME_LEN_MAX + 10 ) * 4 + 50 ] = ""; 3091 sprintf( szSend, "%02u", WOL_GAMEOPT_INFGO ); 3092 3093 User* pUser = pWO->pChatSink->pGameUserList; 3094 while( pUser ) 3095 { 3096 char szUser[ WOL_NAME_LEN_MAX + 10 ]; 3097 PlayerColorType Color = GetPlayerColor( (char*)pUser->name ); 3098 sprintf( szUser, " %s %02u", (char*)pUser->name, Color ); // What if player left just now, and got removed from list. Ok to continue and fail on game start? 3099 strcat( szSend, szUser ); 3100 pUser = pUser->next; 3101 } 3102 if( !pWO->SendGo( szSend ) ) 3103 { 3104 // debugprint( "Call to SendGo() failed.\n" ); 3105 //Fatal( "Call to SendGo() failed.\n" ); 3106 pWO->bSelfDestruct = true; 3107 return; 3108 } 3109 3110 /* ...Method changed. It appears that my channelleave may appear to guests before the "go" privategameopt. 3111 For this reason, I'll wait until I receive a copy of the "go" message sent to myself, before proceeding. 3112 3113 pWO->pChat->PumpMessages(); // Flush the send out. 3114 // (ajw - Note: An apparent bug in wolapi means that this pump does not necessarily flush the go gameopts. 3115 // This is ok in practice, because the host hits pumps later that will flush them.) 3116 3117 // Pretend we just received szSend, and processed it like a guest would. 3118 char* szFakeGoMessage = szSend + 3; 3119 3120 TriggerGameStart( szFakeGoMessage ); 3121 */ 3122 } 3123 3124 //*********************************************************************************************** 3125 void WOL_GameSetupDialog::TriggerGameStart( char* szGoMessage ) 3126 { 3127 // Last function before dialog is exited for game start. (Which must occur now.) 3128 // Host or guest is about to start a game using final data in szGoMessage. 3129 3130 // debugprint( "TriggerGameStart( %s )\n", szGoMessage ); 3131 3132 // If we are in a modal dialog, we must have arrived here through Call_Back()'s PumpMessages. Set global that will 3133 // force a cancel out of the dialog. 3134 if( pWO->bPump_In_Call_Back ) 3135 cancel_current_msgbox = true; 3136 3137 bHostWaitingForGoTrigger = false; 3138 bExitForGameTrigger = true; 3139 3140 // The following is based on Read_Game_Options()... 3141 3142 // WWGetPrivateProfileString("Options", "Handle", "Noname", Session.Handle, sizeof(Session.Handle), buffer); 3143 3144 strcpy( Session.Handle, pWO->szMyName ); 3145 3146 // GameName will be the host's name... 3147 strcpy( Session.GameName, pWO->pGameHostName() ); 3148 // debugprint( "Session.GameName is %s\n", Session.GameName ); 3149 3150 // gotit Session.ColorIdx = (PlayerColorType) WWGetPrivateProfileInt("Options", "Color", 0, buffer); 3151 // gotit Session.PrefColor = Session.ColorIdx; 3152 // gotit int temp = WWGetPrivateProfileInt("Options", "Side", 0, buffer); 3153 // gotit Session.House = (HousesType) ((int)HOUSE_USSR + temp); 3154 3155 // gotit Session.Options.Credits = WWGetPrivateProfileInt("Options", "Credits", 0, buffer); 3156 // gotit Session.Options.Bases = WWGetPrivateProfileInt("Options", "Bases", 0, buffer); 3157 // gotit Session.Options.Tiberium = WWGetPrivateProfileInt("Options", "Tiberium", 0, buffer); 3158 // gotit Session.Options.Goodies = WWGetPrivateProfileInt("Options", "Crates", 0, buffer); 3159 // gotit Special.IsShadowGrow = WWGetPrivateProfileInt ("Options", "Shadow", 0, buffer); 3160 // gotit BuildLevel = WWGetPrivateProfileInt("Options", "BuildLevel", 0, buffer); 3161 // gotit Session.Options.UnitCount = WWGetPrivateProfileInt("Options", "UnitCount", 0, buffer); 3162 // gotit Seed = WWGetPrivateProfileInt("Options", "Seed", 0, buffer); 3163 // gotit Special.IsCaptureTheFlag = WWGetPrivateProfileInt("Options", "CapFlag", 0, buffer); 3164 3165 // UnitBuildPenalty = WWGetPrivateProfileInt ("Options", "BuildRate", 100, buffer); 3166 if( bSlowUnitBuildRate ) 3167 UnitBuildPenalty = 250; 3168 else 3169 UnitBuildPenalty = 100; 3170 3171 //PlanetWestwoodGameID = WWGetPrivateProfileInt("Internet", "GameID", 0, buffer); 3172 PlanetWestwoodGameID = pWO->pChatSink->iGameID; 3173 3174 // Reset ChatSink's iGameID. 3175 pWO->pChatSink->iGameID = 0; 3176 3177 //PlanetWestwoodStartTime = WWGetPrivateProfileInt ("Internet", "StartTime", 0, buffer); 3178 PlanetWestwoodStartTime = time( NULL ); 3179 //WChatHWND = (HWND) WWGetPrivateProfileInt("Internet", "HWND", (int)FindWindow("OWL_Window", "Westwood Chat"), buffer); 3180 3181 // gotit Session.Options.AIPlayers = WWGetPrivateProfileInt("Options", "AI", 0, buffer); //Number of AI players 3182 if (Session.Options.AIPlayers){ 3183 Session.Options.Ghosts = 1; 3184 } 3185 3186 if (Session.Options.Tiberium) { 3187 Special.IsTGrowth = 1; 3188 Special.IsTSpread = 1; 3189 } else { 3190 Special.IsTGrowth = 0; 3191 Special.IsTSpread = 0; 3192 } 3193 3194 // The preceding was based on Read_Game_Options()... 3195 3196 3197 // Now do whatever we've left out that the horrific Net_Fake_New_Dialog() and Net_Fake_Join_Dialog() used to do for us... 3198 3199 // Set up the Session.Players list. 3200 // I think there is dependence on the local player being first, so put him there. 3201 // Else put them in order listed in the szGoMessage. 3202 // I will set "ID" based on a player's color, though it seems unclear if this is even used in the game, or what it should be. 3203 3204 Clear_Vector( &Session.Players ); 3205 3206 // Make the pILPlayers a valid list of players in the game. 3207 // Players might (incredibly rarely) have joined in the last split-second, and we only want the players listed in 3208 // the szGoMessage. To test for whether they're in this list, first wipe the colors from all list items. 3209 // Then we fill them in from info in szGoMessage. 3210 // We can ignore any list items then that have no color assigned. 3211 // Also, we'll know that the colors assigned to valid players indeed match up with what every other client has. 3212 // Remember, all other data should already be sync'ed because it has been implemented in such a way that changes would 3213 // cause "unacceptedness" of guests to occur. 3214 3215 // Clear colors in list. 3216 for( int iItem = 0; iItem < pILPlayers->Count(); iItem++ ) 3217 pILPlayers->Set_Item_Color( iItem, &ColorRemaps[ PCOLOR_NONE ] ); 3218 3219 // Parse szGoMessage to iterate through players. 3220 char szDelimiter[] = " "; 3221 char* szToken; 3222 char szPlayerName[ WOL_NAME_LEN_MAX ]; 3223 3224 szToken = strtok( szGoMessage, szDelimiter ); 3225 3226 while( szToken ) 3227 { 3228 strcpy( szPlayerName, szToken ); 3229 szToken = strtok( NULL, szDelimiter ); 3230 3231 PlayerColorType Color = (PlayerColorType)atoi( szToken ); 3232 SetPlayerColor( szPlayerName, Color ); // ajw note: inserts if not found. 3233 szToken = strtok( NULL, szDelimiter ); 3234 } 3235 3236 // Add myself to Session.Players list. 3237 _ASSERTE( pILPlayers->Find( pWO->szMyName ) != -1 ); 3238 3239 NodeNameType* pPlayerNew = new NodeNameType; 3240 strcpy( pPlayerNew->Name, pWO->szMyName ); // "Name" is 12 chars max. 3241 //pPlayerNew->Address = Session.GAddress; 3242 pPlayerNew->Player.House = GetPlayerHouse( pWO->szMyName ); 3243 //debugprint( "ME: pPlayerNew->Player.House = %i\n", pPlayerNew->Player.House ); 3244 pPlayerNew->Player.Color = GetPlayerColor( pWO->szMyName ); 3245 // This gets done later. 3246 // pPlayerNew->Player.ID = (HousesType)( pPlayerNew->Player.Color + HOUSE_MULTI1 ); 3247 3248 Session.Players.Add( pPlayerNew ); 3249 3250 char szHostName[ WOL_NAME_LEN_MAX ] = "Game host"; 3251 3252 // Add all other players to Session.Players list (if they have a valid color - see just above). 3253 // Also in this step - build the scenario download requests array (used by hosts only). 3254 memset( Session.ScenarioRequests, 0, sizeof( Session.ScenarioRequests ) ); 3255 Session.RequestCount = 0; 3256 for( iItem = 0; iItem < pILPlayers->Count(); iItem++ ) 3257 { 3258 // The following is not very efficient, but doesn't have to be. Better in this case to keep it clear and simple. 3259 pWO->PullPlayerName_Into_From( szPlayerName, pILPlayers->Get_Item( iItem ) ); 3260 if( strcmp( szPlayerName, pWO->szMyName ) != 0 && GetPlayerColor( szPlayerName ) != PCOLOR_NONE ) 3261 { 3262 // debugprint( "Creating player node '%s'\n", szPlayerName ); 3263 pPlayerNew = new NodeNameType; 3264 strcpy( pPlayerNew->Name, szPlayerName ); 3265 // Get player's IP address from pChatSink... 3266 unsigned long lAddress = ( pWO->pChatSink->GetPlayerGameIP( szPlayerName ) ); //ntohl( 3267 // debugprint( "IP address is %i, or 0x%x\n", lAddress, lAddress ); 3268 if( pWO->GameInfoCurrent.bTournament ) 3269 { 3270 // This is a tournament game, and I therefore have only one opponent: this one. 3271 // for convenience, save a copy of his IP address in case I need it later for disconnect pinging. 3272 pWO->TournamentOpponentIP = lAddress; 3273 pWO->bDisconnectPingingCompleted = false; 3274 } 3275 NetNumType net; 3276 NetNodeType node; 3277 memset( net, 0, 4 ); 3278 memset( node, 0, 6 ); 3279 memcpy( node, &lAddress, 4 ); 3280 //memcpy( node + 2, &lAddress, 4 ); 3281 pPlayerNew->Address.Set_Address( net, node ); 3282 //pPlayerNew->Address = Session.GAddress; 3283 pPlayerNew->Player.House = GetPlayerHouse( szPlayerName ); 3284 //debugprint( "Player %i: pPlayerNew->Player.House = %i\n", iItem, pPlayerNew->Player.House ); 3285 pPlayerNew->Player.Color = GetPlayerColor( szPlayerName ); 3286 // This gets done later. 3287 //pPlayerNew->Player.ID = (HousesType)( pPlayerNew->Player.Color + HOUSE_MULTI1 ); 3288 3289 Session.Players.Add( pPlayerNew ); 3290 3291 // If player is the game host, set HostAddress. This global is used when downloading scenarios; who knows where else. 3292 User* pUser = (User*)pILPlayers->Get_Item_ExtraDataPtr( iItem ); 3293 if( pUser && pUser->flags & CHAT_USER_CHANNELOWNER ) 3294 { 3295 Session.HostAddress = pPlayerNew->Address; 3296 strcpy( szHostName, (char*)pUser->name ); 3297 /* 3298 // debugging 3299 NetNumType netxxx; 3300 NetNodeType nodexxx; 3301 Session.HostAddress.Get_Address( netxxx, nodexxx ); 3302 // debugprint( "Host, ip %i.%i.%i.%i.%i.%i\n", nodexxx[0], nodexxx[1], nodexxx[2], nodexxx[3], nodexxx[4], nodexxx[5] ); 3303 */ 3304 } 3305 /* 3306 else 3307 { 3308 NetNumType netxxx; 3309 NetNodeType nodexxx; 3310 pPlayerNew->Address.Get_Address( netxxx, nodexxx ); 3311 // debugprint( "Player ip %i.%i.%i.%i.%i.%i\n", nodexxx[0], nodexxx[1], nodexxx[2], nodexxx[3], nodexxx[4], nodexxx[5] ); 3312 } 3313 */ 3314 3315 if( bHost && pWO->bItemMarkedNeedScenario( iItem ) ) 3316 { 3317 // debugprint( "%s has requested scenario download.\n", szPlayerName ); 3318 Session.ScenarioRequests[ Session.RequestCount++ ] = Session.Players.Count() - 1; 3319 } 3320 } 3321 // else 3322 // debugprint( "%s excluded from Session.Players\n", szPlayerName ); 3323 } 3324 3325 3326 // From Init... 3327 // debugprint( "About to call Open_Socket().\n"); 3328 PacketTransport->Open_Socket( 0 ); 3329 3330 // debugprint( "RA95 - About to call Start_Listening.\n" ); 3331 PacketTransport->Start_Listening(); 3332 3333 /* 3334 ** Flush out any pending packets from a previous game. 3335 */ 3336 PacketTransport->Discard_In_Buffers(); 3337 PacketTransport->Discard_Out_Buffers(); 3338 3339 WWDebugString ("RA95 - About to call Init_Network.\n"); 3340 Init_Network(); 3341 3342 Ipx.Set_Timing ( 30, // retry 2 times per second 3343 -1, // ignore max retries 3344 600); // give up after 10 seconds 3345 3346 // debugprint( "Session.ScenarioFileName is %s.\n", Session.ScenarioFileName ); 3347 3348 3349 /* 3350 ** Read the scenario name from the .INI and try to match it with a scenario file in our list. 3351 */ 3352 // gotit WWGetPrivateProfileString("Options", "Scenario", "SCM01EA.INI", 3353 // Session.Options.ScenarioDescription, 3354 // sizeof (Session.Options.ScenarioDescription), 3355 // buffer); 3356 //WWDebugString ("RA95I - Scenario is "); 3357 //WWDebugString (Session.Options.ScenarioDescription); 3358 //WWDebugString ("\n"); 3359 3360 if( !bHost ) // Else ScenarioIndex is already set. 3361 { 3362 if( bRequestedScenarioDownload ) 3363 { 3364 Session.Options.ScenarioIndex = 1; 3365 if( bSpecialAftermathScenario( Session.Options.ScenarioDescription ) ) 3366 { 3367 // Shouldn't ever happen. We should never have the opportunity to ask for one of these maps to be downloaded. 3368 bExitForGameTrigger = false; 3369 *szTriggerGameStartInfo = 0; 3370 // Trigger the "our host just left the channel" code... 3371 strcpy( szNameOfHostWhoJustBailedOnUs, szHostName ); 3372 return; 3373 } 3374 // Wait for download from game host. 3375 //debugprint( "Wait for download from game host.\n" ); 3376 if( !Get_Scenario_File_From_Host( Session.ScenarioFileName, 1 ) ) 3377 { 3378 // debugprint( "Get_Scenario_File_From_Host failed!\n" ); 3379 bExitForGameTrigger = false; 3380 *szTriggerGameStartInfo = 0; 3381 // Trigger the "our host just left the channel" code... 3382 strcpy( szNameOfHostWhoJustBailedOnUs, szHostName ); 3383 return; 3384 } 3385 Scen.Scenario = Session.Options.ScenarioIndex; 3386 // debugprint( "Scen.Scenario = %i\n", Scen.Scenario ); 3387 strcpy( Scen.ScenarioName, Session.ScenarioFileName ); 3388 // debugprint( "Scen.ScenarioName = %s\n", Scen.ScenarioName ); 3389 } 3390 else 3391 { 3392 // Match ScenarioDescription to a ScenarioIndex. 3393 /* This is how the same code existed previously. Insufficient because Description may match on many scenarios. 3394 Session.Options.ScenarioIndex = -1; 3395 for (int i = 0; i < Session.Scenarios.Count(); i++) { 3396 if (!strcmp (Session.Scenarios[i]->Description(), Session.Options.ScenarioDescription) ){ 3397 Session.Options.ScenarioIndex = i; 3398 break; 3399 } 3400 } 3401 */ 3402 // (We have already done the lookup, in Find_Local_Scenario, above.) 3403 Session.Options.ScenarioIndex = ScenarioIndex_From_Filename( Session.ScenarioFileName ); 3404 _ASSERTE( Session.Options.ScenarioIndex != -1 ); 3405 Scen.Scenario = Session.Options.ScenarioIndex; 3406 // debugprint( "Scen.Scenario = %i\n", Scen.Scenario ); 3407 strcpy( Scen.ScenarioName, Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename() ); 3408 // debugprint( "Scen.ScenarioName = %s\n", Scen.ScenarioName ); 3409 } 3410 } 3411 else // bHost 3412 { 3413 Scen.Scenario = Session.Options.ScenarioIndex; 3414 // debugprint( "Scen.Scenario = %i\n", Scen.Scenario ); 3415 strcpy( Scen.ScenarioName, Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename() ); 3416 // debugprint( "Scen.ScenarioName = %s\n", Scen.ScenarioName ); 3417 strcpy( Session.Options.ScenarioDescription, (char*)Session.Scenarios[ Session.Options.ScenarioIndex ]->Description() ); 3418 } 3419 3420 Options.GameSpeed = 0; 3421 3422 //Session.MaxAhead = WChatMaxAhead = WWGetPrivateProfileInt("Timing", "MaxAhead", 9, buffer); 3423 //Session.FrameSendRate = WChatSendRate = WWGetPrivateProfileInt("Timing", "SendRate", 3, buffer); 3424 Session.MaxAhead = 15; //9; 3425 //Session.FrameSendRate = 5; //3; 3426 Session.FrameSendRate = 3; //3; 3427 3428 // This is from NETDLG processing... 3429 Session.NumPlayers = Session.Players.Count(); 3430 3431 pWO->GameInfoCurrent.iPlayerCount = Session.Players.Count(); 3432 3433 Ipx.Set_Timing (25, (unsigned long) -1, 1000); 3434 3435 3436 if( bHost ) 3437 { 3438 if( Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Official() ) 3439 { 3440 if( !Force_Scenario_Available( Scen.ScenarioName ) ) 3441 { 3442 bExitForGameTrigger = false; 3443 *szTriggerGameStartInfo = 0; 3444 pWO->bSelfDestruct = true; 3445 return; 3446 } 3447 } 3448 if( Session.RequestCount ) 3449 { 3450 // Send the scenario to any guests that requested a download. 3451 //debugprint( "Send the scenario to any guests that requested a download.\n" ); 3452 Send_Remote_File( Scen.ScenarioName, 1 ); 3453 } 3454 } 3455 3456 Session.CommProtocol = COMM_PROTOCOL_MULTI_E_COMP; 3457 Ipx.Set_Timing (30, (unsigned long) -1, 600); 3458 3459 pWO->bEnableNewAftermathUnits = bAftermathUnits; 3460 bAftermathMultiplayer = bAftermathUnits; 3461 3462 *pWO->szExternalPager = 0; 3463 3464 pWO->bDoingDisconnectPinging = false; // Pointlessly making sure. 3465 3466 *szTriggerGameStartInfo = 0; // This was set in order to trigger my coming here. 3467 } 3468 3469 #endif 3470 3471 #include <string.h> 3472 //*********************************************************************************************** 3473 bool bSpecialAftermathScenario( const char* szScenarioDescription ) 3474 { 3475 // Returns true if szScenarioDescription matches one of the descriptions for Aftermath multiplayer 3476 // scenarios that have special Aftermath-only units *embedded* within them. 3477 if( strcmp( szScenarioDescription, "Booby Traps (Mega 8 players)" ) == 0 || 3478 strcmp( szScenarioDescription, "Central Conflict Extreme (Mega 8 players)" ) == 0 || 3479 strcmp( szScenarioDescription, "Circles of Death (Mega 8 players)" ) == 0 || 3480 strcmp( szScenarioDescription, "Holy Grounds (Mega 8 players)" ) == 0 || 3481 strcmp( szScenarioDescription, "Island Wars Extreme (Mega 8 players)" ) == 0 || 3482 strcmp( szScenarioDescription, "King of the Hills Extreme (Mega 8 players)" ) == 0 || 3483 strcmp( szScenarioDescription, "The Hills Have Eyes (Mega 8 players)" ) == 0 ) 3484 return true; 3485 return false; 3486 } 3487 3488 //*********************************************************************************************** 3489 int ScenarioIndex_From_Filename( const char* szScenarioFilename ) 3490 { 3491 #if (0)//PG 3492 // Returns the scenario index that matches the scenario filename, or -1 if no match found. 3493 for( int index = 0; index < Session.Scenarios.Count(); index++ ) 3494 { 3495 if( _stricmp( szScenarioFilename, Session.Scenarios[index]->Get_Filename() ) == 0 ) 3496 return index; 3497 } 3498 #endif 3499 return -1; 3500 }