Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1284 lines
38 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements all the functions exported by the GameUI dll
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #ifdef WIN32
  8. #if !defined( _X360 )
  9. #include <windows.h>
  10. #endif
  11. #include <io.h>
  12. #include <direct.h>
  13. #elif defined( POSIX )
  14. #include <sys/time.h>
  15. #else
  16. #error
  17. #endif
  18. #include <sys/types.h>
  19. #include <sys/stat.h>
  20. #include <stdio.h>
  21. #include <tier0/dbg.h>
  22. #ifdef SendMessage
  23. #undef SendMessage
  24. #endif
  25. #include "filesystem.h"
  26. #include "GameUI_Interface.h"
  27. #include "Sys_Utils.h"
  28. #include "string.h"
  29. #include "tier0/icommandline.h"
  30. // interface to engine
  31. #include "EngineInterface.h"
  32. #include "replay/ienginereplay.h"
  33. #include "replay/ireplaysystem.h"
  34. #include "VGuiSystemModuleLoader.h"
  35. #include "bitmap/tgaloader.h"
  36. #include "GameConsole.h"
  37. #include "LoadingDialog.h"
  38. #include "CDKeyEntryDialog.h"
  39. #include "ModInfo.h"
  40. #include "game/client/IGameClientExports.h"
  41. #include "materialsystem/imaterialsystem.h"
  42. #include "engine/imatchmaking.h"
  43. #include "ixboxsystem.h"
  44. #include "iachievementmgr.h"
  45. #include "IGameUIFuncs.h"
  46. #include <ienginevgui.h>
  47. #include "steam/steam_api.h"
  48. #include "BonusMapsDatabase.h"
  49. #include "BonusMapsDialog.h"
  50. #include "sourcevr/isourcevirtualreality.h"
  51. // vgui2 interface
  52. // note that GameUI project uses ..\vgui2\include, not ..\utils\vgui\include
  53. #include "BasePanel.h"
  54. #include <vgui/Cursor.h>
  55. #include <KeyValues.h>
  56. #include <vgui/ILocalize.h>
  57. #include <vgui/IPanel.h>
  58. #include <vgui/IScheme.h>
  59. #include <vgui/IVGui.h>
  60. #include <vgui/ISystem.h>
  61. #include <vgui/ISurface.h>
  62. #include <vgui_controls/Menu.h>
  63. #include <vgui_controls/PHandle.h>
  64. #include "tier3/tier3.h"
  65. #include "tier0/vcrmode.h"
  66. #include "matsys_controls/matsyscontrols.h"
  67. #include "steam/steam_api.h"
  68. #if defined( _X360 )
  69. #include "xbox/xbox_win32stubs.h"
  70. #endif
  71. #include "tier0/dbg.h"
  72. #include "engine/IEngineSound.h"
  73. // memdbgon must be the last include file in a .cpp file!!!
  74. #include <tier0/memdbgon.h>
  75. IGameUIFuncs *gameuifuncs = NULL;
  76. IEngineVGui *enginevguifuncs = NULL;
  77. IMatchmaking *matchmaking = NULL;
  78. IXboxSystem *xboxsystem = NULL; // 360 only
  79. vgui::ISurface *enginesurfacefuncs = NULL;
  80. IVEngineClient *engine = NULL;
  81. IEngineSound *enginesound = NULL;
  82. IAchievementMgr *achievementmgr = NULL;
  83. IEngineClientReplay *g_pEngineClientReplay = NULL;
  84. ISourceVirtualReality *g_pSourceVR = NULL;
  85. static CSteamAPIContext g_SteamAPIContext;
  86. CSteamAPIContext *steamapicontext = &g_SteamAPIContext;
  87. static CBasePanel *staticPanel = NULL;
  88. class CGameUI;
  89. CGameUI *g_pGameUI = NULL;
  90. class CLoadingDialog;
  91. vgui::DHANDLE<CLoadingDialog> g_hLoadingDialog;
  92. vgui::VPANEL g_hLoadingBackgroundDialog = NULL;
  93. static CGameUI g_GameUI;
  94. static WHANDLE g_hMutex = NULL;
  95. static WHANDLE g_hWaitMutex = NULL;
  96. static IGameClientExports *g_pGameClientExports = NULL;
  97. IGameClientExports *GameClientExports()
  98. {
  99. return g_pGameClientExports;
  100. }
  101. //-----------------------------------------------------------------------------
  102. // Purpose: singleton accessor
  103. //-----------------------------------------------------------------------------
  104. CGameUI &GameUI()
  105. {
  106. return g_GameUI;
  107. }
  108. EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CGameUI, IGameUI, GAMEUI_INTERFACE_VERSION, g_GameUI);
  109. //-----------------------------------------------------------------------------
  110. // Purpose: Constructor
  111. //-----------------------------------------------------------------------------
  112. CGameUI::CGameUI()
  113. {
  114. g_pGameUI = this;
  115. m_bTryingToLoadFriends = false;
  116. m_iFriendsLoadPauseFrames = 0;
  117. m_iGameIP = 0;
  118. m_iGameConnectionPort = 0;
  119. m_iGameQueryPort = 0;
  120. m_bActivatedUI = false;
  121. m_szPreviousStatusText[0] = 0;
  122. m_bIsConsoleUI = false;
  123. m_bHasSavedThisMenuSession = false;
  124. m_bOpenProgressOnStart = false;
  125. }
  126. //-----------------------------------------------------------------------------
  127. // Purpose: Destructor
  128. //-----------------------------------------------------------------------------
  129. CGameUI::~CGameUI()
  130. {
  131. g_pGameUI = NULL;
  132. }
  133. //-----------------------------------------------------------------------------
  134. // Purpose: Initialization
  135. //-----------------------------------------------------------------------------
  136. void CGameUI::Initialize( CreateInterfaceFn factory )
  137. {
  138. ConnectTier1Libraries( &factory, 1 );
  139. ConnectTier2Libraries( &factory, 1 );
  140. ConVar_Register( FCVAR_CLIENTDLL );
  141. ConnectTier3Libraries( &factory, 1 );
  142. enginesound = (IEngineSound *)factory(IENGINESOUND_CLIENT_INTERFACE_VERSION, NULL);
  143. engine = (IVEngineClient *)factory( VENGINE_CLIENT_INTERFACE_VERSION, NULL );
  144. steamapicontext->Init();
  145. ConVarRef var( "gameui_xbox" );
  146. m_bIsConsoleUI = var.IsValid() && var.GetBool();
  147. vgui::VGui_InitInterfacesList( "GameUI", &factory, 1 );
  148. vgui::VGui_InitMatSysInterfacesList( "GameUI", &factory, 1 );
  149. // load localization file
  150. g_pVGuiLocalize->AddFile( "Resource/gameui_%language%.txt", "GAME", true );
  151. // load mod info
  152. ModInfo().LoadCurrentGameInfo();
  153. // load localization file for kb_act.lst
  154. g_pVGuiLocalize->AddFile( "Resource/valve_%language%.txt", "GAME", true );
  155. enginevguifuncs = (IEngineVGui *)factory( VENGINE_VGUI_VERSION, NULL );
  156. enginesurfacefuncs = (vgui::ISurface *)factory(VGUI_SURFACE_INTERFACE_VERSION, NULL);
  157. gameuifuncs = (IGameUIFuncs *)factory( VENGINE_GAMEUIFUNCS_VERSION, NULL );
  158. matchmaking = (IMatchmaking *)factory( VENGINE_MATCHMAKING_VERSION, NULL );
  159. xboxsystem = (IXboxSystem *)factory( XBOXSYSTEM_INTERFACE_VERSION, NULL );
  160. g_pEngineClientReplay = (IEngineClientReplay *)factory( ENGINE_REPLAY_CLIENT_INTERFACE_VERSION, NULL );
  161. if ( ModInfo().SupportsVR() && CommandLine()->CheckParm( "-vr" ) )
  162. {
  163. g_pSourceVR = (ISourceVirtualReality *)factory( SOURCE_VIRTUAL_REALITY_INTERFACE_VERSION, NULL );
  164. }
  165. // NOTE: g_pEngineReplay intentionally not checked here
  166. if ( !enginesurfacefuncs || !gameuifuncs || !enginevguifuncs || !xboxsystem || (IsX360() && !matchmaking) )
  167. {
  168. Error( "CGameUI::Initialize() failed to get necessary interfaces\n" );
  169. }
  170. // setup base panel
  171. staticPanel = new CBasePanel();
  172. staticPanel->SetBounds(0, 0, 400, 300 );
  173. staticPanel->SetPaintBorderEnabled( false );
  174. staticPanel->SetPaintBackgroundEnabled( true );
  175. staticPanel->SetPaintEnabled( false );
  176. staticPanel->SetVisible( true );
  177. staticPanel->SetMouseInputEnabled( false );
  178. staticPanel->SetKeyBoardInputEnabled( false );
  179. vgui::VPANEL rootpanel = enginevguifuncs->GetPanel( PANEL_GAMEUIDLL );
  180. staticPanel->SetParent( rootpanel );
  181. }
  182. void CGameUI::PostInit()
  183. {
  184. if ( IsX360() )
  185. {
  186. enginesound->PrecacheSound( "UI/buttonrollover.wav", true, true );
  187. enginesound->PrecacheSound( "UI/buttonclick.wav", true, true );
  188. enginesound->PrecacheSound( "UI/buttonclickrelease.wav", true, true );
  189. enginesound->PrecacheSound( "player/suit_denydevice.wav", true, true );
  190. }
  191. }
  192. //-----------------------------------------------------------------------------
  193. // Purpose: Sets the specified panel as the background panel for the loading
  194. // dialog. If NULL, default background is used. If you set a panel,
  195. // it should be full-screen with an opaque background, and must be a VGUI popup.
  196. //-----------------------------------------------------------------------------
  197. void CGameUI::SetLoadingBackgroundDialog( vgui::VPANEL panel )
  198. {
  199. g_hLoadingBackgroundDialog = panel;
  200. }
  201. void CGameUI::BonusMapUnlock( const char *pchFileName, const char *pchMapName )
  202. {
  203. if ( !pchFileName || pchFileName[ 0 ] == '\0' ||
  204. !pchMapName || pchMapName[ 0 ] == '\0' )
  205. {
  206. if ( !g_pBonusMapsDialog )
  207. return;
  208. g_pBonusMapsDialog->SetSelectedBooleanStatus( "lock", false );
  209. return;
  210. }
  211. if ( BonusMapsDatabase()->SetBooleanStatus( "lock", pchFileName, pchMapName, false ) )
  212. {
  213. BonusMapsDatabase()->RefreshMapData();
  214. if ( !g_pBonusMapsDialog )
  215. {
  216. // It unlocked without the bonus maps menu open, so flash the menu item
  217. CBasePanel *pBasePanel = BasePanel();
  218. if ( pBasePanel )
  219. {
  220. if ( GameUI().IsConsoleUI() )
  221. {
  222. if ( Q_stricmp( pchFileName, "scripts/advanced_chambers" ) == 0 )
  223. {
  224. pBasePanel->SetMenuItemBlinkingState( "OpenNewGameDialog", true );
  225. }
  226. }
  227. else
  228. {
  229. pBasePanel->SetMenuItemBlinkingState( "OpenBonusMapsDialog", true );
  230. }
  231. }
  232. BonusMapsDatabase()->SetBlink( true );
  233. }
  234. else
  235. g_pBonusMapsDialog->RefreshData(); // Update the open dialog
  236. }
  237. }
  238. void CGameUI::BonusMapComplete( const char *pchFileName, const char *pchMapName )
  239. {
  240. if ( !pchFileName || pchFileName[ 0 ] == '\0' ||
  241. !pchMapName || pchMapName[ 0 ] == '\0' )
  242. {
  243. if ( !g_pBonusMapsDialog )
  244. return;
  245. g_pBonusMapsDialog->SetSelectedBooleanStatus( "complete", true );
  246. BonusMapsDatabase()->RefreshMapData();
  247. g_pBonusMapsDialog->RefreshData();
  248. return;
  249. }
  250. if ( BonusMapsDatabase()->SetBooleanStatus( "complete", pchFileName, pchMapName, true ) )
  251. {
  252. BonusMapsDatabase()->RefreshMapData();
  253. // Update the open dialog
  254. if ( g_pBonusMapsDialog )
  255. g_pBonusMapsDialog->RefreshData();
  256. }
  257. }
  258. void CGameUI::BonusMapChallengeUpdate( const char *pchFileName, const char *pchMapName, const char *pchChallengeName, int iBest )
  259. {
  260. if ( !pchFileName || pchFileName[ 0 ] == '\0' ||
  261. !pchMapName || pchMapName[ 0 ] == '\0' ||
  262. !pchChallengeName || pchChallengeName[ 0 ] == '\0' )
  263. {
  264. return;
  265. }
  266. else
  267. {
  268. if ( BonusMapsDatabase()->UpdateChallengeBest( pchFileName, pchMapName, pchChallengeName, iBest ) )
  269. {
  270. // The challenge best changed, so write it to the file
  271. BonusMapsDatabase()->WriteSaveData();
  272. BonusMapsDatabase()->RefreshMapData();
  273. // Update the open dialog
  274. if ( g_pBonusMapsDialog )
  275. g_pBonusMapsDialog->RefreshData();
  276. }
  277. }
  278. }
  279. void CGameUI::BonusMapChallengeNames( char *pchFileName, char *pchMapName, char *pchChallengeName )
  280. {
  281. if ( !pchFileName || !pchMapName || !pchChallengeName )
  282. return;
  283. BonusMapsDatabase()->GetCurrentChallengeNames( pchFileName, pchMapName, pchChallengeName );
  284. }
  285. void CGameUI::BonusMapChallengeObjectives( int &iBronze, int &iSilver, int &iGold )
  286. {
  287. BonusMapsDatabase()->GetCurrentChallengeObjectives( iBronze, iSilver, iGold );
  288. }
  289. void CGameUI::BonusMapDatabaseSave( void )
  290. {
  291. BonusMapsDatabase()->WriteSaveData();
  292. }
  293. int CGameUI::BonusMapNumAdvancedCompleted( void )
  294. {
  295. return BonusMapsDatabase()->NumAdvancedComplete();
  296. }
  297. void CGameUI::BonusMapNumMedals( int piNumMedals[ 3 ] )
  298. {
  299. BonusMapsDatabase()->NumMedals( piNumMedals );
  300. }
  301. //-----------------------------------------------------------------------------
  302. // Purpose: connects to client interfaces
  303. //-----------------------------------------------------------------------------
  304. void CGameUI::Connect( CreateInterfaceFn gameFactory )
  305. {
  306. g_pGameClientExports = (IGameClientExports *)gameFactory(GAMECLIENTEXPORTS_INTERFACE_VERSION, NULL);
  307. achievementmgr = engine->GetAchievementMgr();
  308. if (!g_pGameClientExports)
  309. {
  310. Error("CGameUI::Initialize() failed to get necessary interfaces\n");
  311. }
  312. m_GameFactory = gameFactory;
  313. }
  314. //-----------------------------------------------------------------------------
  315. // Purpose: Callback function; sends platform Shutdown message to specified window
  316. //-----------------------------------------------------------------------------
  317. int __stdcall SendShutdownMsgFunc(WHANDLE hwnd, int lparam)
  318. {
  319. Sys_PostMessage(hwnd, Sys_RegisterWindowMessage("ShutdownValvePlatform"), 0, 1);
  320. return 1;
  321. }
  322. //-----------------------------------------------------------------------------
  323. // Purpose: Searches for GameStartup*.mp3 files in the sound/ui folder and plays one
  324. //-----------------------------------------------------------------------------
  325. void CGameUI::PlayGameStartupSound()
  326. {
  327. if ( IsX360() )
  328. return;
  329. if ( CommandLine()->FindParm( "-nostartupsound" ) )
  330. return;
  331. FileFindHandle_t fh;
  332. CUtlVector<char *> fileNames;
  333. char path[ 512 ];
  334. bool bHolidayFound = false;
  335. // only want to run the holiday check for TF2
  336. const char *pGameName = CommandLine()->ParmValue( "-game", "hl2" );
  337. if ( ( Q_stricmp( pGameName, "tf" ) == 0 ) || ( Q_stricmp( pGameName, "tf_beta" ) == 0 ) )
  338. {
  339. // check for a holiday sound file
  340. const char *pszHoliday = NULL;
  341. if ( GameClientExports() )
  342. {
  343. pszHoliday = GameClientExports()->GetHolidayString();
  344. if ( pszHoliday && pszHoliday[0] )
  345. {
  346. Q_snprintf( path, sizeof( path ), "sound/ui/holiday/gamestartup_%s*.mp3", pszHoliday );
  347. Q_FixSlashes( path );
  348. char const *fn = g_pFullFileSystem->FindFirstEx( path, "MOD", &fh );
  349. {
  350. if ( fn )
  351. {
  352. bHolidayFound = true;
  353. }
  354. }
  355. }
  356. }
  357. }
  358. // only want to do this if we haven't found a holiday file
  359. if ( !bHolidayFound )
  360. {
  361. Q_snprintf( path, sizeof( path ), "sound/ui/gamestartup*.mp3" );
  362. Q_FixSlashes( path );
  363. }
  364. char const *fn = g_pFullFileSystem->FindFirstEx( path, "MOD", &fh );
  365. if ( fn )
  366. {
  367. do
  368. {
  369. char ext[ 10 ];
  370. Q_ExtractFileExtension( fn, ext, sizeof( ext ) );
  371. if ( !Q_stricmp( ext, "mp3" ) )
  372. {
  373. char temp[ 512 ];
  374. if ( bHolidayFound )
  375. {
  376. Q_snprintf( temp, sizeof( temp ), "ui/holiday/%s", fn );
  377. }
  378. else
  379. {
  380. Q_snprintf( temp, sizeof( temp ), "ui/%s", fn );
  381. }
  382. char *found = new char[ strlen( temp ) + 1 ];
  383. Q_strncpy( found, temp, strlen( temp ) + 1 );
  384. Q_FixSlashes( found );
  385. fileNames.AddToTail( found );
  386. }
  387. fn = g_pFullFileSystem->FindNext( fh );
  388. } while ( fn );
  389. g_pFullFileSystem->FindClose( fh );
  390. }
  391. // did we find any?
  392. if ( fileNames.Count() > 0 )
  393. {
  394. #ifdef WIN32
  395. SYSTEMTIME SystemTime;
  396. GetSystemTime( &SystemTime );
  397. int index = SystemTime.wMilliseconds % fileNames.Count();
  398. #else
  399. struct timeval tm;
  400. gettimeofday( &tm, NULL );
  401. int index = tm.tv_usec/1000 % fileNames.Count();
  402. #endif
  403. if ( fileNames.IsValidIndex( index ) && fileNames[index] )
  404. {
  405. // Play the Saxxy music if we're in saxxy mode.
  406. #if defined( SAXXYMAINMENU_ENABLED )
  407. bool bIsTF = false;
  408. const char *pGameDir = engine->GetGameDirectory();
  409. if ( pGameDir )
  410. {
  411. // Is the game TF?
  412. const int nStrLen = V_strlen( pGameDir );
  413. bIsTF = nStrLen
  414. && nStrLen >= 2 &&
  415. pGameDir[nStrLen-2] == 't' &&
  416. pGameDir[nStrLen-1] == 'f';
  417. }
  418. // escape chars "*#" make it stream, and be affected by snd_musicvolume
  419. const char *pSoundFile = bIsTF ? "ui/holiday/gamestartup_saxxy.mp3" : fileNames[index];
  420. #else
  421. const char *pSoundFile = fileNames[index];
  422. #endif
  423. char found[ 512 ];
  424. Q_snprintf( found, sizeof( found ), "play *#%s", pSoundFile );
  425. engine->ClientCmd_Unrestricted( found );
  426. }
  427. fileNames.PurgeAndDeleteElements();
  428. }
  429. }
  430. //-----------------------------------------------------------------------------
  431. // Purpose: Called to setup the game UI
  432. //-----------------------------------------------------------------------------
  433. void CGameUI::Start()
  434. {
  435. // determine Steam location for configuration
  436. if ( !FindPlatformDirectory( m_szPlatformDir, sizeof( m_szPlatformDir ) ) )
  437. return;
  438. if ( IsPC() )
  439. {
  440. // setup config file directory
  441. char szConfigDir[512];
  442. Q_strncpy( szConfigDir, m_szPlatformDir, sizeof( szConfigDir ) );
  443. Q_strncat( szConfigDir, "config", sizeof( szConfigDir ), COPY_ALL_CHARACTERS );
  444. Msg( "Steam config directory: %s\n", szConfigDir );
  445. g_pFullFileSystem->AddSearchPath(szConfigDir, "CONFIG");
  446. g_pFullFileSystem->CreateDirHierarchy("", "CONFIG");
  447. // user dialog configuration
  448. vgui::system()->SetUserConfigFile("InGameDialogConfig.vdf", "CONFIG");
  449. g_pFullFileSystem->AddSearchPath( "platform", "PLATFORM" );
  450. }
  451. // localization
  452. g_pVGuiLocalize->AddFile( "Resource/platform_%language%.txt");
  453. g_pVGuiLocalize->AddFile( "Resource/vgui_%language%.txt");
  454. Sys_SetLastError( SYS_NO_ERROR );
  455. if ( IsPC() )
  456. {
  457. if ( !IsPosix() )
  458. {
  459. // Alfred says this is really, really old code that does some wacky crap that only
  460. // happened in the first version of HL and it's the only game that does this and
  461. // it was a steam testing type thing and we don't need to do it on Posix, etc.
  462. g_hMutex = Sys_CreateMutex( "ValvePlatformUIMutex" );
  463. g_hWaitMutex = Sys_CreateMutex( "ValvePlatformWaitMutex" );
  464. if ( g_hMutex == 0 || g_hWaitMutex == 0 || Sys_GetLastError() == SYS_ERROR_INVALID_HANDLE )
  465. {
  466. // error, can't get handle to mutex
  467. if (g_hMutex)
  468. {
  469. Sys_ReleaseMutex(g_hMutex);
  470. }
  471. if (g_hWaitMutex)
  472. {
  473. Sys_ReleaseMutex(g_hWaitMutex);
  474. }
  475. g_hMutex = NULL;
  476. g_hWaitMutex = NULL;
  477. Error("Steam Error: Could not access Steam, bad mutex\n");
  478. return;
  479. }
  480. unsigned int waitResult = Sys_WaitForSingleObject(g_hMutex, 0);
  481. if (!(waitResult == SYS_WAIT_OBJECT_0 || waitResult == SYS_WAIT_ABANDONED))
  482. {
  483. // mutex locked, need to deactivate Steam (so we have the Friends/ServerBrowser data files)
  484. // get the wait mutex, so that Steam.exe knows that we're trying to acquire ValveTrackerMutex
  485. waitResult = Sys_WaitForSingleObject(g_hWaitMutex, 0);
  486. #ifdef WIN32
  487. if (waitResult == SYS_WAIT_OBJECT_0 || waitResult == SYS_WAIT_ABANDONED)
  488. {
  489. Sys_EnumWindows(SendShutdownMsgFunc, 1);
  490. }
  491. #endif
  492. }
  493. }
  494. // Delay playing the startup music until the first frame
  495. m_bPlayGameStartupSound = true;
  496. // now we are set up to check every frame to see if we can friends/server browser
  497. m_bTryingToLoadFriends = true;
  498. m_iFriendsLoadPauseFrames = 1;
  499. }
  500. }
  501. //-----------------------------------------------------------------------------
  502. // Purpose: Validates the user has a cdkey in the registry
  503. //-----------------------------------------------------------------------------
  504. void CGameUI::ValidateCDKey()
  505. {
  506. // this check is disabled, since we have no plans for an offline version of hl2
  507. #if 0
  508. //!! hack, write out a regkey for now so developers don't have to type it in
  509. //!! undo this before release
  510. vgui::system()->SetRegistryString("HKEY_CURRENT_USER\\Software\\Valve\\Source\\Settings\\EncryptedCDKey", "QOgi:JXrJj<Eb8abkESf4Pg;OfofJwDzRsyH>AdjtyPnV[FB");
  511. // see what's in the registry
  512. if (!CCDKeyEntryDialog::IsValidWeakCDKeyInRegistry())
  513. {
  514. m_hCDKeyEntryDialog = new CCDKeyEntryDialog(NULL, false);
  515. m_hCDKeyEntryDialog->Activate();
  516. }
  517. #endif
  518. }
  519. //-----------------------------------------------------------------------------
  520. // Purpose: Finds which directory the platform resides in
  521. // Output : Returns true on success, false on failure.
  522. //-----------------------------------------------------------------------------
  523. bool CGameUI::FindPlatformDirectory(char *platformDir, int bufferSize)
  524. {
  525. platformDir[0] = '\0';
  526. if ( platformDir[0] == '\0' )
  527. {
  528. // we're not under steam, so setup using path relative to game
  529. if ( IsPC() )
  530. {
  531. #ifdef WIN32
  532. if ( ::GetModuleFileName( ( HINSTANCE )GetModuleHandle( NULL ), platformDir, bufferSize ) )
  533. {
  534. char *lastslash = strrchr(platformDir, '\\'); // this should be just before the filename
  535. if ( lastslash )
  536. {
  537. *lastslash = 0;
  538. Q_strncat(platformDir, "\\platform\\", bufferSize, COPY_ALL_CHARACTERS );
  539. return true;
  540. }
  541. }
  542. #else
  543. if ( getcwd( platformDir, bufferSize ) )
  544. {
  545. V_AppendSlash( platformDir, bufferSize );
  546. Q_strncat(platformDir, "platform", bufferSize, COPY_ALL_CHARACTERS );
  547. V_AppendSlash( platformDir, bufferSize );
  548. return true;
  549. }
  550. #endif
  551. }
  552. else
  553. {
  554. // xbox fetches the platform path from exisiting platform search path
  555. // path to executeable is not correct for xbox remote configuration
  556. if ( g_pFullFileSystem->GetSearchPath( "PLATFORM", false, platformDir, bufferSize ) )
  557. {
  558. char *pSeperator = strchr( platformDir, ';' );
  559. if ( pSeperator )
  560. *pSeperator = '\0';
  561. return true;
  562. }
  563. }
  564. Error( "Unable to determine platform directory\n" );
  565. return false;
  566. }
  567. return (platformDir[0] != 0);
  568. }
  569. //-----------------------------------------------------------------------------
  570. // Purpose: Called to Shutdown the game UI system
  571. //-----------------------------------------------------------------------------
  572. void CGameUI::Shutdown()
  573. {
  574. // notify all the modules of Shutdown
  575. g_VModuleLoader.ShutdownPlatformModules();
  576. // unload the modules them from memory
  577. g_VModuleLoader.UnloadPlatformModules();
  578. ModInfo().FreeModInfo();
  579. // release platform mutex
  580. // close the mutex
  581. if (g_hMutex)
  582. {
  583. Sys_ReleaseMutex(g_hMutex);
  584. }
  585. if (g_hWaitMutex)
  586. {
  587. Sys_ReleaseMutex(g_hWaitMutex);
  588. }
  589. BonusMapsDatabase()->WriteSaveData();
  590. steamapicontext->Clear();
  591. ConVar_Unregister();
  592. DisconnectTier3Libraries();
  593. DisconnectTier2Libraries();
  594. DisconnectTier1Libraries();
  595. }
  596. //-----------------------------------------------------------------------------
  597. // Purpose: just wraps an engine call to activate the gameUI
  598. //-----------------------------------------------------------------------------
  599. void CGameUI::ActivateGameUI()
  600. {
  601. engine->ExecuteClientCmd("gameui_activate");
  602. }
  603. //-----------------------------------------------------------------------------
  604. // Purpose: just wraps an engine call to hide the gameUI
  605. //-----------------------------------------------------------------------------
  606. void CGameUI::HideGameUI()
  607. {
  608. engine->ExecuteClientCmd("gameui_hide");
  609. }
  610. //-----------------------------------------------------------------------------
  611. // Purpose: Toggle allowing the engine to hide the game UI with the escape key
  612. //-----------------------------------------------------------------------------
  613. void CGameUI::PreventEngineHideGameUI()
  614. {
  615. engine->ExecuteClientCmd("gameui_preventescape");
  616. }
  617. //-----------------------------------------------------------------------------
  618. // Purpose: Toggle allowing the engine to hide the game UI with the escape key
  619. //-----------------------------------------------------------------------------
  620. void CGameUI::AllowEngineHideGameUI()
  621. {
  622. engine->ExecuteClientCmd("gameui_allowescape");
  623. }
  624. //-----------------------------------------------------------------------------
  625. // Purpose: Activate the game UI
  626. //-----------------------------------------------------------------------------
  627. void CGameUI::OnGameUIActivated()
  628. {
  629. m_bActivatedUI = true;
  630. // hide/show the main panel to Activate all game ui
  631. staticPanel->SetVisible( true );
  632. // pause the server in single player
  633. if ( engine->GetMaxClients() <= 1 )
  634. {
  635. engine->ClientCmd_Unrestricted( "setpause" );
  636. }
  637. SetSavedThisMenuSession( false );
  638. // notify taskbar
  639. BasePanel()->OnGameUIActivated();
  640. if ( GameClientExports() )
  641. {
  642. const char *pGameName = CommandLine()->ParmValue( "-game", "hl2" );
  643. // only want to run this for TF2
  644. if ( ( Q_stricmp( pGameName, "tf" ) == 0 ) || ( Q_stricmp( pGameName, "tf_beta" ) == 0 ) )
  645. {
  646. GameClientExports()->OnGameUIActivated();
  647. }
  648. }
  649. }
  650. //-----------------------------------------------------------------------------
  651. // Purpose: Hides the game ui, in whatever state it's in
  652. //-----------------------------------------------------------------------------
  653. void CGameUI::OnGameUIHidden()
  654. {
  655. if ( GameClientExports() )
  656. {
  657. const char *pGameName = CommandLine()->ParmValue( "-game", "hl2" );
  658. // only want to run this for TF2
  659. if ( ( Q_stricmp( pGameName, "tf" ) == 0 ) || ( Q_stricmp( pGameName, "tf_beta" ) == 0 ) )
  660. {
  661. GameClientExports()->OnGameUIHidden();
  662. }
  663. }
  664. // unpause the game when leaving the UI
  665. if ( engine->GetMaxClients() <= 1 )
  666. {
  667. engine->ClientCmd_Unrestricted("unpause");
  668. }
  669. BasePanel()->OnGameUIHidden();
  670. }
  671. //-----------------------------------------------------------------------------
  672. // Purpose: paints all the vgui elements
  673. //-----------------------------------------------------------------------------
  674. void CGameUI::RunFrame()
  675. {
  676. if ( IsX360() && m_bOpenProgressOnStart )
  677. {
  678. StartProgressBar();
  679. m_bOpenProgressOnStart = false;
  680. }
  681. // resize the background panel to the screen size
  682. int wide, tall;
  683. vgui::surface()->GetScreenSize(wide, tall);
  684. staticPanel->SetSize(wide,tall);
  685. // Run frames
  686. g_VModuleLoader.RunFrame();
  687. BasePanel()->RunFrame();
  688. // Play the start-up music the first time we run frame
  689. if ( IsPC() && m_bPlayGameStartupSound )
  690. {
  691. PlayGameStartupSound();
  692. m_bPlayGameStartupSound = false;
  693. }
  694. if ( IsPC() && ( ( IsPosix() && m_bTryingToLoadFriends ) ||
  695. ( m_bTryingToLoadFriends && m_iFriendsLoadPauseFrames-- < 1 && g_hMutex && g_hWaitMutex ) ) )
  696. {
  697. // try and load Steam platform files
  698. unsigned int waitResult = Sys_WaitForSingleObject(g_hMutex, 0);
  699. if ( IsPosix() || ( waitResult == SYS_WAIT_OBJECT_0 || waitResult == SYS_WAIT_ABANDONED ))
  700. {
  701. // we got the mutex, so load Friends/Serverbrowser
  702. // clear the loading flag
  703. m_bTryingToLoadFriends = false;
  704. g_VModuleLoader.LoadPlatformModules(&m_GameFactory, 1, false);
  705. // release the wait mutex
  706. if ( !IsPosix() )
  707. Sys_ReleaseMutex(g_hWaitMutex);
  708. // notify the game of our game name
  709. const char *fullGamePath = engine->GetGameDirectory();
  710. const char *pathSep = strrchr( fullGamePath, '/' );
  711. if ( !pathSep )
  712. {
  713. pathSep = strrchr( fullGamePath, '\\' );
  714. }
  715. if ( pathSep )
  716. {
  717. KeyValues *pKV = new KeyValues("ActiveGameName" );
  718. pKV->SetString( "name", pathSep + 1 );
  719. pKV->SetInt( "appid", engine->GetAppID() );
  720. KeyValues *modinfo = new KeyValues("ModInfo");
  721. if ( modinfo->LoadFromFile( g_pFullFileSystem, "gameinfo.txt" ) )
  722. {
  723. pKV->SetString( "game", modinfo->GetString( "game", "" ) );
  724. }
  725. modinfo->deleteThis();
  726. g_VModuleLoader.PostMessageToAllModules( pKV );
  727. }
  728. // notify the ui of a game connect if we're already in a game
  729. if (m_iGameIP)
  730. {
  731. SendConnectedToGameMessage();
  732. }
  733. }
  734. }
  735. }
  736. //-----------------------------------------------------------------------------
  737. // Purpose: Called when the game connects to a server
  738. //-----------------------------------------------------------------------------
  739. void CGameUI::OLD_OnConnectToServer(const char *game, int IP, int port)
  740. {
  741. // Nobody should use this anymore because the query port and the connection port can be different.
  742. // Use OnConnectToServer2 instead.
  743. Assert( false );
  744. OnConnectToServer2( game, IP, port, port );
  745. }
  746. //-----------------------------------------------------------------------------
  747. // Purpose: Called when the game connects to a server
  748. //-----------------------------------------------------------------------------
  749. void CGameUI::OnConnectToServer2(const char *game, int IP, int connectionPort, int queryPort)
  750. {
  751. m_iGameIP = IP;
  752. m_iGameConnectionPort = connectionPort;
  753. m_iGameQueryPort = queryPort;
  754. SendConnectedToGameMessage();
  755. }
  756. void CGameUI::SendConnectedToGameMessage()
  757. {
  758. KeyValues *kv = new KeyValues( "ConnectedToGame" );
  759. kv->SetInt( "ip", m_iGameIP );
  760. kv->SetInt( "connectionport", m_iGameConnectionPort );
  761. kv->SetInt( "queryport", m_iGameQueryPort );
  762. g_VModuleLoader.PostMessageToAllModules( kv );
  763. }
  764. //-----------------------------------------------------------------------------
  765. // Purpose: Called when the game disconnects from a server
  766. //-----------------------------------------------------------------------------
  767. void CGameUI::OnDisconnectFromServer( uint8 eSteamLoginFailure )
  768. {
  769. m_iGameIP = 0;
  770. m_iGameConnectionPort = 0;
  771. m_iGameQueryPort = 0;
  772. g_VModuleLoader.PostMessageToAllModules(new KeyValues("DisconnectedFromGame"));
  773. if ( eSteamLoginFailure == STEAMLOGINFAILURE_NOSTEAMLOGIN )
  774. {
  775. if ( g_hLoadingDialog )
  776. {
  777. g_hLoadingDialog->DisplayNoSteamConnectionError();
  778. }
  779. }
  780. else if ( eSteamLoginFailure == STEAMLOGINFAILURE_VACBANNED )
  781. {
  782. if ( g_hLoadingDialog )
  783. {
  784. g_hLoadingDialog->DisplayVACBannedError();
  785. }
  786. }
  787. else if ( eSteamLoginFailure == STEAMLOGINFAILURE_LOGGED_IN_ELSEWHERE )
  788. {
  789. if ( g_hLoadingDialog )
  790. {
  791. g_hLoadingDialog->DisplayLoggedInElsewhereError();
  792. }
  793. }
  794. }
  795. //-----------------------------------------------------------------------------
  796. // Purpose: activates the loading dialog on level load start
  797. //-----------------------------------------------------------------------------
  798. void CGameUI::OnLevelLoadingStarted( bool bShowProgressDialog )
  799. {
  800. g_VModuleLoader.PostMessageToAllModules( new KeyValues( "LoadingStarted" ) );
  801. // notify
  802. BasePanel()->OnLevelLoadingStarted();
  803. if ( bShowProgressDialog )
  804. {
  805. StartProgressBar();
  806. }
  807. // Don't play the start game sound if this happens before we get to the first frame
  808. m_bPlayGameStartupSound = false;
  809. }
  810. //-----------------------------------------------------------------------------
  811. // Purpose: closes any level load dialog
  812. //-----------------------------------------------------------------------------
  813. void CGameUI::OnLevelLoadingFinished(bool bError, const char *failureReason, const char *extendedReason)
  814. {
  815. StopProgressBar( bError, failureReason, extendedReason );
  816. // notify all the modules
  817. g_VModuleLoader.PostMessageToAllModules( new KeyValues( "LoadingFinished" ) );
  818. // hide the UI
  819. HideGameUI();
  820. // notify
  821. BasePanel()->OnLevelLoadingFinished();
  822. }
  823. //-----------------------------------------------------------------------------
  824. // Purpose: Updates progress bar
  825. // Output : Returns true if screen should be redrawn
  826. //-----------------------------------------------------------------------------
  827. bool CGameUI::UpdateProgressBar(float progress, const char *statusText)
  828. {
  829. // if either the progress bar or the status text changes, redraw the screen
  830. bool bRedraw = false;
  831. if ( ContinueProgressBar( progress ) )
  832. {
  833. bRedraw = true;
  834. }
  835. if ( SetProgressBarStatusText( statusText ) )
  836. {
  837. bRedraw = true;
  838. }
  839. return bRedraw;
  840. }
  841. //-----------------------------------------------------------------------------
  842. // Purpose:
  843. //-----------------------------------------------------------------------------
  844. void CGameUI::StartProgressBar()
  845. {
  846. if ( !g_hLoadingDialog.Get() )
  847. {
  848. g_hLoadingDialog = new CLoadingDialog(staticPanel);
  849. }
  850. // open a loading dialog
  851. m_szPreviousStatusText[0] = 0;
  852. g_hLoadingDialog->SetProgressPoint(0.0f);
  853. g_hLoadingDialog->Open();
  854. }
  855. //-----------------------------------------------------------------------------
  856. // Purpose: returns true if the screen should be updated
  857. //-----------------------------------------------------------------------------
  858. bool CGameUI::ContinueProgressBar( float progressFraction )
  859. {
  860. if (!g_hLoadingDialog.Get())
  861. return false;
  862. g_hLoadingDialog->Activate();
  863. return g_hLoadingDialog->SetProgressPoint(progressFraction);
  864. }
  865. //-----------------------------------------------------------------------------
  866. // Purpose: stops progress bar, displays error if necessary
  867. //-----------------------------------------------------------------------------
  868. void CGameUI::StopProgressBar(bool bError, const char *failureReason, const char *extendedReason)
  869. {
  870. if (!g_hLoadingDialog.Get() && bError)
  871. {
  872. g_hLoadingDialog = new CLoadingDialog(staticPanel);
  873. }
  874. if (!g_hLoadingDialog.Get())
  875. return;
  876. if ( !IsX360() && bError )
  877. {
  878. // turn the dialog to error display mode
  879. g_hLoadingDialog->DisplayGenericError(failureReason, extendedReason);
  880. }
  881. else
  882. {
  883. // close loading dialog
  884. g_hLoadingDialog->Close();
  885. g_hLoadingDialog = NULL;
  886. }
  887. // should update the background to be in a transition here
  888. }
  889. //-----------------------------------------------------------------------------
  890. // Purpose: sets loading info text
  891. //-----------------------------------------------------------------------------
  892. bool CGameUI::SetProgressBarStatusText(const char *statusText)
  893. {
  894. if (!g_hLoadingDialog.Get())
  895. return false;
  896. if (!statusText)
  897. return false;
  898. if (!stricmp(statusText, m_szPreviousStatusText))
  899. return false;
  900. g_hLoadingDialog->SetStatusText(statusText);
  901. Q_strncpy(m_szPreviousStatusText, statusText, sizeof(m_szPreviousStatusText));
  902. return true;
  903. }
  904. //-----------------------------------------------------------------------------
  905. // Purpose:
  906. //-----------------------------------------------------------------------------
  907. void CGameUI::SetSecondaryProgressBar(float progress /* range [0..1] */)
  908. {
  909. if (!g_hLoadingDialog.Get())
  910. return;
  911. g_hLoadingDialog->SetSecondaryProgress(progress);
  912. }
  913. //-----------------------------------------------------------------------------
  914. // Purpose:
  915. //-----------------------------------------------------------------------------
  916. void CGameUI::SetSecondaryProgressBarText(const char *statusText)
  917. {
  918. if (!g_hLoadingDialog.Get())
  919. return;
  920. g_hLoadingDialog->SetSecondaryProgressText(statusText);
  921. }
  922. //-----------------------------------------------------------------------------
  923. // Purpose: Returns prev settings
  924. //-----------------------------------------------------------------------------
  925. bool CGameUI::SetShowProgressText( bool show )
  926. {
  927. if (!g_hLoadingDialog.Get())
  928. return false;
  929. return g_hLoadingDialog->SetShowProgressText( show );
  930. }
  931. //-----------------------------------------------------------------------------
  932. // Purpose: returns true if we're currently playing the game
  933. //-----------------------------------------------------------------------------
  934. bool CGameUI::IsInLevel()
  935. {
  936. const char *levelName = engine->GetLevelName();
  937. if (levelName && levelName[0] && !engine->IsLevelMainMenuBackground())
  938. {
  939. return true;
  940. }
  941. return false;
  942. }
  943. //-----------------------------------------------------------------------------
  944. // Purpose: returns true if we're at the main menu and a background level is loaded
  945. //-----------------------------------------------------------------------------
  946. bool CGameUI::IsInBackgroundLevel()
  947. {
  948. const char *levelName = engine->GetLevelName();
  949. if (levelName && levelName[0] && engine->IsLevelMainMenuBackground())
  950. {
  951. return true;
  952. }
  953. return false;
  954. }
  955. //-----------------------------------------------------------------------------
  956. // Purpose: returns true if we're in a multiplayer game
  957. //-----------------------------------------------------------------------------
  958. bool CGameUI::IsInMultiplayer()
  959. {
  960. return (IsInLevel() && engine->GetMaxClients() > 1);
  961. }
  962. //-----------------------------------------------------------------------------
  963. // Purpose: returns true if we're playing back a replay
  964. //-----------------------------------------------------------------------------
  965. bool CGameUI::IsInReplay()
  966. {
  967. return g_pEngineClientReplay && g_pEngineClientReplay->IsPlayingReplayDemo();
  968. }
  969. //-----------------------------------------------------------------------------
  970. // Purpose: returns true if we're console ui
  971. //-----------------------------------------------------------------------------
  972. bool CGameUI::IsConsoleUI()
  973. {
  974. return m_bIsConsoleUI;
  975. }
  976. //-----------------------------------------------------------------------------
  977. // Purpose: returns true if we've saved without closing the menu
  978. //-----------------------------------------------------------------------------
  979. bool CGameUI::HasSavedThisMenuSession()
  980. {
  981. return m_bHasSavedThisMenuSession;
  982. }
  983. void CGameUI::SetSavedThisMenuSession( bool bState )
  984. {
  985. m_bHasSavedThisMenuSession = bState;
  986. }
  987. //-----------------------------------------------------------------------------
  988. // Purpose:
  989. //-----------------------------------------------------------------------------
  990. void CGameUI::ShowNewGameDialog( int chapter )
  991. {
  992. char val[32];
  993. Q_snprintf( val, sizeof(val), "%d", chapter);
  994. staticPanel->OnOpenNewGameDialog(val);
  995. }
  996. //-----------------------------------------------------------------------------
  997. // Purpose: Makes the loading background dialog visible, if one has been set
  998. //-----------------------------------------------------------------------------
  999. void CGameUI::ShowLoadingBackgroundDialog()
  1000. {
  1001. if ( g_hLoadingBackgroundDialog )
  1002. {
  1003. vgui::ipanel()->SetParent( g_hLoadingBackgroundDialog, staticPanel->GetVPanel() );
  1004. vgui::ipanel()->PerformApplySchemeSettings( g_hLoadingBackgroundDialog );
  1005. vgui::ipanel()->SetVisible( g_hLoadingBackgroundDialog, true );
  1006. vgui::ipanel()->MoveToFront( g_hLoadingBackgroundDialog );
  1007. vgui::ipanel()->SendMessage( g_hLoadingBackgroundDialog, new KeyValues( "activate" ), staticPanel->GetVPanel() );
  1008. }
  1009. }
  1010. //-----------------------------------------------------------------------------
  1011. // Purpose: Hides the loading background dialog, if one has been set
  1012. //-----------------------------------------------------------------------------
  1013. void CGameUI::HideLoadingBackgroundDialog()
  1014. {
  1015. if ( g_hLoadingBackgroundDialog )
  1016. {
  1017. vgui::ipanel()->SetParent( g_hLoadingBackgroundDialog, NULL );
  1018. vgui::ipanel()->SetVisible( g_hLoadingBackgroundDialog, false );
  1019. vgui::ipanel()->MoveToBack( g_hLoadingBackgroundDialog );
  1020. vgui::ipanel()->SendMessage( g_hLoadingBackgroundDialog, new KeyValues( "deactivate" ), staticPanel->GetVPanel() );
  1021. }
  1022. }
  1023. //-----------------------------------------------------------------------------
  1024. // Purpose: Returns whether a loading background dialog has been set
  1025. //-----------------------------------------------------------------------------
  1026. bool CGameUI::HasLoadingBackgroundDialog()
  1027. {
  1028. return ( 0 != g_hLoadingBackgroundDialog );
  1029. }
  1030. //-----------------------------------------------------------------------------
  1031. // Purpose: Xbox 360 calls from engine to GameUI
  1032. //-----------------------------------------------------------------------------
  1033. void CGameUI::SessionNotification( const int notification, const int param )
  1034. {
  1035. BasePanel()->SessionNotification( notification, param );
  1036. }
  1037. void CGameUI::SystemNotification( const int notification )
  1038. {
  1039. BasePanel()->SystemNotification( notification );
  1040. }
  1041. void CGameUI::ShowMessageDialog( const uint nType, vgui::Panel *pOwner )
  1042. {
  1043. BasePanel()->ShowMessageDialog( nType, pOwner );
  1044. }
  1045. void CGameUI::CloseMessageDialog( const uint nType )
  1046. {
  1047. BasePanel()->CloseMessageDialog( nType );
  1048. }
  1049. void CGameUI::UpdatePlayerInfo( uint64 nPlayerId, const char *pName, int nTeam, byte cVoiceState, int nPlayersNeeded, bool bHost )
  1050. {
  1051. BasePanel()->UpdatePlayerInfo( nPlayerId, pName, nTeam, cVoiceState, nPlayersNeeded, bHost );
  1052. }
  1053. void CGameUI::SessionSearchResult( int searchIdx, void *pHostData, XSESSION_SEARCHRESULT *pResult, int ping )
  1054. {
  1055. BasePanel()->SessionSearchResult( searchIdx, pHostData, pResult, ping );
  1056. }
  1057. void CGameUI::OnCreditsFinished( void )
  1058. {
  1059. BasePanel()->OnCreditsFinished();
  1060. }
  1061. bool CGameUI::ValidateStorageDevice( int *pStorageDeviceValidated )
  1062. {
  1063. return BasePanel()->ValidateStorageDevice( pStorageDeviceValidated );
  1064. }
  1065. void CGameUI::SetProgressOnStart()
  1066. {
  1067. m_bOpenProgressOnStart = true;
  1068. }
  1069. void CGameUI::OnConfirmQuit( void )
  1070. {
  1071. BasePanel()->OnOpenQuitConfirmationDialog();
  1072. }
  1073. bool CGameUI::IsMainMenuVisible( void )
  1074. {
  1075. CBasePanel *pBasePanel = BasePanel();
  1076. if ( pBasePanel )
  1077. return (pBasePanel->IsVisible() && pBasePanel->GetMenuAlpha() > 0 );
  1078. return false;
  1079. }
  1080. // Client DLL is providing us with a panel that it wants to replace the main menu with
  1081. void CGameUI::SetMainMenuOverride( vgui::VPANEL panel )
  1082. {
  1083. CBasePanel *pBasePanel = BasePanel();
  1084. if ( pBasePanel )
  1085. {
  1086. pBasePanel->SetMainMenuOverride( panel );
  1087. }
  1088. }
  1089. // Client DLL is telling us that a main menu command was issued, probably from its custom main menu panel
  1090. void CGameUI::SendMainMenuCommand( const char *pszCommand )
  1091. {
  1092. CBasePanel *pBasePanel = BasePanel();
  1093. if ( pBasePanel )
  1094. {
  1095. pBasePanel->RunMenuCommand( pszCommand );
  1096. }
  1097. }