CnC_Remastered_Collection

Command and Conquer: Red Alert
Log | Files | Refs | README | LICENSE

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 }