WOLAPIOB.CPP (116890B)
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 #ifdef WOLAPI_INTEGRATION 17 18 // WolapiOb.cpp - Implementation of class WolapiObject. 19 // ajw 07/10/98 20 21 #include "WolapiOb.h" 22 #include "RAWolapi.h" 23 #include "WolStrng.h" 24 #include "SEditDlg.h" 25 #include "ToolTip.h" 26 #include "Wol_GSup.h" 27 28 #include "WolDebug.h" 29 30 extern void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, PlayerColorType iColorRemap = PCOLOR_NONE ); 31 extern void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, RemapControlType* pColorRemap ); 32 void HostNameFromGameChannelName( char* szNameToSet, const char* szChannelName ); 33 34 bool WOL_Options_Dialog( WolapiObject* pWO, bool bCalledFromGame ); 35 36 extern bool cancel_current_msgbox; 37 38 const char* Game_Registry_Key(); 39 40 //*********************************************************************************************** 41 WolapiObject::WolapiObject() : pChat( NULL ), pDownload( NULL ), pChatSink( NULL ), pDownloadSink( NULL ), 42 dwChatAdvise( 0 ), dwDownloadAdvise( 0 ), pILChat( NULL ), pILUsers( NULL ), 43 CurrentLevel( WOL_LEVEL_TOP ), bChannelOwner( false ), GameTypeInfos( NULL ), 44 nGameTypeInfos( 0 ), bChatShownBefore( false ), 45 pILPlayers( NULL ), pChatSaveList( NULL ), iLobbyReturnAfterGame( -1 ), iLobbyLast( -1 ), 46 bFindEnabled( true ), 47 bPageEnabled( true ), 48 bLangFilter( true ), 49 bAllGamesShown( true ), 50 pGSupDlg( NULL ), 51 pShpDiscon( NULL ), 52 pShpLeave( NULL ), 53 pShpRefresh( NULL ), 54 pShpSquelch( NULL ), 55 pShpBan( NULL ), 56 pShpKick( NULL ), 57 pShpFindpage( NULL ), 58 pShpOptions( NULL ), 59 pShpLadder( NULL ), 60 pShpHelp( NULL ), 61 pShpBtnDiscon( NULL ), 62 pShpBtnLeave( NULL ), 63 pShpBtnRefresh( NULL ), 64 pShpBtnSquelch( NULL ), 65 pShpBtnBan( NULL ), 66 pShpBtnKick( NULL ), 67 pShpBtnFindpage( NULL ), 68 pShpBtnOptions( NULL ), 69 pShpBtnLadder( NULL ), 70 pShpBtnHelp( NULL ), 71 bReturningAfterGame( false ), 72 pTTipDiscon( NULL ), 73 pTTipLeave( NULL ), 74 pTTipRefresh( NULL ), 75 pTTipSquelch( NULL ), 76 pTTipBan( NULL ), 77 pTTipKick( NULL ), 78 pTTipFindpage( NULL ), 79 pTTipOptions( NULL ), 80 pTTipLadder( NULL ), 81 pTTipHelp( NULL ), 82 bMyRecordUpdated( false ), 83 bChannelListTitleUpdated( false ), 84 bInGame( false ), 85 pStaticUsers( NULL ), 86 bPump_In_Call_Back( false ), 87 bFreezeExternalPager( false ), 88 bDoingDisconnectPinging( false ), 89 bSelfDestruct( false ), 90 bEggSounds( true ), 91 bEgg8Player( false ), 92 bShowRankRA( true ), 93 bShowRankUpdated( false ) 94 { 95 *szMyName = 0; 96 *szMyRecord = 0; 97 *szMyRecordAM = 0; 98 *szChannelListTitle = 0; 99 *szChannelNameCurrent = 0; 100 *szChannelReturnOnGameEnterFail = 0; 101 dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; 102 dwTimeNextChannelUpdate = 0; 103 *szLadderServerHost = 0; 104 *szGameResServerHost1 = 0; 105 *szGameResServerHost2 = 0; 106 107 strcpy( DibIconInfos[ DIBICON_OWNER ].szFile, "dib_own.bmp" ); 108 strcpy( DibIconInfos[ DIBICON_SQUELCH ].szFile, "dib_sqel.bmp" ); 109 strcpy( DibIconInfos[ DIBICON_LATENCY ].szFile, "latency.bmp" ); 110 strcpy( DibIconInfos[ DIBICON_ACCEPT ].szFile, "dib_acpt.bmp" ); 111 strcpy( DibIconInfos[ DIBICON_NOTACCEPT ].szFile, "dib_acp2.bmp" ); 112 strcpy( DibIconInfos[ DIBICON_USER ].szFile, "dib_user.bmp" ); 113 strcpy( DibIconInfos[ DIBICON_PRIVATE ].szFile, "privgame.bmp" ); 114 strcpy( DibIconInfos[ DIBICON_TOURNAMENT ].szFile, "tourgame.bmp" ); 115 strcpy( DibIconInfos[ DIBICON_VOICE ].szFile, "voice.bmp" ); 116 for( int i = 0; i != NUMDIBICONS; i++ ) 117 { 118 DibIconInfos[ i ].hDIB = 0; 119 DibIconInfos[ i ].pDIB = NULL; 120 } 121 122 // Determine name of executable of user's web browser. 123 // This seems to be the "correct" way to do this, but it's bloody stupid. 124 *szWebBrowser = 0; 125 char szFile[] = "\\name_unlikely_to_conflict_77.html"; //"\\it is really dumb for me to have to create this file.html"; 126 HANDLE hFile = ::CreateFile( szFile, GENERIC_WRITE, NULL, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); 127 if( hFile != INVALID_HANDLE_VALUE ) 128 { 129 ::CloseHandle( hFile ); 130 HINSTANCE hExecutable = ::FindExecutable( szFile, "", szWebBrowser ); 131 // if( (int)hExecutable <= 32 ) 132 // { 133 // debugprint( "error %i getting browser\n", hExecutable ); 134 // } 135 // else 136 // debugprint( "szWebBrowser is %s\n", szWebBrowser ); 137 ::DeleteFile( szFile ); 138 } 139 } 140 141 //*********************************************************************************************** 142 WolapiObject::~WolapiObject() 143 { 144 DeleteSavedChat(); 145 146 if( nGameTypeInfos ) 147 { 148 // Delete DIBs that were created, and the wol_gametypeinfos themselves 149 for( unsigned int n = 0; n != nGameTypeInfos; n++ ) 150 { 151 if( GameTypeInfos[ n ].hDIB ) 152 { 153 GlobalUnlock( GameTypeInfos[ n ].hDIB ); // Release pDIB. 154 DestroyDIB( GameTypeInfos[ n ].hDIB ); // Destroy mem alloc'ed for dib bits data. 155 } 156 } 157 delete [] GameTypeInfos; 158 GameTypeInfos = NULL; 159 } 160 161 for( int i = 0; i != NUMDIBICONS; i++ ) 162 { 163 if( DibIconInfos[ i ].pDIB ) 164 { 165 GlobalUnlock( DibIconInfos[ i ].hDIB ); 166 DestroyDIB( DibIconInfos[ i ].hDIB ); 167 } 168 } 169 170 if( pChatSink ) 171 UnsetupCOMStuff(); 172 173 // Delete buttons, etc., shared by dialogs. 174 delete pShpBtnDiscon; 175 delete pShpBtnLeave; 176 delete pShpBtnRefresh; 177 delete pShpBtnSquelch; 178 delete pShpBtnBan; 179 delete pShpBtnKick; 180 delete pShpBtnFindpage; 181 delete pShpBtnOptions; 182 delete pShpBtnLadder; 183 delete pShpBtnHelp; 184 // Delete shared tooltips. 185 delete pTTipDiscon; 186 delete pTTipLeave; 187 delete pTTipRefresh; 188 delete pTTipSquelch; 189 delete pTTipBan; 190 delete pTTipKick; 191 delete pTTipFindpage; 192 delete pTTipOptions; 193 delete pTTipLadder; 194 delete pTTipHelp; 195 } 196 197 //*********************************************************************************************** 198 bool WolapiObject::bSetupCOMStuff() 199 { 200 // debugprint( "++++Begin WolapiObject::bSetupCOMStuff\n" ); 201 202 HRESULT hRes; 203 204 // Grab IChat, INetUtil, set up "sinks". 205 // debugprint( "CoCreateInstance\n" ); 206 CoCreateInstance( CLSID_Chat, NULL, CLSCTX_INPROC_SERVER, IID_IChat, (void**)&pChat ); 207 if( !pChat ) 208 return false; // Severe, essentially fatal. 209 CoCreateInstance( CLSID_NetUtil, NULL, CLSCTX_INPROC_SERVER, IID_INetUtil, (void**)&pNetUtil ); 210 if( !pNetUtil ) 211 return false; // Severe, essentially fatal. 212 213 // Set up RAChatEventSink. 214 pChatSink = new RAChatEventSink( this ); 215 pChatSink->AddRef(); 216 // Set up RANetUtilEventSink. 217 pNetUtilSink = new RANetUtilEventSink( this ); 218 pNetUtilSink->AddRef(); 219 220 // If we could use ATL stuff, this would be different. (We'd use AtlAdvise.) 221 222 IConnectionPoint* pConnectionPoint = NULL; 223 IConnectionPointContainer* pContainer = NULL; 224 225 // Get a connection point from the chat class for the chatsink. 226 // debugprint( "QueryInterface\n" ); 227 hRes = pChat->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); 228 if( !SUCCEEDED(hRes) ) 229 return false; // Severe, essentially fatal. 230 // debugprint( "FindConnectionPoint\n" ); 231 hRes = pContainer->FindConnectionPoint( IID_IChatEvent, &pConnectionPoint ); 232 if( !SUCCEEDED(hRes) ) 233 return false; // Severe, essentially fatal. 234 // Connect chat to chatsink. 235 // debugprint( "Advise. pChatSink = %i, pConnectionPoint = %i\n", pChatSink, pConnectionPoint ); 236 hRes = pConnectionPoint->Advise( (IChatEvent*)pChatSink, &dwChatAdvise ); 237 if( !SUCCEEDED(hRes) ) 238 return false; // Severe, essentially fatal. 239 240 pContainer->Release(); 241 pConnectionPoint->Release(); 242 243 pConnectionPoint = NULL; 244 pContainer = NULL; 245 // Get a connection point from the netutil class for the netutilsink. 246 // debugprint( "QueryInterface\n" ); 247 hRes = pNetUtil->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); 248 if( !SUCCEEDED(hRes) ) 249 return false; // Severe, essentially fatal. 250 // debugprint( "FindConnectionPoint\n" ); 251 hRes = pContainer->FindConnectionPoint( IID_INetUtilEvent, &pConnectionPoint ); 252 if( !SUCCEEDED(hRes) ) 253 return false; // Severe, essentially fatal. 254 // Connect netutil to netutilsink. 255 // debugprint( "Advise. pChatSink = %i, pConnectionPoint = %i\n", pChatSink, pConnectionPoint ); 256 hRes = pConnectionPoint->Advise( (INetUtilEvent*)pNetUtilSink, &dwNetUtilAdvise ); 257 if( !SUCCEEDED(hRes) ) 258 return false; // Severe, essentially fatal. 259 260 pContainer->Release(); 261 pConnectionPoint->Release(); 262 263 // debugprint( "++++End WolapiObject::bSetupCOMStuff\n" ); 264 return true; 265 } 266 267 //*********************************************************************************************** 268 void WolapiObject::UnsetupCOMStuff() 269 { 270 // debugprint( "----Begin WolapiObject::UnsetupCOMStuff\n" ); 271 272 HRESULT hRes; 273 274 // If we could use ATL stuff, this would be different. (We'd use AtlUnadvise.) 275 276 // Unsetup RAChatEventSink and RANetUtilEventSink, release IChat. 277 IConnectionPoint* pConnectionPoint = NULL; 278 IConnectionPointContainer* pContainer = NULL; 279 280 // debugprint( "QueryInterface\n" ); 281 hRes = pChat->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); 282 _ASSERTE(SUCCEEDED(hRes)); 283 // debugprint( "FindConnectionPoint\n" ); 284 hRes = pContainer->FindConnectionPoint( IID_IChatEvent, &pConnectionPoint ); 285 _ASSERTE(SUCCEEDED(hRes)); 286 // debugprint( "Unadvise: %i\n", dwChatAdvise ); 287 pConnectionPoint->Unadvise( dwChatAdvise ); 288 289 pContainer->Release(); 290 pConnectionPoint->Release(); 291 292 pConnectionPoint = NULL; 293 pContainer = NULL; 294 // debugprint( "QueryInterface\n" ); 295 hRes = pNetUtil->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); 296 _ASSERTE(SUCCEEDED(hRes)); 297 // debugprint( "FindConnectionPoint\n" ); 298 hRes = pContainer->FindConnectionPoint( IID_INetUtilEvent, &pConnectionPoint ); 299 _ASSERTE(SUCCEEDED(hRes)); 300 // debugprint( "Unadvise: %i\n", dwNetUtilAdvise ); 301 pConnectionPoint->Unadvise( dwNetUtilAdvise ); 302 303 pContainer->Release(); 304 pConnectionPoint->Release(); 305 306 // debugprint( "pChat->Release\n" ); 307 pChat->Release(); 308 309 // debugprint( "pChatSink->Release\n" ); 310 pChatSink->Release(); // This results in pChatSink deleting itself for us. 311 pChatSink = NULL; 312 313 // debugprint( "pNetUtil->Release\n" ); 314 pNetUtil->Release(); 315 316 // debugprint( "pNetUtilSink->Release\n" ); 317 pNetUtilSink->Release(); // This results in pChatSink deleting itself for us. 318 pNetUtilSink = NULL; 319 320 // debugprint( "----End WolapiObject::UnsetupCOMStuff\n" ); 321 } 322 323 //*********************************************************************************************** 324 void WolapiObject::LinkToChatDlg( IconListClass* pILChat, IconListClass* pILChannels, IconListClass* pILUsers, StaticButtonClass* pStaticUsers ) 325 { 326 // Called to initialize this before the chat dialog is shown. 327 328 // Set pointers to lists in dialog. 329 this->pILChat = pILChat; 330 this->pILChannels = pILChannels; 331 this->pILUsers = pILUsers; 332 333 this->pStaticUsers = pStaticUsers; 334 } 335 336 //*********************************************************************************************** 337 void WolapiObject::ClearListPtrs() 338 { 339 // Called to clear list pointers when chat or gamesetup dialog goes away, for safety. 340 pILChat = NULL; 341 pILChannels = NULL; 342 pILUsers = NULL; 343 344 pILPlayers = NULL; 345 346 pStaticUsers = NULL; 347 } 348 349 //*********************************************************************************************** 350 void WolapiObject::LinkToGameDlg( IconListClass* pILDisc, IconListClass* pILPlayers ) 351 { 352 // Called to initialize this before the gamesetup dialog is shown. 353 354 // Set pointers to lists in dialog. 355 pILChat = pILDisc; 356 this->pILPlayers = pILPlayers; 357 } 358 359 //*********************************************************************************************** 360 void WolapiObject::PrepareButtonsAndIcons() 361 { 362 // Load shapes for buttons. Store images in this order: up, down, disabled. 363 //pShpDiscon = LoadShpFile( "discon.shp" ); etc 364 pShpDiscon = (char*)MFCD::Retrieve( "discon.shp" ); 365 pShpLeave = (char*)MFCD::Retrieve( "leave.shp" ); 366 pShpRefresh = (char*)MFCD::Retrieve( "refresh.shp" ); 367 pShpSquelch = (char*)MFCD::Retrieve( "squelch.shp" ); 368 pShpBan = (char*)MFCD::Retrieve( "ban.shp" ); 369 pShpKick = (char*)MFCD::Retrieve( "kick.shp" ); 370 pShpFindpage = (char*)MFCD::Retrieve( "findpage.shp" ); 371 pShpOptions = (char*)MFCD::Retrieve( "ops.shp" ); 372 pShpLadder = (char*)MFCD::Retrieve( "ladder.shp" ); 373 pShpHelp = (char*)MFCD::Retrieve( "help.shp" ); 374 375 // Set up standard wol buttons, used by both main dialogs. Note hardcoded ID values: must match values in dialog. 376 int iWolButtons_x = 34; 377 int iWolButtons_y = 20; 378 int iWolButtons_dx = 53; 379 int xWolButton = iWolButtons_x; 380 int xTTip = 10; // Offset for tooltip. 381 int yTTip = - 5; // Offset for tooltip. 382 pShpBtnDiscon = new ShapeButtonClass( 100, pShpDiscon, xWolButton, iWolButtons_y ); 383 pTTipDiscon = new ToolTipClass( pShpBtnDiscon, TXT_WOL_TTIP_DISCON, xWolButton + xTTip, iWolButtons_y + yTTip ); 384 xWolButton += iWolButtons_dx; 385 pShpBtnLeave = new ShapeButtonClass( 101, pShpLeave, xWolButton, iWolButtons_y ); 386 pTTipLeave = new ToolTipClass( pShpBtnLeave, TXT_WOL_TTIP_LEAVE, xWolButton + xTTip, iWolButtons_y + yTTip ); 387 xWolButton += iWolButtons_dx; 388 pShpBtnRefresh = new ShapeButtonClass( 102, pShpRefresh, xWolButton, iWolButtons_y ); 389 pTTipRefresh = new ToolTipClass( pShpBtnRefresh, TXT_WOL_TTIP_REFRESH, xWolButton + xTTip, iWolButtons_y + yTTip ); 390 xWolButton += iWolButtons_dx; 391 pShpBtnSquelch = new ShapeButtonClass( 103, pShpSquelch, xWolButton, iWolButtons_y ); 392 pTTipSquelch = new ToolTipClass( pShpBtnSquelch, TXT_WOL_TTIP_SQUELCH, xWolButton + xTTip, iWolButtons_y + yTTip ); 393 xWolButton += iWolButtons_dx; 394 pShpBtnBan = new ShapeButtonClass( 104, pShpBan, xWolButton, iWolButtons_y ); 395 pTTipBan = new ToolTipClass( pShpBtnBan, TXT_WOL_TTIP_BAN, xWolButton + xTTip, iWolButtons_y + yTTip ); 396 xWolButton += iWolButtons_dx; 397 pShpBtnKick = new ShapeButtonClass( 105, pShpKick, xWolButton, iWolButtons_y ); 398 pTTipKick = new ToolTipClass( pShpBtnKick, TXT_WOL_TTIP_KICK, xWolButton + xTTip, iWolButtons_y + yTTip ); 399 xWolButton += iWolButtons_dx; 400 pShpBtnFindpage = new ShapeButtonClass( 106, pShpFindpage, xWolButton, iWolButtons_y ); 401 pTTipFindpage = new ToolTipClass( pShpBtnFindpage, TXT_WOL_TTIP_FINDPAGE, xWolButton + xTTip, iWolButtons_y + yTTip ); 402 xWolButton = 452; 403 pShpBtnOptions = new ShapeButtonClass( 107, pShpOptions, xWolButton, iWolButtons_y ); 404 pTTipOptions = new ToolTipClass( pShpBtnOptions, TXT_WOL_TTIP_OPTIONS, xWolButton + xTTip, iWolButtons_y + yTTip, true ); 405 xWolButton += iWolButtons_dx; 406 pShpBtnLadder = new ShapeButtonClass( 108, pShpLadder, xWolButton, iWolButtons_y ); 407 pTTipLadder = new ToolTipClass( pShpBtnLadder, TXT_WOL_TTIP_LADDER, xWolButton + xTTip, iWolButtons_y + yTTip, true ); 408 xWolButton += iWolButtons_dx; 409 pShpBtnHelp = new ShapeButtonClass( 109, pShpHelp, xWolButton, iWolButtons_y ); 410 pTTipHelp = new ToolTipClass( pShpBtnHelp, TXT_WOL_TTIP_HELP, xWolButton + xTTip, iWolButtons_y + yTTip, true ); 411 412 // Load standard hard-coded icons. 413 HPALETTE hPal = GetCurrentScreenPalette(); 414 415 int iFileLength; 416 const char* pFileData; 417 for( int iDibIcon = 0; iDibIcon != NUMDIBICONS; iDibIcon++ ) 418 { 419 //pFileData = LoadFileIntoMemory( DibIconInfos[ iDibIcon ].szFile, iFileLength ); 420 pFileData = (char*)MFCD::Retrieve( DibIconInfos[ iDibIcon ].szFile ); 421 if( pFileData ) 422 { 423 CCFileClass ccfileDib( DibIconInfos[ iDibIcon ].szFile ); 424 iFileLength = ccfileDib.Size(); 425 //debugprint( "Loaded %s, size is %i.\n", DibIconInfos[ iDibIcon ].szFile, iFileLength ); 426 DibIconInfos[ iDibIcon ].hDIB = LoadDIB_FromMemory( (unsigned char*)pFileData, iFileLength ); 427 if( DibIconInfos[ iDibIcon ].hDIB ) 428 { 429 DibIconInfos[ iDibIcon ].pDIB = (char*)GlobalLock( DibIconInfos[ iDibIcon ].hDIB ); 430 RemapDIBToPalette( hPal, DibIconInfos[ iDibIcon ].pDIB ); 431 } 432 // else 433 // debugprint( "LoadDIB_FromMemory failed!\n" ); 434 } 435 // else 436 // debugprint( "Couldn't find %s in mix.\n", DibIconInfos[ iDibIcon ].szFile ); 437 } 438 439 if( DibIconInfos[ DIBICON_LATENCY ].pDIB ) 440 fLatencyToIconWidth = (float)DIBWidth( DibIconInfos[ DIBICON_LATENCY ].pDIB ) / 1000; 441 else 442 fLatencyToIconWidth = 0; 443 444 // All of the following is for the list of game icons... 445 446 // Load game icons from the wol api. 447 LPCSTR szSkus; 448 if( pChat->GetGametypeList( &szSkus ) == S_OK ) 449 { 450 // Make two copies of szSkus because strtok insists on messing with them. 451 char* szSkus1 = new char[ strlen( szSkus ) + 1 ]; 452 char* szSkus2 = new char[ strlen( szSkus ) + 1 ]; 453 strcpy( szSkus1, szSkus ); 454 strcpy( szSkus2, szSkus ); 455 // Count commas. 456 char seps[] = ","; 457 char* token; 458 nGameTypeInfos = 0; 459 token = strtok( szSkus1, seps ); 460 while( token != NULL ) 461 { 462 nGameTypeInfos++; 463 token = strtok( NULL, seps ); 464 } 465 // There are actually 2 additional game types available in wolapi - 0 (ws icon) and -1 (wwonline icon). 466 nGameTypeInfos += 2; 467 // Create structs to hold infos. 468 // debugprint( "Creating %i gametypeinfos\n", nGameTypeInfos ); 469 GameTypeInfos = new WOL_GAMETYPEINFO[ nGameTypeInfos ]; 470 int iMyIndex = 0; 471 token = strtok( szSkus2, seps ); 472 while( token != NULL ) 473 { 474 GetGameTypeInfo( atoi( token ), GameTypeInfos[ iMyIndex ], hPal ); 475 token = strtok( NULL, seps ); 476 iMyIndex++; 477 } 478 // Get the two extra game type infos... 479 GetGameTypeInfo( -1, GameTypeInfos[ iMyIndex++ ], hPal ); 480 GetGameTypeInfo( 0, GameTypeInfos[ iMyIndex++ ], hPal ); 481 } 482 // else 483 // debugprint( "GetGametypeList() failed.\n" ); 484 485 // Load icons that we'll need to represent Red Alert GameKinds. 486 // These are available in wolapi at their old sku number locations. 487 GetGameTypeInfo( 2, OldRAGameTypeInfos[ 0 ], hPal ); // RA 488 GetGameTypeInfo( 3, OldRAGameTypeInfos[ 1 ], hPal ); // CS 489 GetGameTypeInfo( 4, OldRAGameTypeInfos[ 2 ], hPal ); // AM 490 491 if( hPal ) 492 DeleteObject( hPal ); 493 } 494 495 //*********************************************************************************************** 496 void WolapiObject::GetGameTypeInfo( int iGameType, WOL_GAMETYPEINFO& GameTypeInfo, HPALETTE hPal ) 497 { 498 unsigned char* pVirtualFile; 499 int iFileLength; 500 // debugprint( "GetGametypeInfo, type %i\n", iGameType ); 501 LPCSTR szName; 502 LPCSTR szURL; 503 pChat->GetGametypeInfo( iGameType, 12, &pVirtualFile, &iFileLength, &szName, &szURL ); 504 GameTypeInfo.iGameType = iGameType; 505 if( szName ) 506 strcpy( GameTypeInfo.szName, szName ); 507 else 508 *GameTypeInfo.szName = 0; 509 if( szURL ) 510 strcpy( GameTypeInfo.szURL, szURL ); 511 else 512 *GameTypeInfo.szURL = 0; 513 514 // debugprint( "LoadDIB_FromMemory( %i, %i )\n", pVirtualFile, iFileLength ); 515 // Create a DIB by "loading" (as if it was a file) the bitmap data. 516 GameTypeInfo.hDIB = LoadDIB_FromMemory( pVirtualFile, iFileLength ); 517 // debugprint( "hDIB is %i\n", GameTypeInfo.hDIB ); 518 if( !GameTypeInfo.hDIB ) 519 { 520 GameTypeInfo.pDIB = NULL; 521 return; // Load failed. Should not ever happen. 522 } 523 GameTypeInfo.pDIB = (const char*)GlobalLock( GameTypeInfo.hDIB ); 524 525 // debugprint( "@@@@@ Created gametypeinfo #%i: name %s, pDIB = %i\n", iIndex, GameTypeInfo.szName, GameTypeInfo.pDIB ); 526 527 LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)GameTypeInfo.pDIB; 528 if( lpbi->biBitCount != 8 ) 529 { 530 // Do not use this loaded bmp, as it's not 256 color. 531 GlobalUnlock( GameTypeInfo.hDIB ); // Release pDIB. 532 GameTypeInfo.pDIB = NULL; 533 DestroyDIB( GameTypeInfo.hDIB ); // Destroy mem alloc'ed for dib bits data. 534 GameTypeInfo.hDIB = 0; 535 return; 536 } 537 538 // Remap colors... 539 RemapDIBToPalette( hPal, GameTypeInfo.pDIB ); 540 541 } 542 543 //*********************************************************************************************** 544 void* WolapiObject::IconForGameType( int iGameType ) 545 { 546 // Returns a GameTypeInfos entry by gametype, instead of our (potentially arbitrary) index. 547 // Returns NULL if type not found in list, which will of course never happen... 548 for( int i = 0; i != nGameTypeInfos; i++ ) 549 { 550 if( GameTypeInfos[i].iGameType == iGameType ) 551 return (void*)GameTypeInfos[i].pDIB; 552 } 553 return NULL; 554 } 555 556 //*********************************************************************************************** 557 const char* WolapiObject::NameOfGameType( int iGameType ) const 558 { 559 // Returns the name of a sku by gametype, instead of our (potentially arbitrary) index. 560 // Returns NULL if type not found in list, which will of course never happen... 561 for( int i = 0; i != nGameTypeInfos; i++ ) 562 { 563 if( GameTypeInfos[i].iGameType == iGameType ) 564 return GameTypeInfos[i].szName; 565 } 566 return NULL; 567 } 568 569 //*********************************************************************************************** 570 const char* WolapiObject::URLForGameType( int iGameType ) const 571 { 572 // Returns NULL if type not found in list, which will of course never happen... 573 for( int i = 0; i != nGameTypeInfos; i++ ) 574 { 575 if( GameTypeInfos[i].iGameType == iGameType ) 576 return GameTypeInfos[i].szURL; 577 } 578 return NULL; 579 } 580 581 //*********************************************************************************************** 582 void WolapiObject::PrintMessage( const char* szText, PlayerColorType iColorRemap /* = PCOLOR_NONE */ ) 583 { 584 if( pILChat ) 585 WOL_PrintMessage( *pILChat, szText, iColorRemap ); 586 } 587 588 //*********************************************************************************************** 589 void WolapiObject::PrintMessage( const char* szText, RemapControlType* pColorRemap ) 590 { 591 if( pILChat ) 592 WOL_PrintMessage( *pILChat, szText, pColorRemap ); 593 } 594 595 //*********************************************************************************************** 596 HRESULT WolapiObject::GetChatServer() 597 { 598 // Calls RequestServerList() to get a chat server ready for us to login to. 599 // Returns S_OK and sets pChatSink->pServer if successful. 600 WWMessageBox().Process( TXT_WOL_CONNECTING, TXT_NONE ); 601 602 //if( !( ::GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) ) // ajw - allow use of test servers 603 //{ 604 605 // Request chat server address from server server. 606 pChatSink->bRequestServerListWait = true; 607 // debugprint( "Calling RequestServerList...\n" ); 608 if( !SUCCEEDED( pChat->RequestServerList( GAME_SKU, GAME_VERSION, "unused", "unused", 5 ) ) ) 609 { 610 // debugprint( "RequestServerList call failed\n" ); 611 return E_FAIL; 612 } 613 DWORD dwTimeLimit = timeGetTime(); // ajw My own extra timeout at one minute, in case wolapi chokes. 614 // debugprint( "Called RequestServerList...\n" ); 615 DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 616 ::GetAsyncKeyState( VK_ESCAPE ); // Set up for escape key checking. 617 bool bCancel = false; 618 hresPatchResults = 0; 619 while( pChatSink->bRequestServerListWait && timeGetTime() - dwTimeLimit < 60000 ) 620 { 621 while( timeGetTime() < dwTimeNextPump ) 622 { 623 Call_Back(); 624 if( ::GetAsyncKeyState( VK_ESCAPE ) & 1 ) 625 { 626 bCancel = true; 627 break; 628 } 629 } 630 // debugprint( "PumpMessages after RequestServerList...\n" ); 631 pChat->PumpMessages(); 632 dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 633 // Sleep( PUMPSLEEPDURATION ); // Can't do because we want to Call_Back() 634 // If an "update list" of patches has been received, instead of a server list, this flag will have been set 635 // for us describing the results. We'll either cancel log in or trigger game exit. 636 if( hresPatchResults ) 637 { 638 pChatSink->bRequestServerListWait = false; 639 return hresPatchResults; 640 } 641 } 642 // debugprint( "RequestServerList wait finished\n" ); 643 if( bCancel ) 644 { 645 Keyboard->Clear(); 646 return USERCANCELLED; 647 } 648 if( pChatSink->pServer ) 649 return S_OK; 650 else 651 return E_FAIL; 652 653 /* 654 } 655 else 656 { 657 // Test using local server on LAN. 658 659 // Bypass RequestServerList, as it is unnecessary and may not be possible if serverserver can't be reached. 660 // Set SKU manually because normally RequestServerList does this for you. 661 pChat->SetProductSKU( GAME_SKU ); 662 if( pChatSink->pServer ) 663 delete pChatSink->pServer; 664 pChatSink->pServer = new Server; 665 if( !( ::GetAsyncKeyState( VK_CONTROL ) & 0x8000 ) ) 666 strcpy( (char*)pChatSink->pServer->conndata, "TCP;irc.westwood.com;9000" ); 667 else 668 // Control key down as well. 669 strcpy( (char*)pChatSink->pServer->conndata, "TCP;10.2.20.28;4000" ); 670 strcpy( (char*)pChatSink->pServer->connlabel, "IRC" ); 671 strcpy( (char*)pChatSink->pServer->name, "Chat"); 672 return S_OK; 673 } 674 */ 675 676 } 677 678 //*********************************************************************************************** 679 HRESULT WolapiObject::AttemptLogin( const char* szName, const char* szPass, bool bPassIsMangled ) 680 { 681 // If RequestConnection() succeeds, sets pChatSink->bConnected true and returns S_OK. 682 // Else returns RequestConnection() error result. 683 WWMessageBox().Process( TXT_WOL_ATTEMPTLOGIN, TXT_NONE ); 684 685 // debugprint( "~1\n" ); 686 strcpy( (char*)pChatSink->pServer->login, szName ); 687 strcpy( (char*)pChatSink->pServer->password, szPass ); 688 689 /* 690 // debugprint( "RequestConnection with:\n%s,%s,%s,%s,%s - %s\n", 691 pChatSink->pServer->name, 692 pChatSink->pServer->connlabel, 693 pChatSink->pServer->conndata, 694 pChatSink->pServer->login, 695 pChatSink->pServer->password, 696 bPassIsMangled ? "(mangled)" : "(unmangled)" ); 697 */ 698 pChatSink->bRequestConnectionWait = true; 699 pChatSink->hresRequestConnectionError = 0; 700 701 if( !SUCCEEDED( pChat->RequestConnection( pChatSink->pServer, 15, !bPassIsMangled ) ) ) 702 { 703 // debugprint( "RequestConnection call failed\n" ); 704 return CHAT_E_CON_ERROR; 705 } 706 707 DWORD dwTimeStart = timeGetTime(); 708 DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 709 ::GetAsyncKeyState( VK_ESCAPE ); // Set up for escape key checking. 710 bool bCancel = false; 711 while( pChatSink->bRequestConnectionWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) 712 { 713 while( timeGetTime() < dwTimeNextPump ) 714 { 715 Call_Back(); 716 if( ::GetAsyncKeyState( VK_ESCAPE ) & 1 ) 717 { 718 bCancel = true; 719 break; 720 } 721 } 722 if( bCancel ) 723 break; 724 pChat->PumpMessages(); 725 dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 726 // Sleep( PUMPSLEEPDURATION ); // Can't do because we want to Call_Back() 727 } 728 if( bCancel ) 729 { 730 Keyboard->Clear(); 731 return USERCANCELLED; 732 } 733 if( pChatSink->bRequestConnectionWait ) 734 return CHAT_E_CON_ERROR; 735 736 if( pChatSink->bConnected ) 737 { 738 strcpy( szMyName, szName ); 739 strcpy( szMyRecord, szName ); 740 strcpy( szMyRecordAM, szName ); 741 return S_OK; 742 } 743 else 744 return pChatSink->hresRequestConnectionError; 745 } 746 747 //*********************************************************************************************** 748 bool WolapiObject::bLoggedIn() 749 { 750 return pChatSink->bConnected; 751 } 752 753 //*********************************************************************************************** 754 void WolapiObject::Logout() 755 { 756 // Requests logout from wolapi. Doesn't return any error values, as what we would do if it 757 // failed - force the user to stay connected? 758 759 if( bSelfDestruct ) 760 WWMessageBox().Process( TXT_WOL_ERRORLOGOUT, TXT_NONE ); 761 else 762 WWMessageBox().Process( TXT_WOL_ATTEMPTLOGOUT, TXT_NONE ); 763 764 // debugprint( "RequestLogout()\n" ); 765 766 pChatSink->bRequestLogoutWait = true; 767 768 if( !SUCCEEDED( pChat->RequestLogout() ) ) 769 { 770 // debugprint( "RequestLogout() call failed\n" ); 771 } 772 773 DWORD dwTimePatience = timeGetTime(); // After 5 seconds we run out of patience and bail. 774 DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 775 while( pChatSink->bRequestLogoutWait && timeGetTime() - dwTimePatience < 5000 ) 776 { 777 while( timeGetTime() < dwTimeNextPump ) 778 Call_Back(); 779 pChat->PumpMessages(); 780 dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 781 } 782 783 pChatSink->bConnected = false; 784 *szMyName = 0; 785 786 Sound_Effect( WOLSOUND_LOGOUT ); 787 } 788 789 //*********************************************************************************************** 790 bool WolapiObject::UpdateChannels( int iChannelType, CHANNELFILTER ChannelFilter, bool bAutoping ) 791 { 792 // This is now non-modal. 793 // Sends off a request for a new channels list. 794 795 // // Returns false upon total failure. 796 // WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); 797 798 // pChatSink->bRequestChannelListWait = true; 799 pChatSink->ChannelFilter = ChannelFilter; 800 801 // debugprint( "RequestChannelList(), iChannelType = %i, filter = %i\n", iChannelType, ChannelFilter ); 802 if( !SUCCEEDED( pChat->RequestChannelList( iChannelType, bAutoping ) ) ) 803 { 804 // debugprint( "RequestChannelList() call failed\n" ); 805 return false; 806 } 807 /* 808 DWORD dwTimeStart = timeGetTime(); 809 DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 810 while( pChatSink->bRequestChannelListWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) 811 { 812 while( timeGetTime() < dwTimeNextPump ) 813 Call_Back(); 814 pChat->PumpMessages(); 815 dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 816 } 817 818 if( pChatSink->bRequestChannelListWait ) 819 return false; 820 */ 821 LastUpdateChannelCallLevel = CurrentLevel; 822 823 return true; 824 } 825 826 //*********************************************************************************************** 827 void WolapiObject::OnChannelList() 828 { 829 // The chatsink calls this when its OnChannelList() is called, and it has remade its internal channel list. 830 // The question here is: should we display the values now in the chatsink? 831 // As UpdateChannels() is now non-modal, there is the danger that we have moved away from the place in 832 // the channel heirarchy where we originally called RequestChannelList(). 833 // To help ensure we're getting this where we expect to get it, we check the value of CurrentLevel against 834 // what it was when we called UpdateChannels(). 835 if( CurrentLevel == LastUpdateChannelCallLevel ) 836 ListChannels(); 837 } 838 839 //*********************************************************************************************** 840 void WolapiObject::ListChannels() 841 { 842 // Show pChatSink's pChannelList in pILChannels. 843 // The extra data ptr hidden in each list item will hold a void pointer to the channel described. 844 // debugprint( "ListChannels(), pChannelList = %i\n", pChatSink->pChannelList ); 845 846 static WOL_LEVEL LevelLastListed = WOL_LEVEL_INVALID; 847 848 int iListViewIndex = 0; 849 850 // If redrawing the same list as before, preserve the view position. 851 if( LevelLastListed == CurrentLevel ) 852 iListViewIndex = pILChannels->Get_View_Index(); 853 else 854 LevelLastListed = CurrentLevel; 855 856 pILChannels->Clear(); 857 switch( CurrentLevel ) 858 { 859 case WOL_LEVEL_GAMESOFTYPE: 860 pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_GAMES, NULL, ICON_SHAPE, CHANNELTYPE_GAMES ); 861 break; 862 case WOL_LEVEL_LOBBIES: 863 pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_GAMES, NULL, ICON_SHAPE, CHANNELTYPE_GAMES ); 864 break; 865 case WOL_LEVEL_INLOBBY: 866 pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_LOBBIES, NULL, ICON_SHAPE, CHANNELTYPE_LOBBIES ); 867 break; 868 default: 869 pILChannels->Add_Item( TXT_WOL_CHANNEL_TOP, CHANNELTYPE_TOP, NULL, ICON_SHAPE, CHANNELTYPE_TOP ); 870 break; 871 } 872 873 Channel* pChannel = pChatSink->pChannelList; 874 while( pChannel ) 875 { 876 if( pChannel->type == 0 ) 877 { 878 // Show chat channel. 879 char* pShow; 880 int iLobby = iChannelLobbyNumber( pChannel->name ); 881 if( iLobby == - 1 ) 882 { 883 // Regular chat channel. 884 pShow = new char[ strlen( (char*)pChannel->name ) + 10 ]; 885 sprintf( pShow, "%s\t%-3u", (char*)pChannel->name, pChannel->currentUsers ); 886 char szHelp[ 200 ]; 887 sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_CHAT, (char*)pChannel->name, pChannel->currentUsers ); 888 pILChannels->Add_Item( pShow, szHelp, (void*)DibIconInfos[ DIBICON_USER ].pDIB, ICON_DIB, CHANNELTYPE_CHATCHANNEL, (void*)pChannel ); 889 } 890 else 891 { 892 // Channel is a lobby. 893 char szLobbyName[ REASONABLELOBBYINTERPRETEDNAMELEN ]; 894 InterpretLobbyNumber( szLobbyName, iLobby ); 895 pShow = new char[ REASONABLELOBBYINTERPRETEDNAMELEN + 10 ]; 896 sprintf( pShow, "%s\t%-3u", szLobbyName, pChannel->currentUsers ); 897 char szHelp[ 200 ]; 898 sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_LOBBY, szLobbyName, pChannel->currentUsers ); 899 pILChannels->Add_Item( pShow, szHelp, IconForGameType( -1 ), ICON_DIB, CHANNELTYPE_LOBBYCHANNEL, (void*)pChannel ); 900 // debugprint( ":::::added pChannel %i, name %s, as %s\n", pChannel, pChannel->name, pShow ); 901 } 902 delete [] pShow; 903 } 904 else 905 { 906 // Show game channel. 907 char* pShow = new char[ strlen( (char*)pChannel->name ) + 10 ]; 908 char szHelp[ 200 ]; 909 void* pGameKindIcon; 910 if( pChannel->type == GAME_TYPE ) 911 { 912 // Get RedAlert GameKind. 913 CREATEGAMEINFO::GAMEKIND GameKind = (CREATEGAMEINFO::GAMEKIND)( pChannel->reserved & 0xFF000000 ); 914 switch( GameKind ) 915 { 916 case CREATEGAMEINFO::RAGAME: 917 pGameKindIcon = (void*)OldRAGameTypeInfos[ 0 ].pDIB; // Red Alert icon 918 sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_RAGAME, TXT_WOL_TTIP_REDALERT, 919 pChannel->currentUsers, pChannel->maxUsers ); 920 break; 921 case CREATEGAMEINFO::CSGAME: 922 pGameKindIcon = (void*)OldRAGameTypeInfos[ 1 ].pDIB; // CS icon 923 sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_RAGAME, TXT_WOL_TTIP_COUNTERSTRIKE, 924 pChannel->currentUsers, pChannel->maxUsers ); 925 break; 926 case CREATEGAMEINFO::AMGAME: 927 pGameKindIcon = (void*)OldRAGameTypeInfos[ 2 ].pDIB; // AM icon 928 sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_RAGAME, TXT_WOL_TTIP_AFTERMATH, 929 pChannel->currentUsers, pChannel->maxUsers ); 930 break; 931 default: 932 // debugprint( "Illegal value for GameKind channel reserved field: %s\n", (char*)pChannel->name ); 933 pGameKindIcon = NULL; 934 break; 935 } 936 sprintf( pShow, "%s\t%u/%u", (char*)pChannel->name, pChannel->currentUsers, pChannel->maxUsers ); 937 } 938 else 939 { 940 pGameKindIcon = IconForGameType( pChannel->type ); 941 sprintf( pShow, "%s\t%-2u", (char*)pChannel->name, pChannel->currentUsers ); 942 sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_GAME, NameOfGameType( pChannel->type ), 943 pChannel->currentUsers ); 944 } 945 void* pPrivateIcon = NULL; 946 if( pChannel->flags & CHAN_MODE_KEY ) 947 { 948 // Game is private. 949 pPrivateIcon = (void*)DibIconInfos[ DIBICON_PRIVATE ].pDIB; 950 strcat( szHelp, TXT_WOL_TTIP_PRIVATEGAME ); 951 } 952 953 void* pTournamentIcon = NULL; 954 if( pChannel->tournament ) 955 { 956 // Game is tournament. 957 pTournamentIcon = (void*)DibIconInfos[ DIBICON_TOURNAMENT ].pDIB; 958 strcat( szHelp, TXT_WOL_TTIP_TOURNAMENTGAME ); 959 } 960 961 int iLatencyUse = pChannel->latency; 962 if( iLatencyUse == -1 ) 963 iLatencyUse = 0; 964 965 static int iLatencyBarX = 227 - DIBWidth( DibIconInfos[ DIBICON_LATENCY ].pDIB ) - 19; 966 967 pILChannels->Add_Item( pShow, szHelp, pGameKindIcon, ICON_DIB, 968 CHANNELTYPE_GAMECHANNEL, (void*)pChannel, NULL, 969 pPrivateIcon, ICON_DIB, pTournamentIcon, ICON_DIB, 970 (void*)DibIconInfos[ DIBICON_LATENCY ].pDIB, ICON_DIB, 971 iLatencyBarX, 0, iLatencyUse * fLatencyToIconWidth ); 972 delete [] pShow; 973 } 974 pChannel = pChannel->next; 975 } 976 if( iListViewIndex ) 977 pILChannels->Set_View_Index( iListViewIndex ); // Not perfect but should keep list pretty stable on updates. 978 } 979 980 //*********************************************************************************************** 981 HRESULT WolapiObject::ChannelJoin( const char* szChannelName, const char* szKey ) 982 { 983 // Used for CHAT channels (or lobbies) only. Channel type is set to 0. 984 Channel ChannelTemp; 985 memset( &ChannelTemp, 0, sizeof( ChannelTemp ) ); 986 strcpy( (char*)ChannelTemp.name, szChannelName ); 987 strcpy( (char*)ChannelTemp.key, szKey ); 988 return ChannelJoin( &ChannelTemp ); 989 } 990 991 //*********************************************************************************************** 992 HRESULT WolapiObject::ChannelJoin( Channel* pChannelToJoin ) 993 { 994 // Returns an HRESULT, the meaning of which is totally customized for my own uses. 995 996 WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); 997 998 pChatSink->bRequestChannelJoinWait = true; 999 pChatSink->hresRequestJoinResult = 0; 1000 1001 // debugprint( "RequestChannelJoin(), %s\n", pChannelToJoin->name ); 1002 HRESULT hRes = pChat->RequestChannelJoin( pChannelToJoin ); 1003 if( !SUCCEEDED( hRes ) ) 1004 { 1005 // debugprint( "RequestChannelJoin() call failed, result %i ", hRes ); 1006 DebugChatDef( hRes ); 1007 return E_FAIL; 1008 } 1009 pChatSink->bIgnoreChannelLists = true; // Turn off response to channel lists. 1010 DWORD dwTimeStart = timeGetTime(); 1011 DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 1012 while( pChatSink->bRequestChannelJoinWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) 1013 { 1014 while( timeGetTime() < dwTimeNextPump ) 1015 Call_Back(); 1016 pChat->PumpMessages(); 1017 dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 1018 } 1019 pChatSink->bIgnoreChannelLists = false; // Turn on response to channel lists. 1020 1021 if( pChatSink->bRequestChannelJoinWait ) 1022 return CHAT_E_TIMEOUT; 1023 1024 switch( pChatSink->hresRequestJoinResult ) 1025 { 1026 case CHAT_E_CHANNELDOESNOTEXIST: 1027 case CHAT_E_BADCHANNELPASSWORD: 1028 case CHAT_E_BANNED: 1029 case CHAT_E_CHANNELFULL: 1030 return pChatSink->hresRequestJoinResult; 1031 } 1032 1033 if( !pChatSink->bJoined ) 1034 return E_FAIL; 1035 1036 return S_OK; 1037 } 1038 1039 //*********************************************************************************************** 1040 bool WolapiObject::ChannelLeave() 1041 { 1042 // Returns false upon total failure. 1043 WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); 1044 1045 pChatSink->bRequestChannelLeaveWait = true; 1046 pChatSink->DeleteUserList(); 1047 1048 // debugprint( "RequestChannelLeave()\n" ); 1049 if( !SUCCEEDED( pChat->RequestChannelLeave() ) ) 1050 { 1051 // debugprint( "RequestChannelLeave() call failed\n" ); 1052 return false; 1053 } 1054 pChatSink->bIgnoreChannelLists = true; // Turn off response to channel lists. 1055 DWORD dwTimeStart = timeGetTime(); 1056 DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 1057 while( pChatSink->bRequestChannelLeaveWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) 1058 { 1059 while( timeGetTime() < dwTimeNextPump ) 1060 Call_Back(); 1061 pChat->PumpMessages(); 1062 dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 1063 } 1064 pChatSink->bIgnoreChannelLists = false; 1065 1066 if( pChatSink->bRequestChannelLeaveWait || pChatSink->bJoined ) 1067 return false; 1068 1069 return true; 1070 } 1071 1072 /* 1073 //*********************************************************************************************** 1074 bool WolapiObject::UserList() 1075 { 1076 // Returns false upon total failure. 1077 WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); 1078 1079 pChatSink->bRequestUserListWait = true; 1080 1081 // I no longer request a user list, as this was being done only when entering a channel, and it turns out wolapi gives 1082 // us a user list automatically when joining. This function is used as a blocker that waits until the user list has 1083 // definitely arrived. 1084 // debugprint( "RequestUserList()\n" ); 1085 // if( !SUCCEEDED( pChat->RequestUserList() ) ) 1086 // { 1087 // debugprint( "RequestUserList() call failed\n" ); 1088 // return false; 1089 // } 1090 1091 DWORD dwTimeStart = timeGetTime(); 1092 DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 1093 while( pChatSink->bRequestUserListWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) 1094 { 1095 while( timeGetTime() < dwTimeNextPump ) 1096 Call_Back(); 1097 pChat->PumpMessages(); 1098 dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 1099 } 1100 1101 if( pChatSink->bRequestUserListWait ) 1102 { 1103 pChatSink->bRequestUserListWait = false; 1104 return false; 1105 } 1106 1107 return true; 1108 } 1109 */ 1110 1111 //*********************************************************************************************** 1112 //typedef char CHANNELUSERNAME[ WOL_NAME_LEN_MAX ]; 1113 struct CHANNELUSERINFO 1114 { 1115 char szName[ WOL_NAME_LEN_MAX ]; 1116 bool bFlagged; 1117 RemapControlType* pColorRemap; 1118 HousesType House; // Only used if game channel. 1119 bool bAccept; // Only used if game channel. 1120 char szExtra[ 50 ]; // Only used if game channel. 1121 }; 1122 1123 //*********************************************************************************************** 1124 bool WolapiObject::ListChannelUsers() 1125 { 1126 // Show pChatSink's pUserList in pILUsers or pILPlayers (depending on CurrentLevel), after clearing it. 1127 // The extra data ptr hidden in each list item will hold a void pointer to the user described. 1128 // For simplicity, I destroy the old list and write a new one, even though possibly only one item 1129 // may have changed. 1130 // In order for the multiselect flags in the list to persist, I record the names that are flagged 1131 // before clearing the list, then reset them (if found) in the new list. 1132 // This is inefficient, but should be fine in this non-time-critical situation. 1133 // (I also save item color here, and save all items. Now it's really inefficient.) 1134 // (Oh, boy. Now I've added the persistence of house info when this is a game channel...) 1135 // The idea was to avoid duplication of player data, and not have any dependence on the integrity of the chatsink's 1136 // players list. Now it is a case of it working "well enough" to not require time to elegantize it. 1137 1138 // Extra bonus, if useful: returns true if an operator (channel owner) is found in the channel, false otherwise. 1139 1140 bool bChannelOwnerFound = false; 1141 1142 bool bInLobby; 1143 IconListClass* pListToUse; 1144 if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) 1145 { 1146 bInLobby = false; 1147 pListToUse = pILPlayers; 1148 } 1149 else 1150 { 1151 bInLobby = ( iChannelLobbyNumber( (unsigned char*)szChannelNameCurrent ) != -1 ); 1152 pListToUse = pILUsers; 1153 } 1154 1155 if( pListToUse && // Fails in rare cases when list draw is triggered before it is fully set up. 1156 *szChannelNameCurrent ) // No users to list if not in a channel. 1157 { 1158 // If redrawing the same list as before, preserve the view position. 1159 static char szChannelLastListed[ WOL_CHANNAME_LEN_MAX ] = { 0 }; 1160 //debugprint( "szChannelLastListed '%s', szChannelNameCurrent '%s'\n", szChannelLastListed, szChannelNameCurrent ); 1161 int iListViewIndex = 0; 1162 if( strcmp( szChannelLastListed, szChannelNameCurrent ) == 0 ) 1163 iListViewIndex = pListToUse->Get_View_Index(); 1164 else 1165 strcpy( szChannelLastListed, szChannelNameCurrent ); 1166 1167 //debugprint( "ListChannelUsers(), pUserList = %i\n", pChatSink->pUserList ); 1168 // Save users in current list. 1169 int iCount = pListToUse->Count(); 1170 CHANNELUSERINFO* pUsersSaved = NULL; 1171 int iUsersSaved = 0; 1172 if( iCount ) 1173 { 1174 pUsersSaved = new CHANNELUSERINFO[ iCount ]; 1175 for( int i = 0; i != iCount; i++ ) 1176 { 1177 PullPlayerName_Into_From( pUsersSaved[ iUsersSaved ].szName, pListToUse->Get_Item( i ) ); 1178 pUsersSaved[ iUsersSaved ].bFlagged = pListToUse->bItemIsMultiSelected( i ); 1179 pUsersSaved[ iUsersSaved ].pColorRemap = pListToUse->Get_Item_Color( i ); 1180 // debugprint( " Saving color of %s as %i.\n", pUsersSaved[ iUsersSaved ].szName, pUsersSaved[ iUsersSaved ].pColorRemap ); 1181 if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) 1182 { 1183 pUsersSaved[ iUsersSaved ].House = PullPlayerHouse_From( pListToUse->Get_Item( i ) ); 1184 pUsersSaved[ iUsersSaved ].bAccept = bItemMarkedAccepted( i ); 1185 const char* szExtra = pListToUse->Get_Item_ExtraDataString( i ); 1186 if( szExtra ) 1187 strcpy( pUsersSaved[ iUsersSaved ].szExtra, szExtra ); 1188 else 1189 *pUsersSaved[ iUsersSaved ].szExtra = 0; 1190 } 1191 iUsersSaved++; 1192 } 1193 } 1194 // Clear list and recreate with new users list. 1195 pListToUse->Clear(); 1196 User* pUser = pChatSink->pUserList; 1197 int iUserCount = 0; 1198 while( pUser ) 1199 { 1200 ++iUserCount; 1201 void* pIcon1 = NULL; 1202 void* pIcon2 = NULL; 1203 if( pUser->flags & CHAT_USER_CHANNELOWNER ) 1204 { 1205 pIcon1 = (void*)DibIconInfos[ DIBICON_OWNER ].pDIB; 1206 bChannelOwnerFound = true; 1207 } 1208 else 1209 { 1210 if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) 1211 pIcon1 = (void*)DibIconInfos[ DIBICON_NOTACCEPT ].pDIB; 1212 else 1213 { 1214 if( pUser->flags & CHAT_USER_VOICE ) 1215 pIcon1 = (void*)DibIconInfos[ DIBICON_VOICE ].pDIB; 1216 else 1217 pIcon1 = (void*)DibIconInfos[ DIBICON_USER ].pDIB; 1218 } 1219 } 1220 if( pUser->flags & CHAT_USER_SQUELCHED ) 1221 pIcon2 = (void*)DibIconInfos[ DIBICON_SQUELCH ].pDIB; 1222 1223 if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL || bInLobby ) 1224 { 1225 int iRank = pNetUtilSink->GetUserRank( (char*)pUser->name, bShowRankRA ); 1226 char szNameToShow[ WOL_NAME_LEN_MAX + 40 ]; 1227 if( iRank ) 1228 { 1229 // debugprint(" Found %s has rank %u\n", (char*)pUser->name, iRank ); 1230 sprintf( szNameToShow, TXT_WOL_USERRANK, (char*)pUser->name, iRank ); 1231 } 1232 else 1233 strcpy( szNameToShow, (char*)pUser->name ); 1234 1235 static int iLatencyBarX = 124*RESFACTOR - DIBWidth( DibIconInfos[ DIBICON_LATENCY ].pDIB ) - 5 - 16; 1236 1237 // If we have had a chance to request pings to the player, there'll be some avg. results waiting for us. 1238 int iLatencyBarWidth = 0; 1239 int iLatency; 1240 if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) 1241 { 1242 unsigned long UserIP = pChatSink->GetUserIP( (char*)pUser->name ); 1243 // debugprint( "player %s ip address %i\n", szNameToShow, UserIP ); 1244 if( UserIP && pNetUtil->GetAvgPing( UserIP, &iLatency ) == S_OK ) 1245 { 1246 // debugprint( "player %s latency %i\n", szNameToShow, iLatency ); 1247 if( iLatency == -1 ) 1248 iLatency = 0; 1249 iLatencyBarWidth = iLatency * fLatencyToIconWidth; 1250 } 1251 } 1252 pListToUse->Add_Item( szNameToShow, NULL, pIcon1, ICON_DIB, NULL, (void*)pUser, NULL, pIcon2, ICON_DIB, NULL, ICON_DIB, 1253 (void*)DibIconInfos[ DIBICON_LATENCY ].pDIB, ICON_DIB, iLatencyBarX, 2, iLatencyBarWidth ); 1254 } 1255 else 1256 pListToUse->Add_Item( (char*)pUser->name, NULL, pIcon1, ICON_DIB, NULL, (void*)pUser, NULL, pIcon2, ICON_DIB ); 1257 1258 pUser = pUser->next; 1259 } 1260 if( pStaticUsers ) 1261 { 1262 // Display number of users in channel. 1263 char szCount[100]; 1264 sprintf( szCount, TXT_WOL_USERLIST, iUserCount ); 1265 pStaticUsers->Set_Text( szCount ); 1266 } 1267 // Reset multiselectedness, color, and item text for a user. Slow. 1268 // (What a bloody, bloody hack.) 1269 for( int iUser = 0; iUser != iUsersSaved; iUser++ ) 1270 { 1271 int iFind = pListToUse->Find( pUsersSaved[ iUser ].szName ); // Finds any item beginning with szName... 1272 if( iFind != -1 ) 1273 { 1274 if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) 1275 { 1276 if( pUsersSaved[ iUser ].House != HOUSE_NONE ) 1277 { 1278 // Append house text to item string, as we found a valid house name after the name, above. 1279 char szItem[ 120 ]; 1280 WritePlayerListItem( szItem, pUsersSaved[ iUser ].szName, pUsersSaved[ iUser ].House ); 1281 pListToUse->Set_Item( iFind, szItem ); 1282 } 1283 if( pUsersSaved[ iUser ].bAccept ) 1284 { 1285 // Player was marked "accepted" before. If he has one now, it's because he is the host. 1286 // Else it was an accepted icon before, so put one in again now. (a-hacking-we-will-go) 1287 if( !bItemMarkedAccepted( iFind ) ) 1288 MarkItemAccepted( iFind, true ); 1289 } 1290 if( *pUsersSaved[ iUser ].szExtra ) 1291 pListToUse->Set_Item_ExtraDataString( iFind, pUsersSaved[ iUser ].szExtra ); 1292 } 1293 if( pUsersSaved[ iUser ].bFlagged ) 1294 pListToUse->MultiSelect( iFind, true ); 1295 // debugprint( " Restoring color of %s as %i.\n", pUsersSaved[ iUser ].szName, pUsersSaved[ iUser ].pColorRemap ); 1296 pListToUse->Set_Item_Color( iFind, pUsersSaved[ iUser ].pColorRemap ); 1297 } 1298 // else 1299 // debugprint( "ListChannelUsers() - Couldn't find %s!\n", pUsersSaved[ iUser ].szName ); 1300 } 1301 delete [] pUsersSaved; 1302 if( iListViewIndex ) 1303 pListToUse->Set_View_Index( iListViewIndex ); // Not perfect but should keep list pretty stable on updates. 1304 } 1305 return bChannelOwnerFound; 1306 } 1307 1308 //*********************************************************************************************** 1309 bool WolapiObject::bItemMarkedAccepted( int iIndex ) 1310 { 1311 // Returns true if the iIndex'th entry in pILPlayers has an icon pointer in position 0 that 1312 // is either the host icon or the accepted icon. 1313 const IconList_ItemExtras* pItemExtras = pILPlayers->Get_ItemExtras( iIndex ); 1314 return ( pItemExtras->pIcon[0] == (void*)DibIconInfos[ DIBICON_OWNER ].pDIB || 1315 pItemExtras->pIcon[0] == (void*)DibIconInfos[ DIBICON_ACCEPT ].pDIB ); 1316 } 1317 1318 //*********************************************************************************************** 1319 bool WolapiObject::MarkItemAccepted( int iIndex, bool bAccept ) 1320 { 1321 pILPlayers->Flag_To_Redraw(); 1322 if( bAccept ) 1323 return pILPlayers->Set_Icon( iIndex, 0, (void*)DibIconInfos[ DIBICON_ACCEPT ].pDIB, ICON_DIB ); 1324 else 1325 //return pILPlayers->Set_Icon( iIndex, 0, NULL, ICON_DIB ); 1326 return pILPlayers->Set_Icon( iIndex, 0, (void*)DibIconInfos[ DIBICON_NOTACCEPT ].pDIB, ICON_DIB ); 1327 } 1328 1329 //*********************************************************************************************** 1330 bool WolapiObject::bItemMarkedReadyToGo( int iIndex ) 1331 { 1332 // Returns true if the iIndex'th entry in pILPlayers marks the player as "ready to go". 1333 // This is true if the player is marked as "ready" or "need scenario". 1334 const char* szItem = pILPlayers->Get_Item_ExtraDataString( iIndex ); 1335 if( !szItem ) 1336 return false; 1337 // debugprint( "szItem is %s\n", szItem ); 1338 return ( strcmp( szItem, "ready" ) == 0 || strcmp( szItem, "need scenario" ) == 0 ); 1339 } 1340 1341 //*********************************************************************************************** 1342 void WolapiObject::MarkItemReadyToGo( int iIndex, const char* szReadyState ) 1343 { 1344 // Set szReadyState to "ready", "need scenario", or NULL. 1345 // First two cases are regarded as player being ready to go. 1346 pILPlayers->Flag_To_Redraw(); 1347 pILPlayers->Set_Item_ExtraDataString( iIndex, szReadyState ); 1348 } 1349 1350 //*********************************************************************************************** 1351 bool WolapiObject::bItemMarkedNeedScenario( int iIndex ) 1352 { 1353 // Returns true if the iIndex'th entry in pILPlayers marks the player as ready to go, but needing scenario download. 1354 const char* szItem = pILPlayers->Get_Item_ExtraDataString( iIndex ); 1355 if( !szItem ) 1356 return false; 1357 return ( strcmp( szItem, "need scenario" ) == 0 ); 1358 } 1359 1360 //*********************************************************************************************** 1361 void WolapiObject::PullPlayerName_Into_From( char* szDest, const char* szSource ) 1362 { 1363 // Sets szDest to the "player name" found in szSource. 1364 // Called "player" name because this is mainly designed for use in game channels. 1365 // Player name appears first in item, separated by a space from anything later. 1366 1367 char* pSpace = strstr( szSource, " " ); 1368 if( !pSpace ) 1369 { 1370 // No space character. Use entire item. 1371 strcpy( szDest, szSource ); 1372 } 1373 else 1374 { 1375 int iSpacePosition = pSpace - szSource; 1376 strncpy( szDest, szSource, iSpacePosition ); 1377 szDest[ iSpacePosition ] = 0; // terminate 1378 } 1379 // debugprint( "PullPlayerName_Into_From: '%s' from '%s', ok?\n", szDest, szSource ); 1380 } 1381 1382 //*********************************************************************************************** 1383 HousesType WolapiObject::PullPlayerHouse_From( const char* szSource ) 1384 { 1385 // Pulls the house value out of a player list item in a game channel. 1386 // House appears as the last word, and it's in <>. 1387 // char* pChar = strrchr( szSource, ' ' ); // Last space character. was failing on roy. uni cause of space 1388 // if( !pChar ) 1389 // return HOUSE_NONE; 1390 // ++pChar; 1391 // if( *pChar++ != '<' ) // We know house has to be last, so if not the case, no house in item. 1392 // return HOUSE_NONE; 1393 char* pChar = strrchr( szSource, '<' ); // Last < character. 1394 if( !pChar ) 1395 return HOUSE_NONE; 1396 ++pChar; 1397 int iLen = strlen( pChar ); // Remaining: "housename>" 1398 // Copy remaining string. 1399 char szHouse[ 30 ]; 1400 strcpy( szHouse, pChar ); 1401 *( szHouse + iLen - 1 ) = 0; // Terminate to remove ">" 1402 // debugprint( "PullPlayerHouse_From: '%s' from '%s', ok?\n", szHouse, szSource ); 1403 // pChar is now a valid house name. 1404 1405 // return HouseTypeClass::From_Name( szHouse ); 1406 #ifdef ENGLISH 1407 // Bloody bloody hell I can't believe there are bugs in RA like the one I deal with here... 1408 if( strcmp( szHouse, "Russia" ) == 0 ) 1409 return HOUSE_USSR; 1410 else 1411 return HouseTypeClass::From_Name( szHouse ); // Fails on "Russia". (Thinks "USSR".) 1412 #else 1413 for( HousesType house = HOUSE_USSR; house <= HOUSE_FRANCE; house++ ) 1414 if( strcmp( Text_String(HouseTypeClass::As_Reference(house).Full_Name()), szHouse ) == 0 ) 1415 return house; 1416 // debugprint( "dohfus" ); // should never happen 1417 // Fatal( "" ); 1418 return HOUSE_USSR; 1419 #endif 1420 } 1421 1422 //*********************************************************************************************** 1423 void WolapiObject::WritePlayerListItem( char* szDest, const char* szName, HousesType House ) 1424 { 1425 // Sets szDest to the way a player list item appears in a game channel. 1426 char szHouse[ 50 ]; 1427 strcpy( szHouse, Text_String( HouseTypeClass::As_Reference( House ).Full_Name() ) ); 1428 1429 int iRank = pNetUtilSink->GetUserRank( szName, bShowRankRA ); // Horrendous inefficiency here, when called for relisting players... 1430 if( iRank ) 1431 sprintf( szDest, TXT_WOL_USERRANKHOUSE, szName, iRank, szHouse ); 1432 else 1433 sprintf( szDest, TXT_WOL_USERHOUSE, szName, szHouse ); 1434 // debugprint( "WritePlayerListItem: '%s', ok?\n", szDest ); 1435 } 1436 1437 //*********************************************************************************************** 1438 void WolapiObject::RequestPlayerPings() 1439 { 1440 // Does a RequestPing for every other player listed in pILPlayers. 1441 for( int i = 0; i < pILPlayers->Count(); i++ ) 1442 { 1443 User* pUser = (User*)pILPlayers->Get_Item_ExtraDataPtr( i ); 1444 if( pUser && !( pUser->flags & CHAT_USER_MYSELF ) ) 1445 { 1446 unsigned long UserIP = pChatSink->GetUserIP( (char*)pUser->name ); 1447 if( UserIP ) 1448 { 1449 int iUnused; 1450 in_addr inaddrUser; 1451 inaddrUser.s_addr = UserIP; 1452 char* szIP = inet_ntoa( inaddrUser ); 1453 // debugprint( "RequestPing of %s, ipaddr of %i, aka %s\n", (char*)pUser->name, UserIP, szIP ); 1454 pNetUtil->RequestPing( szIP, 1000, &iUnused ); 1455 } 1456 } 1457 } 1458 } 1459 1460 //*********************************************************************************************** 1461 void WolapiObject::SendMessage( const char* szMessage, IconListClass& ILUsers, bool bAction ) 1462 { 1463 // Send regular chat message. 1464 1465 if( *szMessage == 0 ) 1466 return; 1467 1468 if( strlen( szMessage ) > 4 && szMessage[0] == 63 && szMessage[1] == 97 && szMessage[2] == 106 && szMessage[3] == 119 ) 1469 { 1470 int i = atoi( szMessage + 4 ); 1471 if( i >= VOX_ACCOMPLISHED && i <= VOX_LOAD1 ) 1472 Speak( (VoxType)i ); 1473 return; 1474 } 1475 if( strlen( szMessage ) > 4 && szMessage[0] == 35 && szMessage[1] == 97 && szMessage[2] == 106 && szMessage[3] == 119 ) 1476 { 1477 int i = atoi( szMessage + 4 ); 1478 if( i >= VOX_ACCOMPLISHED && i <= VOX_LOAD1 ) 1479 Speak( (VoxType)i ); 1480 } 1481 1482 // Iterate through ILUsers looking for selected entries. Build up a users list of selected 1483 // items. If the list turns out to be blank, send message publicly. 1484 User* pUserListSend = NULL; 1485 User* pUserNew; 1486 User* pUserTail = NULL; 1487 int iCount = ILUsers.Count(); 1488 int iPrivatePrintLen = 1; 1489 for( int i = 0; i != iCount; i++ ) 1490 { 1491 if( ILUsers.bItemIsMultiSelected( i ) ) 1492 { 1493 pUserNew = new User; 1494 *pUserNew = *( (User*)ILUsers.Get_Item_ExtraDataPtr( i ) ); 1495 // debugprint( "Copied %s for sendmessage.\n", pUserNew->name ); 1496 pUserNew->next = NULL; // (We don't want the value that was just copied!) 1497 if( !pUserTail ) 1498 { 1499 // First User in list. 1500 pUserListSend = pUserNew; 1501 } 1502 else 1503 { 1504 pUserTail->next = pUserNew; 1505 } 1506 pUserTail = pUserNew; 1507 iPrivatePrintLen += ( strlen( (char*)pUserNew->name ) + 2 ); // Extra space and comma. 1508 } 1509 } 1510 if( pUserListSend ) 1511 { 1512 // Send private message. 1513 if( !bAction ) 1514 pChat->RequestPrivateMessage( pUserListSend, szMessage ); 1515 else 1516 pChat->RequestPrivateAction( pUserListSend, szMessage ); 1517 char* szPrint = 0; 1518 if( iPrivatePrintLen > 50 ) 1519 { 1520 // Too many users specified to print out. Just say "multiple users". 1521 if( !bAction ) 1522 { 1523 szPrint = new char[ strlen( szMessage ) + 135 ]; 1524 sprintf( szPrint, "%s %s", TXT_WOL_PRIVATETOMULTIPLE, szMessage ); 1525 } 1526 else 1527 { 1528 szPrint = new char[ strlen( szMessage ) + strlen( szMyName ) + 138 ]; 1529 sprintf( szPrint, "%s %s %s", TXT_WOL_PRIVATETOMULTIPLE, szMyName, szMessage ); 1530 } 1531 } 1532 else 1533 { 1534 if( !bAction ) 1535 szPrint = new char[ strlen( szMessage ) + iPrivatePrintLen + 120 ]; 1536 else 1537 szPrint = new char[ strlen( szMessage ) + iPrivatePrintLen + 125 + strlen( szMyName ) ]; 1538 //strcpy( szPrint, "<Private to " ); 1539 sprintf( szPrint, "<%s ", TXT_WOL_PRIVATETO ); 1540 User* pUserPrint = pUserListSend; 1541 while( pUserPrint ) 1542 { 1543 strcat( szPrint, (char*)pUserPrint->name ); 1544 if( pUserPrint->next ) 1545 strcat( szPrint, ", " ); 1546 else 1547 strcat( szPrint, ">: " ); 1548 pUserPrint = pUserPrint->next; 1549 } 1550 if( bAction ) 1551 { 1552 strcat( szPrint, szMyName ); 1553 strcat( szPrint, " " ); 1554 } 1555 strcat( szPrint, szMessage ); 1556 } 1557 if( !bAction ) 1558 PrintMessage( szPrint, WOLCOLORREMAP_SELFSPEAKING ); 1559 else 1560 { 1561 PrintMessage( szPrint, WOLCOLORREMAP_ACTION ); 1562 pChatSink->ActionEggSound( szMessage ); 1563 } 1564 delete [] szPrint; 1565 } 1566 else 1567 { 1568 // Send public message. 1569 if( !bAction ) 1570 { 1571 // Easter egg related. 1572 if( _stricmp( szMessage, "/nousersounds" ) == 0 ) 1573 { 1574 bEggSounds = false; 1575 return; 1576 } 1577 else if( _stricmp( szMessage, "/usersounds" ) == 0 ) // Left as obvious text in the exe, for someone to find... :-) 1578 { 1579 bEggSounds = true; 1580 return; 1581 } 1582 else if( _stricmp( szMessage, "/8playergames" ) == 0 ) // Left as obvious text in the exe, for someone to find... :-) 1583 { 1584 bEgg8Player = true; 1585 return; 1586 } 1587 HRESULT hRes = pChat->RequestPublicMessage( szMessage ); 1588 if( hRes != S_OK ) 1589 { 1590 // debugprint( " RequestPublicMessage() failed with: " ); 1591 // DebugChatDef( hRes ); 1592 } 1593 } 1594 else 1595 { 1596 HRESULT hRes = pChat->RequestPublicAction( szMessage ); 1597 if( hRes != S_OK ) 1598 { 1599 // debugprint( " RequestPublicAction() failed with: " ); 1600 // DebugChatDef( hRes ); 1601 } 1602 } 1603 char* szPrint = new char[ strlen( szMessage ) + strlen( szMyName ) + 10 ]; 1604 if( !bAction ) 1605 { 1606 sprintf( szPrint, "%s: %s", szMyName, szMessage ); 1607 PrintMessage( szPrint, WOLCOLORREMAP_SELFSPEAKING ); 1608 } 1609 else 1610 { 1611 sprintf( szPrint, "%s %s", szMyName, szMessage ); 1612 PrintMessage( szPrint, WOLCOLORREMAP_ACTION ); 1613 pChatSink->ActionEggSound( szMessage ); 1614 } 1615 delete [] szPrint; 1616 } 1617 } 1618 1619 //*********************************************************************************************** 1620 bool WolapiObject::ChannelCreate( const char* szChannelName, const char* szKey, bool bGame /* = false */, 1621 int iMaxPlayers /* = 0 */, bool bTournament /* = false */, int iLobby /* = 0 */, 1622 CREATEGAMEINFO::GAMEKIND GameKind /* = red alert */ ) 1623 { 1624 // Create a channel. 1625 // szKey is NULL if a public channel is to be created, else channel password. 1626 1627 // Returns true if everything goes okay. 1628 1629 if( pChatSink->bJoined ) 1630 { 1631 // This never happens. Here just in case. 1632 // debugprint( "WolapiObject::ChannelCreate called when bJoined is true!\n" ); 1633 return false; 1634 // Fatal( "WolapiObject::ChannelCreate called when bJoined is true!" ); 1635 } 1636 1637 Channel ChannelNew; 1638 1639 // Prepare the struct. 1640 memset( &ChannelNew, 0, sizeof( ChannelNew ) ); 1641 1642 if( !bGame ) 1643 { 1644 // ChannelNew.type = 0; 0 for chat channel. 1645 strcpy( (char*)ChannelNew.name, szChannelName ); 1646 } 1647 else 1648 { 1649 ChannelNew.type = GAME_TYPE; 1650 ChannelNew.maxUsers = iMaxPlayers; 1651 ChannelNew.tournament = bTournament; 1652 // Channel 'reserved' stores GameKind in the highest byte, and 1653 // lobby number to return to in the lower three bytes. 1654 // Note: If lobby number is -1 (no lobby to return to), it's encoded as 0x00FFFFFF 1655 ChannelNew.reserved = ( iLobby & 0x00FFFFFF ) | GameKind; 1656 strcpy( (char*)ChannelNew.name, szChannelName ); 1657 } 1658 1659 // debugprint( "RequestChannelCreate(), channel name: '%s'\n", szChannelName ); 1660 1661 if( szKey ) 1662 strcpy( (char*)ChannelNew.key, szKey ); 1663 1664 WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); 1665 1666 pChatSink->bRequestChannelCreateWait = true; 1667 1668 HRESULT hRes = pChat->RequestChannelCreate( &ChannelNew ); 1669 if( !SUCCEEDED( hRes ) ) 1670 { 1671 // debugprint( "RequestChannelCreate() call failed:" ); 1672 DebugChatDef( hRes ); 1673 return false; 1674 } 1675 pChatSink->bIgnoreChannelLists = true; // Turn off response to channel lists. 1676 DWORD dwTimeStart = timeGetTime(); 1677 DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 1678 while( pChatSink->bRequestChannelCreateWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) 1679 { 1680 while( timeGetTime() < dwTimeNextPump ) 1681 Call_Back(); 1682 pChat->PumpMessages(); 1683 dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 1684 } 1685 pChatSink->bIgnoreChannelLists = false; 1686 1687 if( pChatSink->bRequestChannelCreateWait || !pChatSink->bJoined ) 1688 return false; // Timed out or callback got fail value. 1689 1690 if( bGame ) 1691 iLobbyReturnAfterGame = iLobby; 1692 1693 return true; 1694 } 1695 1696 //*********************************************************************************************** 1697 void WolapiObject::DoFindPage() 1698 { 1699 // User presses find/page button. 1700 SimpleEditDlgClass* pFindPageDlg; 1701 1702 // Ask user for user desired. 1703 Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT ); // Required before String_Pixel_Width() call, for god's sake. 1704 pFindPageDlg = new SimpleEditDlgClass( 400, TXT_WOL_PAGELOCATE, TXT_WOL_USERNAMEPROMPT, WOL_NAME_LEN_MAX ); 1705 pFindPageDlg->SetButtons( TXT_WOL_LOCATE, Text_String( TXT_CANCEL ), TXT_WOL_PAGE ); 1706 bPump_In_Call_Back = true; 1707 const char* szNameDlgResult = pFindPageDlg->Show(); 1708 bPump_In_Call_Back = false; 1709 1710 if( strcmp( szNameDlgResult, Text_String( TXT_CANCEL ) ) == 0 || !*pFindPageDlg->szEdit ) 1711 { 1712 delete pFindPageDlg; 1713 return; 1714 } 1715 1716 if( strcmp( szNameDlgResult, TXT_WOL_LOCATE ) == 0 ) 1717 { 1718 // Locate user. 1719 HRESULT hRes = Locate( pFindPageDlg->szEdit ); 1720 switch( hRes ) 1721 { 1722 case CHAT_S_FIND_NOTHERE: 1723 bPump_In_Call_Back = true; 1724 WWMessageBox().Process( TXT_WOL_FIND_NOTHERE ); 1725 bPump_In_Call_Back = false; 1726 break; 1727 case CHAT_S_FIND_NOCHAN: 1728 bPump_In_Call_Back = true; 1729 WWMessageBox().Process( TXT_WOL_FIND_NOCHAN ); 1730 bPump_In_Call_Back = false; 1731 break; 1732 case CHAT_S_FIND_OFF: 1733 bPump_In_Call_Back = true; 1734 WWMessageBox().Process( TXT_WOL_FIND_OFF ); 1735 bPump_In_Call_Back = false; 1736 break; 1737 case CHAT_E_TIMEOUT: 1738 bPump_In_Call_Back = true; 1739 WWMessageBox().Process( TXT_WOL_TIMEOUT ); 1740 bPump_In_Call_Back = false; 1741 break; 1742 case E_FAIL: 1743 GenericErrorMessage(); 1744 break; 1745 case S_OK: 1746 { 1747 char* szChannel = (char*)pChatSink->OnFindChannel.name; 1748 int iLobby = iChannelLobbyNumber( (unsigned char*)szChannel ); 1749 char* szFound; 1750 if( iLobby != -1 ) 1751 { 1752 char szLobbyName[ REASONABLELOBBYINTERPRETEDNAMELEN ]; 1753 InterpretLobbyNumber( szLobbyName, iLobby ); 1754 szFound = new char[ strlen( TXT_WOL_FOUNDIN ) + strlen( szLobbyName ) + 5 ]; 1755 sprintf( szFound, TXT_WOL_FOUNDIN, szLobbyName ); 1756 } 1757 else 1758 { 1759 szFound = new char[ strlen( TXT_WOL_FOUNDIN ) + strlen( szChannel ) + 5 ]; 1760 sprintf( szFound, TXT_WOL_FOUNDIN, szChannel ); 1761 } 1762 bPump_In_Call_Back = true; 1763 WWMessageBox().Process( szFound ); 1764 bPump_In_Call_Back = false; 1765 delete [] szFound; 1766 break; 1767 } 1768 } 1769 } 1770 else 1771 { 1772 // Page user. 1773 // Ask user for text to send. 1774 SimpleEditDlgClass* pMessDlg = new SimpleEditDlgClass( 600, TXT_WOL_PAGEMESSAGETITLE, 1775 TXT_WOL_PAGEMESSAGEPROMPT, MAXCHATSENDLENGTH ); 1776 bPump_In_Call_Back = true; 1777 if( strcmp( pMessDlg->Show(), Text_String( TXT_OK ) ) == 0 && *pMessDlg->szEdit ) 1778 { 1779 switch( Page( pFindPageDlg->szEdit, pMessDlg->szEdit, true ) ) 1780 { 1781 case CHAT_S_PAGE_NOTHERE: 1782 WWMessageBox().Process( TXT_WOL_PAGE_NOTHERE ); 1783 break; 1784 case CHAT_S_PAGE_OFF: 1785 WWMessageBox().Process( TXT_WOL_PAGE_OFF ); 1786 break; 1787 case CHAT_E_TIMEOUT: 1788 WWMessageBox().Process( TXT_WOL_TIMEOUT ); 1789 break; 1790 case E_FAIL: 1791 GenericErrorMessage(); 1792 break; 1793 case S_OK: 1794 char szMessage[ WOL_NAME_LEN_MAX + 30 ]; 1795 sprintf( szMessage, TXT_WOL_WASPAGED, pFindPageDlg->szEdit ); 1796 PrintMessage( szMessage, WOLCOLORREMAP_LOCALMACHINEMESS ); 1797 break; 1798 } 1799 } 1800 bPump_In_Call_Back = false; 1801 } 1802 1803 delete pFindPageDlg; 1804 } 1805 1806 //*********************************************************************************************** 1807 HRESULT WolapiObject::Locate( const char* szUser ) 1808 { 1809 // Returns HRESULT with possibly customized meanings. 1810 1811 char* szMessage = new char[ strlen( TXT_WOL_LOCATING ) + strlen( szUser ) + 5 ]; 1812 sprintf( szMessage, TXT_WOL_LOCATING, szUser ); 1813 WWMessageBox().Process( szMessage, TXT_NONE ); 1814 delete [] szMessage; 1815 1816 pChatSink->bRequestFindWait = true; 1817 1818 User userFind; 1819 strcpy( (char*)userFind.name, szUser ); 1820 1821 // debugprint( "RequestFind()\n" ); 1822 if( !SUCCEEDED( pChat->RequestFind( &userFind ) ) ) 1823 { 1824 // debugprint( "RequestFind() call failed\n" ); 1825 return 0; 1826 } 1827 DWORD dwTimeStart = timeGetTime(); 1828 DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 1829 while( pChatSink->bRequestFindWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) 1830 { 1831 while( timeGetTime() < dwTimeNextPump ) 1832 Call_Back(); 1833 pChat->PumpMessages(); 1834 // debugprint( ">Find pump\n" ); 1835 dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 1836 } 1837 1838 if( pChatSink->bRequestFindWait ) 1839 return CHAT_E_TIMEOUT; 1840 1841 return pChatSink->hresRequestFindResult; 1842 } 1843 1844 //*********************************************************************************************** 1845 HRESULT WolapiObject::Page( const char* szUser, const char* szSend, bool bWaitForResult ) 1846 { 1847 // Returns HRESULT with possibly customized meanings. 1848 1849 if( bWaitForResult ) 1850 { 1851 char* szMessage = new char[ strlen( TXT_WOL_PAGING ) + strlen( szUser ) + 5 ]; 1852 sprintf( szMessage, TXT_WOL_PAGING, szUser ); 1853 WWMessageBox().Process( szMessage, TXT_NONE ); 1854 delete [] szMessage; 1855 } 1856 1857 pChatSink->bRequestPageWait = true; 1858 1859 User userFind; 1860 strcpy( (char*)userFind.name, szUser ); 1861 1862 // debugprint( "RequestPage()\n" ); 1863 if( !SUCCEEDED( pChat->RequestPage( &userFind, szSend ) ) ) 1864 { 1865 // debugprint( "RequestPage() call failed\n" ); 1866 return 0; 1867 } 1868 if( !bWaitForResult ) 1869 return 0; 1870 DWORD dwTimeStart = timeGetTime(); 1871 DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 1872 while( pChatSink->bRequestPageWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) 1873 { 1874 while( timeGetTime() < dwTimeNextPump ) 1875 Call_Back(); 1876 pChat->PumpMessages(); 1877 // debugprint( ">Page pump\n" ); 1878 dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 1879 } 1880 1881 if( pChatSink->bRequestPageWait ) 1882 return CHAT_E_TIMEOUT; 1883 1884 return pChatSink->hresRequestPageResult; 1885 } 1886 1887 //*********************************************************************************************** 1888 void WolapiObject::DoKick( IconListClass* pILUsersOrPlayers, bool bAndBan ) 1889 { 1890 // Kick selected users. 1891 1892 if( CurrentLevel != WOL_LEVEL_INCHATCHANNEL && CurrentLevel != WOL_LEVEL_INLOBBY && CurrentLevel != WOL_LEVEL_INGAMECHANNEL ) 1893 { 1894 PrintMessage( TXT_WOL_YOURENOTINCHANNEL, WOLCOLORREMAP_LOCALMACHINEMESS ); 1895 Sound_Effect( WOLSOUND_ERROR ); 1896 } 1897 else if( !bChannelOwner ) 1898 { 1899 PrintMessage( TXT_WOL_ONLYOWNERCANKICK, WOLCOLORREMAP_LOCALMACHINEMESS ); 1900 Sound_Effect( WOLSOUND_ERROR ); 1901 } 1902 else 1903 { 1904 int iFound = 0; 1905 for( int i = 0; i < pILUsersOrPlayers->Count(); i++ ) 1906 { 1907 if( pILUsersOrPlayers->bItemIsMultiSelected( i ) ) 1908 { 1909 User* pUser = (User*)pILUsersOrPlayers->Get_Item_ExtraDataPtr( i ); 1910 if( pUser && strcmp( (char*)pUser->name, szMyName ) != 0 ) // Don't kick yourself. 1911 { 1912 Kick( pUser ); 1913 if( bAndBan ) 1914 Ban( pUser ); 1915 iFound++; 1916 if( iFound < 5 ) 1917 Sound_Effect( (VocType)( VOC_SCREAM1 + ( rand() % 9 ) ) ); 1918 } 1919 } 1920 } 1921 if( !iFound ) 1922 { 1923 PrintMessage( TXT_WOL_NOONETOKICK, WOLCOLORREMAP_LOCALMACHINEMESS ); 1924 Sound_Effect( WOLSOUND_ERROR ); 1925 } 1926 } 1927 } 1928 1929 //*********************************************************************************************** 1930 bool WolapiObject::Kick( User* pUserToKick ) 1931 { 1932 // Returns false if something terrible happens. 1933 // debugprint( "RequestUserKick()\n" ); 1934 if( !SUCCEEDED( pChat->RequestUserKick( pUserToKick ) ) ) 1935 { 1936 // debugprint( "RequestUserKick() call failed\n" ); 1937 return false; 1938 } 1939 return true; 1940 } 1941 1942 //*********************************************************************************************** 1943 bool WolapiObject::Ban( User* pUserToKick ) 1944 { 1945 // Returns false if something terrible happens. 1946 // debugprint( "RequestChannelBan()\n" ); 1947 if( !SUCCEEDED( pChat->RequestChannelBan( (char*)pUserToKick->name, true ) ) ) 1948 { 1949 // debugprint( "RequestUserKick() call failed\n" ); 1950 return false; 1951 } 1952 return true; 1953 } 1954 1955 //*********************************************************************************************** 1956 void WolapiObject::DoSquelch( IconListClass* pILUsersOrPlayers ) 1957 { 1958 // Squelch/unsquelch selected users. 1959 bool bFound = false; 1960 for( int i = 0; i < pILUsersOrPlayers->Count(); i++ ) 1961 { 1962 if( pILUsersOrPlayers->bItemIsMultiSelected( i ) ) 1963 { 1964 User* pUser = (User*)pILUsersOrPlayers->Get_Item_ExtraDataPtr( i ); 1965 if( pUser ) 1966 { 1967 if( strcmp( (char*)pUser->name, szMyName ) != 0 ) // Don't squelch yourself. 1968 { 1969 Squelch( pUser ); 1970 // char szMess[ 150 ]; 1971 // if( Squelch( pUser ) ) 1972 // sprintf( szMess, TXT_WOL_USERISSQUELCHED, (char*)pUser->name ); 1973 // else 1974 // sprintf( szMess, TXT_WOL_USERISNOTSQUELCHED, (char*)pUser->name ); 1975 // WOL_PrintMessage( chatlist, szMess, WOLCOLORREMAP_LOCALMACHINEMESS ); 1976 1977 bFound = true; 1978 pILUsersOrPlayers->Flag_To_Redraw(); 1979 } 1980 else 1981 PrintMessage( TXT_WOL_CANTSQUELCHSELF, WOLCOLORREMAP_LOCALMACHINEMESS ); 1982 } 1983 } 1984 } 1985 if( bFound ) 1986 { 1987 Sound_Effect( VOC_SQUISH ); 1988 ListChannelUsers(); // Refresh displayed user list. 1989 } 1990 } 1991 1992 //*********************************************************************************************** 1993 bool WolapiObject::Squelch( User* pUserToSquelch ) 1994 { 1995 // Returns true if user is now squelched, false if not squelched. 1996 // Sets User pointer flags value. 1997 // debugprint( "Squelch:: pUser is %i, flags is %i\n", pUserToSquelch, pUserToSquelch->flags ); 1998 1999 if( pUserToSquelch->flags & CHAT_USER_SQUELCHED ) 2000 { 2001 pChat->SetSquelch( pUserToSquelch, false ); 2002 pUserToSquelch->flags &= ~CHAT_USER_SQUELCHED; 2003 return false; 2004 } 2005 pChat->SetSquelch( pUserToSquelch, true ); 2006 pUserToSquelch->flags |= CHAT_USER_SQUELCHED; 2007 return true; 2008 } 2009 2010 //*********************************************************************************************** 2011 void WolapiObject::DoOptions() 2012 { 2013 // Show options dialog. 2014 bPump_In_Call_Back = true; 2015 WOL_Options_Dialog( this, false ); 2016 bPump_In_Call_Back = false; 2017 // Set trigger for an immediate channel list update, in case local lobby games filter was changed. 2018 dwTimeNextChannelUpdate = ::timeGetTime(); 2019 } 2020 2021 //*********************************************************************************************** 2022 bool WolapiObject::DoLadder() 2023 { 2024 bPump_In_Call_Back = true; 2025 if( WWMessageBox().Process( TXT_WOL_LADDERSHELL, TXT_YES, TXT_NO ) == 0 ) 2026 { 2027 bPump_In_Call_Back = false; 2028 #ifdef ENGLISH 2029 //return SpawnBrowser( "http://www.westwood.com/ra_ladders.html" ); 2030 return SpawnBrowser( "http://www.westwood.com/westwoodonline/tournaments/redalert/index.html" ); 2031 #else 2032 #ifdef GERMAN 2033 // return SpawnBrowser( "http://www.westwood.com/ra_ladders_german.html" ); 2034 return SpawnBrowser( "http://www.westwood.com/westwoodonline/tournaments/redalert/index.html" ); 2035 #else 2036 // return SpawnBrowser( "http://www.westwood.com/ra_ladders_french.html" ); 2037 return SpawnBrowser( "http://www.westwood.com/westwoodonline/tournaments/redalert/index.html" ); 2038 #endif 2039 #endif 2040 } 2041 2042 bPump_In_Call_Back = false; 2043 return false; 2044 } 2045 2046 //*********************************************************************************************** 2047 bool WolapiObject::DoHelp() 2048 { 2049 bPump_In_Call_Back = true; 2050 if( WWMessageBox().Process( TXT_WOL_HELPSHELL, TXT_YES, TXT_NO ) == 0 ) 2051 { 2052 bPump_In_Call_Back = false; 2053 const char* szURL; 2054 if( pChat->GetHelpURL( &szURL ) == S_OK ) 2055 return SpawnBrowser( szURL ); 2056 GenericErrorMessage(); 2057 } 2058 2059 bPump_In_Call_Back = false; 2060 return false; 2061 } 2062 2063 //*********************************************************************************************** 2064 bool WolapiObject::DoWebRegistration() 2065 { 2066 // Get the executable name from the registry. 2067 HKEY hKey; 2068 if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, "SOFTWARE\\Westwood\\Register", 0, KEY_READ, &hKey ) != ERROR_SUCCESS ) 2069 { 2070 GenericErrorMessage(); 2071 return false; 2072 } 2073 char szPath[ _MAX_PATH + 1 ]; 2074 DWORD dwBufSize = _MAX_PATH; 2075 if( RegQueryValueEx( hKey, "InstallPath", 0, NULL, (LPBYTE)szPath, &dwBufSize ) != ERROR_SUCCESS ) 2076 { 2077 GenericErrorMessage(); 2078 return false; 2079 } 2080 RegCloseKey( hKey ); 2081 // debugprint( "Registration app is '%s'\n", szPath ); 2082 2083 bPump_In_Call_Back = true; 2084 if( WWMessageBox().Process( TXT_WOL_WEBREGISTRATIONSHELL, TXT_YES, TXT_NO ) == 0 ) 2085 { 2086 bPump_In_Call_Back = false; 2087 ::ShellExecute( NULL, "open", szPath, NULL, ".", SW_SHOW ); 2088 return true; 2089 } 2090 2091 bPump_In_Call_Back = false; 2092 return false; 2093 } 2094 2095 //*********************************************************************************************** 2096 bool WolapiObject::DoGameAdvertising( const Channel* pChannel ) 2097 { 2098 const char* szURL = URLForGameType( pChannel->type ); 2099 if( !szURL ) 2100 { 2101 GenericErrorMessage(); 2102 return false; 2103 } 2104 2105 char szQuestion[512]; 2106 sprintf( szQuestion, TXT_WOL_GAMEADVERTSHELL, NameOfGameType( pChannel->type ) ); 2107 bPump_In_Call_Back = true; 2108 if( WWMessageBox().Process( szQuestion, TXT_YES, TXT_NO ) == 0 ) 2109 { 2110 bPump_In_Call_Back = false; 2111 return SpawnBrowser( szURL ); 2112 } 2113 2114 bPump_In_Call_Back = false; 2115 return false; 2116 } 2117 2118 //*********************************************************************************************** 2119 bool WolapiObject::SpawnBrowser( const char* szURL ) 2120 { 2121 // Attempts to launch user's web browser, and monitors it, waiting for user to close it, at which 2122 // point we bring focus back to the game. 2123 2124 // Loosely based on Dune2000 example. 2125 2126 bool bSuccess = false; 2127 STARTUPINFO si; 2128 PROCESS_INFORMATION pi; 2129 2130 ZeroMemory( &si, sizeof(si) ); 2131 si.cb = sizeof(si); 2132 2133 if( *szWebBrowser ) 2134 { 2135 char szCommandLine[ _MAX_PATH + 300 ]; 2136 sprintf( szCommandLine, "\"%s\" %s", szWebBrowser, szURL ); 2137 // debugprint( "About to CreateProcess: '%s'\n", szCommandLine ); 2138 Hide_Mouse(); 2139 BlackPalette.Set( FADE_PALETTE_FAST, Call_Back ); 2140 // ::ShowWindow( MainWindow, SW_SHOWMINIMIZED ); 2141 SeenPage.Clear(); 2142 if( ::CreateProcess( NULL, 2143 szCommandLine, // Command line. 2144 NULL, // Process handle not inheritable. 2145 NULL, // Thread handle not inheritable. 2146 FALSE, // Set handle inheritance to FALSE. 2147 0, // No creation flags. 2148 NULL, // Use parent’s environment block. 2149 NULL, // Use parent’s starting directory. 2150 &si, // Pointer to STARTUPINFO structure. 2151 &pi ) ) // Pointer to PROCESS_INFORMATION structure. 2152 { 2153 if( pi.hProcess ) 2154 { 2155 // debugprint( "CreateProcess: '%s'\n", szCommandLine ); 2156 bSuccess = true; 2157 ::WaitForInputIdle( pi.hProcess, 5000 ); 2158 bPump_In_Call_Back = true; 2159 for( ; ; ) 2160 { 2161 DWORD dwActive; 2162 Call_Back(); 2163 Sleep( 200 ); 2164 ::GetExitCodeProcess( pi.hProcess, &dwActive ); 2165 if( dwActive != STILL_ACTIVE || cancel_current_msgbox ) 2166 { 2167 // Either user closed the browser app, or game is starting and we should return focus to game. 2168 cancel_current_msgbox = false; 2169 ::SetForegroundWindow( MainWindow ); 2170 ::ShowWindow( MainWindow, SW_RESTORE ); 2171 break; 2172 } 2173 if( ::GetTopWindow( NULL ) == MainWindow ) 2174 { 2175 ::ShowWindow( MainWindow, SW_RESTORE ); // In case it was topmost but minimized. 2176 break; 2177 } 2178 } 2179 bPump_In_Call_Back = false; 2180 GamePalette.Set( FADE_PALETTE_FAST, Call_Back ); 2181 Show_Mouse(); 2182 } 2183 } 2184 } 2185 2186 if( !bSuccess ) 2187 { 2188 // This was the old way - does not pop you back into game when finished... 2189 if( (int)::ShellExecute( NULL, NULL, szURL, NULL, ".", SW_SHOW ) <= 32 ) 2190 { 2191 // debugprint( "ShellExecute\n" ); 2192 // ShellExecute failed as well. Just print a message instead. 2193 GamePalette.Set(); 2194 ::ShowWindow( MainWindow, SW_RESTORE ); 2195 char szError[ 300 ]; 2196 sprintf( szError, TXT_WOL_CANTLAUNCHBROWSER, szURL ); 2197 Show_Mouse(); 2198 WWMessageBox().Process( szError ); 2199 return false; 2200 } 2201 // (We return immediately after launching in this case.) 2202 GamePalette.Set(); 2203 Show_Mouse(); 2204 } 2205 return true; 2206 } 2207 2208 //*********************************************************************************************** 2209 void WolapiObject::ChannelListTitle( const char* szTitle ) 2210 { 2211 strcpy( szChannelListTitle, szTitle ); 2212 bChannelListTitleUpdated = true; 2213 } 2214 2215 //*********************************************************************************************** 2216 bool WolapiObject::EnterLevel_Top() 2217 { 2218 // <Showing the top level choices.> 2219 2220 // debugprint( "*** EnterLevel_Top\n" ); 2221 // (Might as well hardcode the channels tree.) 2222 2223 ChannelListTitle( TXT_WOL_TOPLEVELTITLE ); 2224 pILChannels->Clear(); 2225 //void* pTopIcon = (void*)DibIconInfos[ DIBICON_ACCEPT ].pDIB; 2226 void* pTopIcon = IconForGameType( 0 ); 2227 pILChannels->Add_Item( TXT_WOL_OFFICIALCHAT, CHANNELTYPE_OFFICIALCHAT, pTopIcon, ICON_DIB, CHANNELTYPE_OFFICIALCHAT ); 2228 pILChannels->Add_Item( TXT_WOL_USERCHAT, CHANNELTYPE_USERCHAT, pTopIcon, ICON_DIB, CHANNELTYPE_USERCHAT ); 2229 pILChannels->Add_Item( TXT_WOL_GAMECHANNELS, CHANNELTYPE_GAMES, pTopIcon, ICON_DIB, CHANNELTYPE_GAMES ); 2230 2231 // Set wol buttons enabled/disabled. 2232 pShpBtnLeave->Disable(); 2233 pShpBtnRefresh->Disable(); 2234 pShpBtnSquelch->Disable(); 2235 pShpBtnBan->Disable(); 2236 pShpBtnKick->Disable(); 2237 2238 CurrentLevel = WOL_LEVEL_TOP; 2239 2240 return true; 2241 } 2242 2243 //*********************************************************************************************** 2244 bool WolapiObject::EnterLevel_OfficialChat() 2245 { 2246 // <Showing available official chat channels.> 2247 2248 // debugprint( "*** EnterLevel_OfficialChat\n" ); 2249 // (Might as well hardcode the channels tree.) 2250 2251 CurrentLevel = WOL_LEVEL_OFFICIALCHAT; 2252 if( !UpdateChannels( 0, CHANNELFILTER_OFFICIAL, false ) ) 2253 { 2254 GenericErrorMessage(); 2255 return false; 2256 } 2257 2258 ChannelListTitle( TXT_WOL_OFFICIALCHAT ); 2259 pILChannels->Clear(); 2260 pILChannels->Add_Item( TXT_WOL_CHANNELLISTLOADING, CHANNELTYPE_LOADING, NULL, ICON_SHAPE, CHANNELTYPE_LOADING ); 2261 dwTimeNextChannelUpdate = ::timeGetTime(); // Set trigger for an immediate channel list update. 2262 2263 // Set wol buttons enabled/disabled. 2264 pShpBtnLeave->Disable(); 2265 pShpBtnRefresh->Enable(); 2266 pShpBtnSquelch->Disable(); 2267 pShpBtnBan->Disable(); 2268 pShpBtnKick->Disable(); 2269 2270 return true; 2271 } 2272 2273 //*********************************************************************************************** 2274 bool WolapiObject::EnterLevel_UserChat() 2275 { 2276 // <Showing available user chat channels.> 2277 2278 // debugprint( "*** EnterLevel_UserChat\n" ); 2279 // (Might as well hardcode the channels tree.) 2280 2281 CurrentLevel = WOL_LEVEL_USERCHAT; 2282 if( !UpdateChannels( 0, CHANNELFILTER_UNOFFICIAL, false ) ) 2283 { 2284 GenericErrorMessage(); 2285 return false; 2286 } 2287 2288 ChannelListTitle( TXT_WOL_USERCHAT ); 2289 pILChannels->Clear(); 2290 pILChannels->Add_Item( TXT_WOL_CHANNELLISTLOADING, CHANNELTYPE_LOADING, NULL, ICON_SHAPE, CHANNELTYPE_LOADING ); 2291 dwTimeNextChannelUpdate = ::timeGetTime(); // Set trigger for an immediate channel list update. 2292 2293 // Set wol buttons enabled/disabled. 2294 pShpBtnLeave->Disable(); 2295 pShpBtnRefresh->Enable(); 2296 pShpBtnSquelch->Disable(); 2297 pShpBtnBan->Disable(); 2298 pShpBtnKick->Disable(); 2299 2300 return true; 2301 } 2302 2303 //*********************************************************************************************** 2304 bool WolapiObject::EnterLevel_Games() 2305 { 2306 // <Showing each westwood game type.> 2307 2308 // debugprint( "*** EnterLevel_Games\n" ); 2309 // (Might as well hardcode the channels tree.) 2310 2311 CurrentLevel = WOL_LEVEL_GAMES; 2312 2313 ChannelListTitle( TXT_WOL_GAMECHANNELS ); 2314 pILChannels->Clear(); 2315 pILChannels->Add_Item( TXT_WOL_CHANNEL_TOP, CHANNELTYPE_TOP, NULL, ICON_SHAPE, CHANNELTYPE_TOP ); 2316 2317 // Create entry for our lobbies at the top. 2318 bool bFound = false; 2319 // (There are actually 2 additional game types at the end of GameTypeInfos - for ws icon and wwonline icon.) 2320 for( int i = 0; i < nGameTypeInfos - 2; i++ ) 2321 { 2322 if( GameTypeInfos[ i ].iGameType == GAME_TYPE ) 2323 { 2324 //pILChannels->Add_Item( GameTypeInfos[ i ].szName, CHANNELTYPE_LOBBIES, (void*)GameTypeInfos[ i ].pDIB, ICON_DIB, CHANNELTYPE_LOBBIES ); 2325 pILChannels->Add_Item( TXT_WOL_REDALERTLOBBIES, CHANNELTYPE_LOBBIES, (void*)GameTypeInfos[ i ].pDIB, ICON_DIB, CHANNELTYPE_LOBBIES ); 2326 bFound = true; 2327 break; 2328 } 2329 } 2330 if( !bFound ) 2331 { 2332 // In the production version, this should never happen, as there should always be a gametypeinfo created that matches 2333 // our game type. It depends on the recentness of the WOR file accompanying the wolapi.dll. 2334 pILChannels->Add_Item( TXT_WOL_REDALERTLOBBIES, CHANNELTYPE_LOBBIES, (void*)OldRAGameTypeInfos[ 0 ].pDIB, ICON_DIB, CHANNELTYPE_LOBBIES ); 2335 } 2336 2337 // A pointer to the GameTypeInfos entry is stored in the item for convenience later. 2338 for( i = 0; i < nGameTypeInfos - 2; i++ ) 2339 { 2340 int iType = GameTypeInfos[ i ].iGameType; 2341 if( iType != GAME_TYPE ) // Else it is our game - skip it here since we put it at the top. 2342 { 2343 if( iType != 2 && iType != 3 && iType != 4 ) // Hack needed for the time being, to prevent the old ra games from being seen. 2344 { 2345 char szHelp[ 200 ]; 2346 sprintf( szHelp, TXT_WOL_TTIP_CHANNELTYPE_GAMESOFTYPE, GameTypeInfos[ i ].szName ); 2347 pILChannels->Add_Item( GameTypeInfos[ i ].szName, szHelp, (void*)GameTypeInfos[ i ].pDIB, ICON_DIB, CHANNELTYPE_GAMESOFTYPE, (void*)&GameTypeInfos[ i ] ); 2348 } 2349 } 2350 } 2351 2352 // Set wol buttons enabled/disabled. 2353 pShpBtnLeave->Disable(); 2354 pShpBtnRefresh->Disable(); 2355 pShpBtnSquelch->Disable(); 2356 pShpBtnBan->Disable(); 2357 pShpBtnKick->Disable(); 2358 2359 return true; 2360 } 2361 2362 //*********************************************************************************************** 2363 bool WolapiObject::EnterLevel_GamesOfType( WOL_GAMETYPEINFO* pGameTypeInfo ) 2364 { 2365 // <Showing current game channels of a specific type - not our own game type.> 2366 2367 // debugprint( "*** EnterLevel_GamesOfType: pGameTypeInfo->szName %s, iGameType %i, URL %s\n", pGameTypeInfo->szName, pGameTypeInfo->iGameType, pGameTypeInfo->szURL ); 2368 2369 CurrentLevel = WOL_LEVEL_GAMESOFTYPE; 2370 if( !UpdateChannels( pGameTypeInfo->iGameType, CHANNELFILTER_NO, true ) ) 2371 { 2372 GenericErrorMessage(); 2373 return false; 2374 } 2375 2376 ChannelListTitle( pGameTypeInfo->szName ); 2377 pILChannels->Clear(); 2378 pILChannels->Add_Item( TXT_WOL_CHANNELLISTLOADING, CHANNELTYPE_LOADING, NULL, ICON_SHAPE, CHANNELTYPE_LOADING ); 2379 dwTimeNextChannelUpdate = ::timeGetTime(); // Set trigger for an immediate channel list update. 2380 2381 // Set wol buttons enabled/disabled. 2382 pShpBtnLeave->Disable(); 2383 pShpBtnRefresh->Enable(); 2384 pShpBtnSquelch->Disable(); 2385 pShpBtnBan->Disable(); 2386 pShpBtnKick->Disable(); 2387 2388 return true; 2389 } 2390 2391 //*********************************************************************************************** 2392 bool WolapiObject::EnterLevel_Lobbies() 2393 { 2394 // <Showing available lobbies.> 2395 2396 // debugprint( "*** EnterLevel_Lobbies\n" ); 2397 2398 CurrentLevel = WOL_LEVEL_LOBBIES; 2399 if( !UpdateChannels( 0, CHANNELFILTER_LOBBIES, false ) ) 2400 { 2401 GenericErrorMessage(); 2402 return false; 2403 } 2404 2405 ChannelListTitle( TXT_WOL_REDALERTLOBBIES ); 2406 pILChannels->Clear(); 2407 pILChannels->Add_Item( TXT_WOL_CHANNELLISTLOADING, CHANNELTYPE_LOADING, NULL, ICON_SHAPE, CHANNELTYPE_LOADING ); 2408 dwTimeNextChannelUpdate = ::timeGetTime(); // Set trigger for an immediate channel list update. 2409 2410 // Set wol buttons enabled/disabled. 2411 pShpBtnLeave->Disable(); 2412 pShpBtnRefresh->Enable(); 2413 pShpBtnSquelch->Disable(); 2414 pShpBtnBan->Disable(); 2415 pShpBtnKick->Disable(); 2416 2417 return true; 2418 } 2419 2420 //*********************************************************************************************** 2421 bool WolapiObject::OnEnteringChatChannel( const char* szChannelName, bool bICreatedChannel, int iLobby ) 2422 { 2423 // Called when a chat channel (or lobby) has been successfully joined. 2424 // debugprint( "*** OnEnteringChatChannel '%s'\n", szChannelName ); 2425 2426 // // Block until we have a userlist. - Not necessary - always comes immediately following OnJoin. 2427 // if( !UserList() ) 2428 // return false; 2429 2430 // Request ladders if this is a lobby. 2431 if( iLobby != -1 ) 2432 RequestLadders( NULL ); 2433 2434 // Set channels list. 2435 pILChannels->Clear(); 2436 // Add a "return" choice at the top of the channel list, based on where we want to go 'back' to... 2437 if( iLobby == -1 ) 2438 { 2439 switch( CurrentLevel ) 2440 { 2441 case WOL_LEVEL_OFFICIALCHAT: 2442 pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_OFFICIALCHAT, NULL, ICON_SHAPE, CHANNELTYPE_OFFICIALCHAT ); 2443 break; 2444 case WOL_LEVEL_USERCHAT: 2445 pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_USERCHAT, NULL, ICON_SHAPE, CHANNELTYPE_USERCHAT ); 2446 break; 2447 default: 2448 // If entering a channel from anywhere else, user must have created the channel. 2449 // Make "back" take them to user channels list. 2450 // if( bICreatedChannel ) // ajw just verifying 2451 pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_USERCHAT, NULL, ICON_SHAPE, CHANNELTYPE_USERCHAT ); 2452 /* else 2453 { 2454 // debugprint( "Case that should not occur in OnEnteringChatChannel. CurrentLevel %i\n", CurrentLevel ); 2455 pILChannels->Add_Item( "ERROR in OnEnteringChatChannel", NULL, NULL, ICON_SHAPE, CHANNELTYPE_TOP ); 2456 } 2457 */ 2458 break; 2459 } 2460 } 2461 else 2462 { 2463 pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_LOBBIES, NULL, ICON_SHAPE, CHANNELTYPE_LOBBIES ); 2464 } 2465 2466 char* szMess; 2467 if( iLobby == -1 ) 2468 { 2469 CurrentLevel = WOL_LEVEL_INCHATCHANNEL; 2470 szMess = new char[ strlen( TXT_WOL_YOUJOINED ) + strlen( szChannelName ) + 5 ]; 2471 sprintf( szMess, TXT_WOL_YOUJOINED, szChannelName ); 2472 ChannelListTitle( szChannelName ); 2473 } 2474 else 2475 { 2476 CurrentLevel = WOL_LEVEL_INLOBBY; 2477 char szLobbyName[ REASONABLELOBBYINTERPRETEDNAMELEN ]; 2478 InterpretLobbyNumber( szLobbyName, iLobby ); 2479 szMess = new char[ strlen( TXT_WOL_YOUJOINEDLOBBY ) + REASONABLELOBBYINTERPRETEDNAMELEN + 10 ]; 2480 sprintf( szMess, TXT_WOL_YOUJOINEDLOBBY, szLobbyName ); 2481 ChannelListTitle( szLobbyName ); 2482 iLobbyLast = iLobby; 2483 dwTimeNextChannelUpdate = ::timeGetTime(); // Set trigger for an immediate channel list update. 2484 } 2485 2486 strcpy( szChannelNameCurrent, szChannelName ); 2487 2488 bChannelOwner = bICreatedChannel; 2489 2490 // Set users list. 2491 ListChannelUsers(); 2492 2493 PrintMessage( szMess, WOLCOLORREMAP_LOCALMACHINEMESS ); 2494 delete [] szMess; 2495 2496 Sound_Effect( WOLSOUND_ENTERCHAN ); 2497 2498 // Set wol buttons enabled/disabled. 2499 pShpBtnLeave->Enable(); 2500 if( CurrentLevel == WOL_LEVEL_INLOBBY ) 2501 pShpBtnRefresh->Enable(); 2502 else 2503 pShpBtnRefresh->Disable(); 2504 pShpBtnSquelch->Enable(); 2505 if( bChannelOwner ) 2506 { 2507 pShpBtnBan->Enable(); 2508 pShpBtnKick->Enable(); 2509 } 2510 else 2511 { 2512 pShpBtnBan->Disable(); 2513 pShpBtnKick->Disable(); 2514 } 2515 2516 return true; 2517 } 2518 2519 //*********************************************************************************************** 2520 void WolapiObject::OnExitingChatChannel() 2521 { 2522 // Called when we successfully ExitChannel, or we get kicked out. (Lobbies included.) 2523 2524 // Clear users list. 2525 pILUsers->Clear(); 2526 if( pStaticUsers ) 2527 pStaticUsers->Set_Text( TXT_WOL_NOUSERLIST ); 2528 2529 // debugprint( "*** OnExitingChatChannel() - szChannelNameCurrent '%s', CurrentLevel %i\n", szChannelNameCurrent, CurrentLevel ); 2530 int iLobby = iChannelLobbyNumber( (unsigned char*)szChannelNameCurrent ); 2531 char* szMess; 2532 if( iLobby == -1 ) 2533 { 2534 szMess = new char[ strlen( TXT_WOL_YOULEFT ) + strlen( szChannelNameCurrent ) + 5 ]; 2535 sprintf( szMess, TXT_WOL_YOULEFT, szChannelNameCurrent ); 2536 } 2537 else 2538 { 2539 // Channel is a lobby. 2540 char szLobbyName[ REASONABLELOBBYINTERPRETEDNAMELEN ]; 2541 InterpretLobbyNumber( szLobbyName, iLobby ); 2542 szMess = new char[ strlen( TXT_WOL_YOULEFTLOBBY ) + REASONABLELOBBYINTERPRETEDNAMELEN + 10 ]; 2543 sprintf( szMess, TXT_WOL_YOULEFTLOBBY, szLobbyName ); 2544 } 2545 PrintMessage( szMess, WOLCOLORREMAP_LOCALMACHINEMESS ); 2546 delete [] szMess; 2547 2548 *szChannelNameCurrent = 0; 2549 CurrentLevel = WOL_LEVEL_INVALID; 2550 2551 Sound_Effect( WOLSOUND_EXITCHAN ); 2552 } 2553 2554 //*********************************************************************************************** 2555 bool WolapiObject::ExitChatChannelForGameChannel() 2556 { 2557 // We are about to try and join/create a game channel, and are currently in a chat channel. 2558 2559 // Save this channel name, so we can come back to it if game channel join/create fails. 2560 strcpy( szChannelReturnOnGameEnterFail, szChannelNameCurrent ); 2561 2562 if( !ChannelLeave() ) 2563 { 2564 GenericErrorMessage(); 2565 return false; 2566 } 2567 return true; 2568 } 2569 2570 //*********************************************************************************************** 2571 bool WolapiObject::OnEnteringGameChannel( const char* szChannelName, bool bICreatedChannel, 2572 const CREATEGAMEINFO& CreateGameInfo ) 2573 { 2574 // Called when a game channel has been successfully joined, while still in chat dialog, 2575 // before game dialog has been created. 2576 // CreateGameInfo is copied to GameInfoCurrent, so that we know what kind of a game we're in during setup. 2577 2578 // debugprint( "*** OnEnteringGameChannel() - %s\n", szChannelName ); 2579 2580 CurrentLevel = WOL_LEVEL_INGAMECHANNEL; 2581 strcpy( szChannelNameCurrent, szChannelName ); 2582 2583 bChannelOwner = bICreatedChannel; 2584 // GameKindCurrent = GameKind; 2585 GameInfoCurrent = CreateGameInfo; 2586 strcpy( GameInfoCurrent.szPassword, CreateGameInfo.szPassword ); 2587 2588 // Remove shared buttons from wolchat's command list. 2589 pShpBtnDiscon->Zap(); 2590 pShpBtnLeave->Zap(); 2591 pShpBtnRefresh->Zap(); 2592 pShpBtnSquelch->Zap(); 2593 pShpBtnBan->Zap(); 2594 pShpBtnKick->Zap(); 2595 pShpBtnFindpage->Zap(); 2596 pShpBtnOptions->Zap(); 2597 pShpBtnLadder->Zap(); 2598 pShpBtnHelp->Zap(); 2599 2600 // Set wol buttons enabled/disabled. 2601 pShpBtnLeave->Enable(); 2602 pShpBtnRefresh->Disable(); 2603 pShpBtnSquelch->Enable(); 2604 if( bChannelOwner ) 2605 { 2606 pShpBtnBan->Enable(); 2607 pShpBtnKick->Enable(); 2608 } 2609 else 2610 { 2611 pShpBtnBan->Disable(); 2612 pShpBtnKick->Disable(); 2613 } 2614 2615 if( CreateGameInfo.GameKind == CREATEGAMEINFO::AMGAME ) 2616 { 2617 if( bShowRankRA ) 2618 { 2619 // Switch to "show AM rankings" mode. 2620 bShowRankRA = false; 2621 bMyRecordUpdated = true; 2622 bShowRankUpdated = true; 2623 } 2624 } 2625 else 2626 { 2627 if( !bShowRankRA ) 2628 { 2629 // Switch to "show RA rankings" mode. 2630 bShowRankRA = true; 2631 bMyRecordUpdated = true; 2632 bShowRankUpdated = true; 2633 } 2634 } 2635 2636 return true; 2637 } 2638 2639 //*********************************************************************************************** 2640 bool WolapiObject::OnEnteringGameSetup() 2641 { 2642 // Called when entering the game setup screen. Controls are initialized. OnEnteringGameChannel 2643 // has just been called earlier. 2644 2645 // Returns false only if we find there is not host - he must have simultaneously left. 2646 2647 // // Block until we have a userlist. - Not necessary - always comes immediately following OnJoin. 2648 // if( !UserList() ) 2649 // return false; 2650 2651 // Request ladders. 2652 RequestLadders( NULL ); 2653 2654 // Request IP addresses. 2655 RequestIPs( NULL ); 2656 2657 // Set users list. 2658 if( !ListChannelUsers() ) 2659 { 2660 // No host was found currently in channel! 2661 return false; 2662 } 2663 2664 if( !pGSupDlg->bHost ) 2665 { 2666 char* szMess = new char[ strlen( TXT_WOL_YOUJOINEDGAME ) + WOL_NAME_LEN_MAX + 5 ]; 2667 char szHostName[ WOL_NAME_LEN_MAX ]; 2668 HostNameFromGameChannelName( szHostName, szChannelNameCurrent ); 2669 sprintf( szMess, TXT_WOL_YOUJOINEDGAME, szHostName ); 2670 PrintMessage( szMess, WOLCOLORREMAP_LOCALMACHINEMESS ); 2671 delete [] szMess; 2672 } 2673 else 2674 PrintMessage( TXT_WOL_YOUCREATEDGAME, WOLCOLORREMAP_LOCALMACHINEMESS ); 2675 2676 return true; 2677 } 2678 2679 //*********************************************************************************************** 2680 void WolapiObject::OnFailedToEnterGameChannel() 2681 { 2682 if( *szChannelReturnOnGameEnterFail == 0 ) 2683 return; 2684 2685 // This is called when we fail to join/create a game channel. 2686 *szChannelNameCurrent = 0; 2687 2688 // Because we don't save the channel key as well, assume the usual lobby password. If we fail, we'll return to top level. 2689 HRESULT hRes = ChannelJoin( szChannelReturnOnGameEnterFail, LOBBYPASSWORD ); 2690 switch( hRes ) 2691 { 2692 case S_OK: 2693 OnEnteringChatChannel( szChannelReturnOnGameEnterFail, false, iChannelLobbyNumber( (unsigned char*)szChannelReturnOnGameEnterFail ) ); 2694 break; 2695 default: 2696 // ChannelJoin returned fail value. 2697 // (Now only applies if you could ever enter a game channel from a non-lobby.) 2698 // There is the possibility that the channel we were in disappeared in the instant between leaving it and 2699 // failing to join the game channel. <sigh> Or, the channel has a password, that we didn't record. In either 2700 // case, go back to the top level. 2701 GenericErrorMessage(); 2702 EnterLevel_Top(); 2703 } 2704 } 2705 2706 //*********************************************************************************************** 2707 void WolapiObject::OnExitingGameChannel() 2708 { 2709 // This is called after we leave a game channel, while still in the game setup dialog. 2710 2711 // Remove shared buttons from wolgsup's command list. 2712 pShpBtnDiscon->Zap(); 2713 pShpBtnLeave->Zap(); 2714 pShpBtnRefresh->Zap(); 2715 pShpBtnSquelch->Zap(); 2716 pShpBtnBan->Zap(); 2717 pShpBtnKick->Zap(); 2718 pShpBtnFindpage->Zap(); 2719 pShpBtnOptions->Zap(); 2720 pShpBtnLadder->Zap(); 2721 pShpBtnHelp->Zap(); 2722 2723 CurrentLevel = WOL_LEVEL_INVALID; 2724 *szChannelNameCurrent = 0; 2725 } 2726 2727 //*********************************************************************************************** 2728 void WolapiObject::RejoinLobbyAfterGame() 2729 { 2730 // Called to rejoin lobby after EITHER a game, or the game setup dialog. 2731 //debugprint( "RejoinLobbyAfterGame, iLobbyReturnAfterGame is %i\n", iLobbyReturnAfterGame ); 2732 2733 if( iLobbyReturnAfterGame == -1 ) 2734 { 2735 // Will never happen presumably, if games are always entered via a lobby chat channel. 2736 // We will naturally reenter the top level. 2737 } 2738 else 2739 { 2740 char szChannelToJoin[ WOL_CHANNAME_LEN_MAX ]; 2741 //sprintf( szChannelToJoin, "Lob_%i_%i", GAME_TYPE, iLobbyReturnAfterGame ); 2742 sprintf( szChannelToJoin, "%s%i", LOB_PREFIX, iLobbyReturnAfterGame ); 2743 //debugprint( "RejoinLobbyAfterGame, channel is %s\n", szChannelToJoin ); 2744 2745 HRESULT hRes = ChannelJoin( szChannelToJoin, LOBBYPASSWORD ); 2746 switch( hRes ) 2747 { 2748 case S_OK: 2749 //OnEnteringChatChannel( szChannelToJoin, false ); Done automatically now in wol_chat. 2750 break; 2751 default: 2752 // Something went wrong when trying to rejoin the lobby we were in. 2753 // We'll go back to the top level instead, which happens automatically if we do this... 2754 iLobbyReturnAfterGame = -1; 2755 break; 2756 } 2757 } 2758 } 2759 2760 //*********************************************************************************************** 2761 bool WolapiObject::RequestLadders( const char* szName ) 2762 { 2763 // If szName is NULL, calls RequestLadderList() until all ladder structs for all users in pChatSink's current 2764 // list have been asked for. Does not wait for results - these come in asynchronously. The previous list is 2765 // erased before new results come in. 2766 // If szName is valid, asks for specific name only. Result is appended to current ladder list. 2767 // This function does not block. 2768 2769 if( szName && *szName ) 2770 { 2771 // debugprint( "RequestLadderList( %s )\n", szName ); 2772 if( !SUCCEEDED( pNetUtil->RequestLadderList( szLadderServerHost, iLadderServerPort, szName, LADDER_CODE_RA, -1, 0, 0 ) ) ) 2773 { 2774 // debugprint( "RequestLadderList() call failed\n" ); 2775 return false; 2776 } 2777 if( !SUCCEEDED( pNetUtil->RequestLadderList( szLadderServerHost, iLadderServerPort, szName, LADDER_CODE_AM, -1, 0, 0 ) ) ) 2778 { 2779 // debugprint( "RequestLadderList() call failed\n" ); 2780 return false; 2781 } 2782 return true; 2783 } 2784 2785 char szNames[ ( WOL_NAME_LEN_MAX + 1 ) * 30 ]; // Neal says max is actually 25 names requested at once. Do 24... 2786 2787 pNetUtilSink->DeleteLadderList(); 2788 2789 // Do not request more than this number of times, to prevent overloads to ladder server. 2790 // If we have that many people in the channel, forget about doing ladders for all of them. 2791 // Probably this will never come into play (except while testing), because lobbies will be limited in # of users. 2792 int iCallLimit = 4; 2793 2794 User* pUser = pChatSink->pUserList; 2795 while( pUser ) 2796 { 2797 // Reset names string. 2798 *szNames = 0; 2799 // Get 24 users from list and add names to string. 2800 for( int i = 0; i != 24; ++i ) 2801 { 2802 strcat( szNames, (char*)pUser->name ); 2803 strcat( szNames, ":" ); 2804 pUser = pUser->next; 2805 if( !pUser ) 2806 break; 2807 } 2808 // Remove last colon. 2809 szNames[ strlen( szNames ) - 1 ] = 0; 2810 2811 // debugprint( "RequestLadderList( %s )\n", szNames ); 2812 if( !SUCCEEDED( pNetUtil->RequestLadderList( szLadderServerHost, iLadderServerPort, szNames, LADDER_CODE_RA, -1, 0, 0 ) ) ) 2813 { 2814 // debugprint( "RequestLadderList() call failed\n" ); 2815 return false; 2816 } 2817 if( !SUCCEEDED( pNetUtil->RequestLadderList( szLadderServerHost, iLadderServerPort, szNames, LADDER_CODE_AM, -1, 0, 0 ) ) ) 2818 { 2819 // debugprint( "RequestLadderList() call failed\n" ); 2820 return false; 2821 } 2822 if( --iCallLimit == 0 ) 2823 return false; 2824 } 2825 return true; 2826 } 2827 2828 //*********************************************************************************************** 2829 bool WolapiObject::RequestIPs( const char* szName ) 2830 { 2831 // If szName is NULL, calls RequestUserIP() until IPs for all users in pChatSink's current 2832 // list have been asked for. Does not wait for results - these come in asynchronously. The previous list is 2833 // erased before new results come in. 2834 // If szName is valid, asks for specific name only. Result is appended to current IP list. 2835 // This function does not block. 2836 2837 if( szName && *szName ) 2838 { 2839 User user; 2840 strcpy( (char*)user.name, szName ); 2841 // debugprint( "RequestUserIP( %s )\n", szName ); 2842 if( !SUCCEEDED( pChat->RequestUserIP( &user ) ) ) 2843 { 2844 // debugprint( "RequestUserIP() call failed\n" ); 2845 return false; 2846 } 2847 return true; 2848 } 2849 2850 // Do all users in current chatsink list. 2851 2852 pChatSink->DeleteUserIPList(); // Clear old user IPs. (To keep searches fast, if we go in and out of game channels a lot.) 2853 2854 User* pUser = pChatSink->pUserList; 2855 while( pUser ) 2856 { 2857 if( !( pUser->flags & CHAT_USER_MYSELF ) ) 2858 { 2859 if( !SUCCEEDED( pChat->RequestUserIP( pUser ) ) ) 2860 { 2861 // debugprint( "RequestUserIP() call failed\n" ); 2862 return false; 2863 } 2864 } 2865 pUser = pUser->next; 2866 } 2867 return true; 2868 } 2869 2870 //*********************************************************************************************** 2871 void WolapiObject::SaveChat() 2872 { 2873 // Basically, a big hack to avoiding restructuring things so that the dialogs are persistent 2874 // objects. Save the contents of the chat list in the chat dialog so that we can refresh it 2875 // after returning from the game setup dialog (if necessary). 2876 // This turns out to be the easiest and most straightforward way to implement this. 2877 pChatSaveLast = pChatSaveList = NULL; 2878 CHATSAVE* pChatSaveNew; 2879 2880 for( int i = 0; i != pILChat->Count(); i++ ) 2881 { 2882 pChatSaveNew = new CHATSAVE; 2883 const char* szItem = pILChat->Get_Item( i ); 2884 if( strlen( szItem ) < SAVECHATWIDTH ) 2885 strcpy( pChatSaveNew->szText, szItem ); 2886 const IconList_ItemExtras* pItemExtras = pILChat->Get_ItemExtras( i ); 2887 pChatSaveNew->ItemExtras.pColorRemap = pItemExtras->pColorRemap; 2888 pChatSaveNew->next = NULL; 2889 2890 if( pChatSaveLast ) 2891 pChatSaveLast->next = pChatSaveNew; 2892 else 2893 pChatSaveList = pChatSaveNew; 2894 pChatSaveLast = pChatSaveNew; 2895 } 2896 } 2897 2898 //*********************************************************************************************** 2899 void WolapiObject::RestoreChat() 2900 { 2901 // See SaveChat()... 2902 CHATSAVE* pChatSave = pChatSaveList; 2903 while( pChatSave ) 2904 { 2905 PrintMessage( pChatSave->szText, pChatSave->ItemExtras.pColorRemap ); 2906 pChatSave = pChatSave->next; 2907 } 2908 } 2909 2910 //*********************************************************************************************** 2911 void WolapiObject::AddHostLeftMessageToSavedChat( const char* szName ) 2912 { 2913 CHATSAVE* pChatSaveNew; 2914 pChatSaveNew = new CHATSAVE; 2915 sprintf( pChatSaveNew->szText, TXT_WOL_HOSTLEFTGAME, szName ); 2916 pChatSaveNew->ItemExtras.pColorRemap = &ColorRemaps[ WOLCOLORREMAP_LOCALMACHINEMESS ]; 2917 pChatSaveNew->next = NULL; 2918 if( pChatSaveLast ) 2919 pChatSaveLast->next = pChatSaveNew; 2920 else 2921 pChatSaveList = pChatSaveNew; 2922 pChatSaveLast = pChatSaveNew; 2923 } 2924 2925 //*********************************************************************************************** 2926 void WolapiObject::AddMessageToSavedChat( const char* szMessage ) 2927 { 2928 CHATSAVE* pChatSaveNew; 2929 pChatSaveNew = new CHATSAVE; 2930 strcpy( pChatSaveNew->szText, szMessage ); 2931 pChatSaveNew->ItemExtras.pColorRemap = &ColorRemaps[ WOLCOLORREMAP_LOCALMACHINEMESS ]; 2932 pChatSaveNew->next = NULL; 2933 if( pChatSaveLast ) 2934 pChatSaveLast->next = pChatSaveNew; 2935 else 2936 pChatSaveList = pChatSaveNew; 2937 pChatSaveLast = pChatSaveNew; 2938 } 2939 2940 //*********************************************************************************************** 2941 void WolapiObject::DeleteSavedChat() 2942 { 2943 // See SaveChat()... 2944 CHATSAVE* pChatSaveNext; 2945 while( pChatSaveList ) 2946 { 2947 pChatSaveNext = pChatSaveList->next; 2948 delete pChatSaveList; 2949 pChatSaveList = pChatSaveNext; 2950 } 2951 } 2952 2953 //*********************************************************************************************** 2954 void WolapiObject::GenericErrorMessage() 2955 { 2956 // Displays generic "something bad happened" error message. 2957 bPump_In_Call_Back = true; 2958 WWMessageBox().Process( TXT_WOL_ERRORMESSAGE ); 2959 bPump_In_Call_Back = false; 2960 } 2961 2962 //*********************************************************************************************** 2963 bool WolapiObject::GetNameOfBeginningLobby( char* szNameToSet ) 2964 { 2965 // Checks for game lobbies, sets szNameToSet to the channel name that the new user should enter and returns true if succeeds. 2966 if( !GetLobbyChannels() ) 2967 return false; 2968 2969 // Chatsink should now have a list of lobbies. 2970 int iCount = 0; 2971 Channel* pChannel = pChatSink->pChannelList; 2972 if( !pChannel ) 2973 // List is empty. 2974 return false; 2975 2976 // Return the name of the first lobby with less than 50 users. 2977 while( pChannel ) 2978 { 2979 if( pChannel->currentUsers < 50 ) 2980 { 2981 strcpy( szNameToSet, (char*)pChannel->name ); 2982 return true; 2983 } 2984 ++iCount; 2985 pChannel = pChannel->next; 2986 } 2987 2988 // All lobbies have 50 or more users. So just choose a random one. 2989 int iChoice = ( rand() % iCount ); 2990 pChannel = pChatSink->pChannelList; 2991 for( int i = 0; i != iChoice; i++ ) 2992 pChannel = pChannel->next; 2993 2994 strcpy( szNameToSet, (char*)pChannel->name ); 2995 2996 return true; 2997 } 2998 2999 //*********************************************************************************************** 3000 bool WolapiObject::GetLobbyChannels() 3001 { 3002 // Modal version of UpdateChannels, for fetching lobby names. 3003 3004 // // Returns false upon total failure. ajxxx do same for other calls 3005 // WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); 3006 3007 pChatSink->bRequestChannelListForLobbiesWait = true; 3008 pChatSink->ChannelFilter = CHANNELFILTER_LOBBIES; 3009 3010 // debugprint( "RequestChannelList() for lobbies\n" ); 3011 if( !SUCCEEDED( pChat->RequestChannelList( 0, false ) ) ) 3012 { 3013 // debugprint( "RequestChannelList() call failed\n" ); 3014 return false; 3015 } 3016 3017 DWORD dwTimeStart = timeGetTime(); 3018 DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 3019 while( pChatSink->bRequestChannelListForLobbiesWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) 3020 { 3021 while( timeGetTime() < dwTimeNextPump ) 3022 Call_Back(); 3023 pChat->PumpMessages(); 3024 dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 3025 } 3026 3027 if( pChatSink->bRequestChannelListForLobbiesWait ) 3028 return false; 3029 3030 return true; 3031 } 3032 3033 //*********************************************************************************************** 3034 const char* WolapiObject::pGameHostName() 3035 { 3036 // Returns a POINTER (careful - temporary!) to the name of the creator of the game channel we're in, or null. 3037 // Uses players' list as its means of reference. 3038 if( pILPlayers ) 3039 { 3040 for( int i = 0; i != pILPlayers->Count(); i++ ) 3041 { 3042 User* pUser = (User*)pILPlayers->Get_Item_ExtraDataPtr( i ); 3043 if( pUser && pUser->flags & CHAT_USER_CHANNELOWNER ) 3044 return (char*)pUser->name; 3045 } 3046 } 3047 return NULL; 3048 } 3049 3050 //*********************************************************************************************** 3051 User* WolapiObject::pGameHost() 3052 { 3053 // Returns a POINTER (careful - temporary!) to the creator of the game channel we're in, or null. 3054 // Uses players' list as its means of reference. 3055 if( pILPlayers ) 3056 { 3057 for( int i = 0; i != pILPlayers->Count(); i++ ) 3058 { 3059 User* pUser = (User*)pILPlayers->Get_Item_ExtraDataPtr( i ); 3060 if( pUser && pUser->flags & CHAT_USER_CHANNELOWNER ) 3061 return pUser; 3062 } 3063 } 3064 return NULL; 3065 } 3066 3067 //*********************************************************************************************** 3068 bool WolapiObject::SendGameOpt( const char* szSend, User* pUserPriv ) 3069 { 3070 // Used during game setup to send public or private game options string. 3071 // If pUserPriv is NULL, message is public, else private to pUserPriv. 3072 if( !pUserPriv ) 3073 { 3074 // debugprint( "Send public game opt: '%s'\n", szSend ); 3075 if( !SUCCEEDED( pChat->RequestPublicGameOptions( szSend ) ) ) 3076 { 3077 // debugprint( "RequestPublicGameOptions() call failed\n" ); 3078 return false; 3079 } 3080 } 3081 else 3082 { 3083 // debugprint( "Send private game opt to %s: '%s'\n", (char*)pUserPriv->name, szSend ); 3084 if( !SUCCEEDED( pChat->RequestPrivateGameOptions( pUserPriv, szSend ) ) ) 3085 { 3086 // debugprint( "RequestPrivateGameOptions() call failed\n" ); 3087 return false; 3088 } 3089 } 3090 return true; 3091 } 3092 3093 //*********************************************************************************************** 3094 bool WolapiObject::RequestGameStart() 3095 { 3096 // Host is starting a game. 3097 3098 /* 3099 // Block any users that join the channel in the next microsecond from becoming involved, and 3100 // block any users that leave from being recognized as having left. 3101 //what if someone leaves? 3102 // This is done to preserve the integrity of the ChatSink's user list 3103 pWO->pChatSink->bIgnoreJoin = true; 3104 */ 3105 pChatSink->bRequestGameStartWait = true; 3106 3107 // debugprint( "RequestGameStart()\n" ); 3108 if( !SUCCEEDED( pChat->RequestGameStart( pChatSink->pUserList ) ) ) 3109 { 3110 // debugprint( "RequestGameStart() call failed\n" ); 3111 return false; 3112 } 3113 3114 DWORD dwTimeStart = timeGetTime(); 3115 DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 3116 while( pChatSink->bRequestGameStartWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) 3117 { 3118 while( timeGetTime() < dwTimeNextPump ) 3119 Call_Back(); 3120 pChat->PumpMessages(); 3121 dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; 3122 } 3123 3124 if( pChatSink->bRequestGameStartWait ) 3125 { 3126 // debugprint( "WolapiObject::RequestGameStart returning false\n" ); 3127 pChatSink->bRequestGameStartWait = false; 3128 return false; 3129 } 3130 3131 // debugprint( "WolapiObject::RequestGameStart returning true\n" ); 3132 return true; 3133 } 3134 3135 //*********************************************************************************************** 3136 bool WolapiObject::SendGo( const char* szSend ) 3137 { 3138 // Send a "GO" message to all players included in the list that came back from OnGameStart. 3139 // (Don't just broadcast it. We don't want to include any users that may have joined the channel 3140 // in the last microsecond.) 3141 // debugprint( "SendGo()\n" ); 3142 3143 User* pUser = pChatSink->pGameUserList; 3144 3145 while( pUser ) 3146 { 3147 // if( !( pUser->flags & CHAT_USER_MYSELF ) ) Method changed. I now wait for go message to bounce back to me. 3148 // { 3149 // debugprint( "Send private game opt to %s: '%s'\n", (char*)pUser->name, szSend ); 3150 if( !SUCCEEDED( pChat->RequestPrivateGameOptions( pUser, szSend ) ) ) 3151 { 3152 // debugprint( "RequestPrivateGameOptions() call failed\n" ); 3153 return false; 3154 } 3155 // } 3156 pUser = pUser->next; 3157 } 3158 return true; 3159 } 3160 3161 //*********************************************************************************************** 3162 void WolapiObject::Init_DisconnectPinging() 3163 { 3164 // Sets us up to begin "disconnect pinging" - the pinging that occurs when connection is broken 3165 // during a tournament game. The idea is to try and figure out who is responsible for the connection 3166 // going down. We do this by repeatedly pinging the opponent and the game results server. The number 3167 // of successful pings is sent in the game results package. 3168 iDisconnectPingCurrent = 0; 3169 for( int i = 0; i != DISCONNECT_PING_COUNT; ++i ) 3170 { 3171 DisconnectPingResult_Server[ i ] = PING_UNSTARTED; 3172 DisconnectPingResult_Opponent[ i ] = PING_UNSTARTED; 3173 } 3174 bDisconnectPingingCompleted = false; 3175 bDoingDisconnectPinging = true; 3176 } 3177 3178 //*********************************************************************************************** 3179 bool WolapiObject::Pump_DisconnectPinging() 3180 { 3181 // Called repeatedly and continuously when it seems a tournament game connection with the opponent 3182 // has been broken. Does PumpMessages() and requests new pings when previous results have been received. 3183 // Returns true when the required number of pings have been completed. 3184 3185 if( ::timeGetTime() > dwTimeNextWolapiPump ) 3186 { 3187 pChat->PumpMessages(); 3188 pNetUtil->PumpMessages(); 3189 dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; 3190 } 3191 3192 switch( DisconnectPingResult_Server[ iDisconnectPingCurrent ] ) 3193 { 3194 case PING_UNSTARTED: 3195 // Pings have yet to be requested. 3196 // Ping game results server. 3197 int iUnused; 3198 if( *szGameResServerHost1 ) 3199 { 3200 // debugprint( "RequestPing ( gameres server )\n" ); 3201 if( pNetUtil->RequestPing( szGameResServerHost1, 1000, &iUnused ) != S_OK ) 3202 { 3203 // debugprint( "RequestPing() ( gameres server ) failed\n" ); 3204 DisconnectPingResult_Server[ iDisconnectPingCurrent ] = PING_BAD; 3205 } 3206 DisconnectPingResult_Server[ iDisconnectPingCurrent ] = PING_WAITING; 3207 } 3208 else 3209 // We never got an address for the gameresults server. Fake fail result. 3210 DisconnectPingResult_Server[ iDisconnectPingCurrent ] = PING_BAD; 3211 3212 // Ping opponent. 3213 in_addr inaddr; 3214 char* szIP; 3215 inaddr.s_addr = TournamentOpponentIP; 3216 szIP = inet_ntoa( inaddr ); 3217 // debugprint( "RequestPing ( opponent )\n" ); 3218 if( pNetUtil->RequestPing( szIP, 1000, &iUnused ) != S_OK ) 3219 { 3220 // debugprint( "RequestPing() ( opponent ) failed\n" ); 3221 DisconnectPingResult_Opponent[ iDisconnectPingCurrent ] = PING_BAD; 3222 } 3223 else 3224 DisconnectPingResult_Opponent[ iDisconnectPingCurrent ] = PING_WAITING; 3225 break; 3226 case PING_WAITING: 3227 // Ping results still pending. (Callback will set vars when results arrive.) 3228 break; 3229 default: 3230 // Ping result for server is in. 3231 if( DisconnectPingResult_Opponent[ iDisconnectPingCurrent ] == PING_WAITING ) 3232 break; 3233 // Both results are in. Begin new ping, or end disconnect pinging. 3234 iDisconnectPingCurrent++; 3235 if( iDisconnectPingCurrent == DISCONNECT_PING_COUNT ) 3236 { 3237 bDisconnectPingingCompleted = true; 3238 bDoingDisconnectPinging = false; 3239 return true; 3240 } 3241 break; 3242 } 3243 return false; 3244 } 3245 3246 //*********************************************************************************************** 3247 void WolapiObject::DisconnectPingResultsString( char* szStringToSet ) 3248 { 3249 int iGoodServerPings = 0; 3250 int iGoodPlayerPings = 0; 3251 for( int i = 0; i < DISCONNECT_PING_COUNT; ++i ) 3252 { 3253 if( DisconnectPingResult_Server[ i ] == PING_GOOD ) ++iGoodServerPings; 3254 if( DisconnectPingResult_Opponent[ i ] == PING_GOOD ) ++iGoodPlayerPings; 3255 } 3256 3257 sprintf( szStringToSet, "%1u/%1u %1u/%1u", iGoodServerPings, DISCONNECT_PING_COUNT, iGoodPlayerPings, DISCONNECT_PING_COUNT ); 3258 } 3259 3260 //*********************************************************************************************** 3261 void WolapiObject::SetOptionDefaults() 3262 { 3263 // Get stored defaults for options. 3264 HKEY hKey; 3265 if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_READ, &hKey ) == ERROR_SUCCESS ) 3266 { 3267 DWORD dwValue; 3268 DWORD dwBufSize = sizeof( DWORD ); 3269 if( RegQueryValueEx( hKey, "WOLAPI Find Enabled", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) 3270 bFindEnabled = true; 3271 else 3272 bFindEnabled = (bool)dwValue; 3273 if( RegQueryValueEx( hKey, "WOLAPI Page Enabled", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) 3274 bPageEnabled = true; 3275 else 3276 bPageEnabled = (bool)dwValue; 3277 if( RegQueryValueEx( hKey, "WOLAPI Lang Filter", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) 3278 bLangFilter = true; 3279 else 3280 bLangFilter = (bool)dwValue; 3281 if( RegQueryValueEx( hKey, "WOLAPI Show All Games", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) 3282 bAllGamesShown = true; 3283 else 3284 bAllGamesShown = (bool)dwValue; 3285 3286 RegCloseKey( hKey ); 3287 } 3288 pChat->SetFindPage( bFindEnabled, bPageEnabled ); 3289 pChat->SetLangFilter( bLangFilter ); 3290 } 3291 3292 //*********************************************************************************************** 3293 void WolapiObject::SetOptions( bool bEnableFind, bool bEnablePage, bool bLangFilterOn, bool bShowAllGames ) 3294 { 3295 // Set options and remember them in registry. 3296 3297 bFindEnabled = bEnableFind; 3298 bPageEnabled = bEnablePage; 3299 bLangFilter = bLangFilterOn; 3300 bAllGamesShown = bShowAllGames; 3301 3302 HKEY hKey; 3303 if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_WRITE, &hKey ) == ERROR_SUCCESS ) 3304 { 3305 DWORD dwValue = bFindEnabled ? 1 : 0; 3306 RegSetValueEx( hKey, "WOLAPI Find Enabled", 0, REG_DWORD, (LPBYTE)&dwValue, sizeof( dwValue ) ); 3307 dwValue = bPageEnabled ? 1 : 0; 3308 RegSetValueEx( hKey, "WOLAPI Page Enabled", 0, REG_DWORD, (LPBYTE)&dwValue, sizeof( dwValue ) ); 3309 dwValue = bLangFilter ? 1 : 0; 3310 RegSetValueEx( hKey, "WOLAPI Lang Filter", 0, REG_DWORD, (LPBYTE)&dwValue, sizeof( dwValue ) ); 3311 dwValue = bAllGamesShown ? 1 : 0; 3312 RegSetValueEx( hKey, "WOLAPI Show All Games", 0, REG_DWORD, (LPBYTE)&dwValue, sizeof( dwValue ) ); 3313 3314 RegCloseKey( hKey ); 3315 } 3316 pChat->SetFindPage( bFindEnabled, bPageEnabled ); 3317 pChat->SetLangFilter( bLangFilter ); 3318 } 3319 3320 //*********************************************************************************************** 3321 HPALETTE GetCurrentScreenPalette() 3322 { 3323 // Get the palette of the current screen. 3324 // Returns 0 if can't get palette. 3325 // Remember to DeleteObject the HPALETTE if non-zero. 3326 GraphicViewPortClass draw_window( LogicPage->Get_Graphic_Buffer(), 3327 WindowList[WINDOW_MAIN][WINDOWX] + LogicPage->Get_XPos(), 3328 WindowList[WINDOW_MAIN][WINDOWY] + LogicPage->Get_YPos(), 3329 WindowList[WINDOW_MAIN][WINDOWWIDTH], 3330 WindowList[WINDOW_MAIN][WINDOWHEIGHT] ); 3331 LPDIRECTDRAWSURFACE lpDDS = draw_window.Get_Graphic_Buffer()->Get_DD_Surface(); 3332 LPDIRECTDRAWPALETTE lpDDP; 3333 HPALETTE hPal = 0; 3334 if( lpDDS->GetPalette( &lpDDP ) == DD_OK ) 3335 { 3336 PALETTEENTRY pe[256]; 3337 if( lpDDP->GetEntries( 0, 0, 256, pe ) == DD_OK ) 3338 { 3339 LOGPALETTE* pLogPal = (LOGPALETTE*)new char[ sizeof( LOGPALETTE ) + sizeof( PALETTEENTRY ) * 255 ]; 3340 pLogPal->palVersion = 0x300; 3341 pLogPal->palNumEntries = 256; 3342 for( int i = 0; i != 256; i++ ) 3343 { 3344 pLogPal->palPalEntry[i].peRed = pe[i].peRed; 3345 pLogPal->palPalEntry[i].peGreen = pe[i].peGreen; 3346 pLogPal->palPalEntry[i].peBlue = pe[i].peBlue; 3347 pLogPal->palPalEntry[i].peFlags = 0; 3348 // debugprint( "DD Palette %03u: %03u, %03u, %03u\n", i, pe[i].peRed, pe[i].peGreen, pe[i].peBlue ); 3349 } 3350 hPal = CreatePalette( pLogPal ); 3351 delete [] pLogPal; 3352 } 3353 // else 3354 // debugprint( "DD GetEntries failed.\n" ); 3355 } 3356 // else 3357 // debugprint( "DD GetPalette failed.\n" ); 3358 3359 return hPal; 3360 } 3361 3362 //*********************************************************************************************** 3363 void RemapDIBToPalette( HPALETTE hPal, const char* pDIB ) // Note: pDIB is treated as non-const. 3364 { 3365 // Converts pDIB's actual pixel data to proper values for a different palette (hPal). 3366 // Obeys convention that index 0 in the DIB's palette should map to transparent. For our purposes, make it black. 3367 3368 // Set the values of the qNewPalette array to hold the new destination palette index we want 3369 // a bmp palette entry to map to. 3370 unsigned char qNewPalette[ 256 ]; 3371 3372 LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)pDIB; 3373 3374 RGBQUAD* pRGBEntry = (RGBQUAD*)( (char*)lpbi + lpbi->biSize ); // This now points to the first entry in the bmp's palette table. 3375 3376 // debugprint( "Starting rgbquads at %i\n", pRGBEntry ); 3377 3378 // Index zero is supposed to be transparent. In our case, that means make it black. 3379 qNewPalette[0] = GetNearestPaletteIndex( hPal, RGB( 0, 0, 0 ) ); 3380 pRGBEntry++; 3381 3382 for( int i = 1; i != 256; i++ ) 3383 { 3384 qNewPalette[i] = GetNearestPaletteIndex( hPal, RGB( pRGBEntry->rgbRed, pRGBEntry->rgbGreen, pRGBEntry->rgbBlue ) ); 3385 // if( iIndex == 1 ) 3386 // debugprint( "Remapping bmp %03u to new %03u\n", i, qNewPalette[i] ); 3387 pRGBEntry++; 3388 } 3389 3390 // Convert the data to values that match game palette. 3391 int iWidth = DIBWidth( pDIB ); 3392 int iHeight = DIBHeight( pDIB ); 3393 int iSrcPitch = ( iWidth + 3 ) & ~3; 3394 int iLength = iSrcPitch * iHeight; 3395 unsigned char* pBits = (unsigned char*)FindDIBBits( pDIB ); 3396 // debugprint( "First Byte value %03u will become %03u\n", *pBits, qNewPalette[ *pBits ] ); 3397 for( i = 0; i != iLength; i++ ) 3398 { 3399 *pBits++ = qNewPalette[ *pBits ]; 3400 } 3401 } 3402 3403 /* 3404 //*********************************************************************************************** 3405 char* LoadFileIntoMemory( const char* szFileName, int& iLength ) 3406 { 3407 // Loads a file into a buffer. 3408 // Delete[] the pointer when you're done with the buffer. 3409 HANDLE hFile; 3410 hFile = CreateFile( szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); 3411 if( hFile == INVALID_HANDLE_VALUE ) 3412 return NULL; 3413 iLength = GetFileSize( hFile, NULL ); 3414 char* pData = new char[ iLength ]; 3415 DWORD dwBytesRead; 3416 ReadFile( hFile, pData, iLength, &dwBytesRead, NULL ); 3417 if( dwBytesRead != iLength ) 3418 // debugprint( "######LoadFileIntoMemory expected %i bytes and got %i\n", iLength, dwBytesRead ); 3419 CloseHandle( hFile ); 3420 return pData; 3421 } 3422 */ 3423 3424 //*********************************************************************************************** 3425 void HostNameFromGameChannelName( char* szNameToSet, const char* szChannelName ) 3426 { 3427 int iApostrophe = strcspn( szChannelName, "'" ); 3428 memcpy( szNameToSet, szChannelName, iApostrophe ); 3429 szNameToSet[ iApostrophe ] = 0; 3430 } 3431 3432 #endif