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.

887 lines
24 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "inputsystem/iinputsystem.h"
  9. #include "input.h"
  10. #include <cdll_client_int.h>
  11. #include <globalvars_base.h>
  12. #include <cdll_util.h>
  13. #include <KeyValues.h>
  14. #include "spectatorgui.h"
  15. #include <vgui/IScheme.h>
  16. #include <vgui/ILocalize.h>
  17. #include <vgui/ISurface.h>
  18. #include <vgui/IPanel.h>
  19. #include <vgui_controls/ImageList.h>
  20. #include <vgui_controls/MenuItem.h>
  21. #include <vgui_controls/TextImage.h>
  22. #include <stdio.h> // _snprintf define
  23. #include <game/client/iviewport.h>
  24. #include "commandmenu.h"
  25. #include "hltvcamera.h"
  26. #if defined( REPLAY_ENABLED )
  27. #include "replay/replaycamera.h"
  28. #endif
  29. #include <vgui_controls/TextEntry.h>
  30. #include <vgui_controls/Panel.h>
  31. #include <vgui_controls/ImagePanel.h>
  32. #include <vgui_controls/Menu.h>
  33. #include "IGameUIFuncs.h" // for key bindings
  34. #include <imapoverview.h>
  35. #include <shareddefs.h>
  36. #include <igameresources.h>
  37. #ifdef TF_CLIENT_DLL
  38. #include "tf_gamerules.h"
  39. void AddSubKeyNamed( KeyValues *pKeys, const char *pszName );
  40. #endif
  41. // memdbgon must be the last include file in a .cpp file!!!
  42. #include "tier0/memdbgon.h"
  43. #ifndef _XBOX
  44. extern IGameUIFuncs *gameuifuncs; // for key binding details
  45. #endif
  46. // void DuckMessage(const char *str); // from vgui_teamfortressviewport.cpp
  47. ConVar spec_scoreboard( "spec_scoreboard", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
  48. CSpectatorGUI *g_pSpectatorGUI = NULL;
  49. // NB disconnect between localization text and observer mode enums
  50. static const char *s_SpectatorModes[] =
  51. {
  52. "#Spec_Mode0", // OBS_MODE_NONE = 0,
  53. "#Spec_Mode1", // OBS_MODE_DEATHCAM,
  54. "", // OBS_MODE_FREEZECAM,
  55. "#Spec_Mode2", // OBS_MODE_FIXED,
  56. "#Spec_Mode3", // OBS_MODE_IN_EYE,
  57. "#Spec_Mode4", // OBS_MODE_CHASE,
  58. "#Spec_Mode_POI", // OBS_MODE_POI, PASSTIME
  59. "#Spec_Mode5", // OBS_MODE_ROAMING,
  60. };
  61. using namespace vgui;
  62. ConVar cl_spec_mode(
  63. "cl_spec_mode",
  64. "1",
  65. FCVAR_ARCHIVE | FCVAR_USERINFO | FCVAR_SERVER_CAN_EXECUTE,
  66. "spectator mode" );
  67. //-----------------------------------------------------------------------------
  68. // Purpose: left and right buttons pointing buttons
  69. //-----------------------------------------------------------------------------
  70. class CSpecButton : public Button
  71. {
  72. public:
  73. CSpecButton(Panel *parent, const char *panelName): Button(parent, panelName, "") {}
  74. private:
  75. void ApplySchemeSettings(vgui::IScheme *pScheme)
  76. {
  77. Button::ApplySchemeSettings(pScheme);
  78. SetFont(pScheme->GetFont("Marlett", IsProportional()) );
  79. }
  80. };
  81. //-----------------------------------------------------------------------------
  82. // Purpose: Constructor
  83. //-----------------------------------------------------------------------------
  84. CSpectatorMenu::CSpectatorMenu( IViewPort *pViewPort ) : Frame( NULL, PANEL_SPECMENU )
  85. {
  86. m_iDuckKey = BUTTON_CODE_INVALID;
  87. m_pViewPort = pViewPort;
  88. SetMouseInputEnabled( true );
  89. SetKeyBoardInputEnabled( true );
  90. SetTitleBarVisible( false ); // don't draw a title bar
  91. SetMoveable( false );
  92. SetSizeable( false );
  93. SetProportional(true);
  94. SetScheme("ClientScheme");
  95. m_pPlayerList = new ComboBox(this, "playercombo", 10 , false);
  96. HFont hFallbackFont = scheme()->GetIScheme( GetScheme() )->GetFont( "DefaultVerySmallFallBack", false );
  97. if ( INVALID_FONT != hFallbackFont )
  98. {
  99. m_pPlayerList->SetUseFallbackFont( true, hFallbackFont );
  100. }
  101. m_pViewOptions = new ComboBox(this, "viewcombo", 10 , false );
  102. m_pConfigSettings = new ComboBox(this, "settingscombo", 10 , false );
  103. m_pLeftButton = new CSpecButton( this, "specprev");
  104. m_pLeftButton->SetText("3");
  105. m_pRightButton = new CSpecButton( this, "specnext");
  106. m_pRightButton->SetText("4");
  107. m_pPlayerList->SetText("");
  108. m_pViewOptions->SetText("#Spec_Modes");
  109. m_pConfigSettings->SetText("#Spec_Options");
  110. m_pPlayerList->SetOpenDirection( Menu::UP );
  111. m_pViewOptions->SetOpenDirection( Menu::UP );
  112. m_pConfigSettings->SetOpenDirection( Menu::UP );
  113. // create view config menu
  114. CommandMenu * menu = new CommandMenu(m_pConfigSettings, "spectatormenu", gViewPortInterface);
  115. menu->LoadFromFile( "Resource/spectatormenu.res" );
  116. m_pConfigSettings->SetMenu( menu ); // attach menu to combo box
  117. // create view mode menu
  118. menu = new CommandMenu(m_pViewOptions, "spectatormodes", gViewPortInterface);
  119. menu->LoadFromFile("Resource/spectatormodes.res");
  120. m_pViewOptions->SetMenu( menu ); // attach menu to combo box
  121. LoadControlSettings( "Resource/UI/BottomSpectator.res" );
  122. ListenForGameEvent( "spec_target_updated" );
  123. }
  124. void CSpectatorMenu::ApplySchemeSettings(IScheme *pScheme)
  125. {
  126. BaseClass::ApplySchemeSettings(pScheme);
  127. // need to MakeReadyForUse() on the menus so we can set their bg color before they are displayed
  128. m_pConfigSettings->GetMenu()->MakeReadyForUse();
  129. m_pViewOptions->GetMenu()->MakeReadyForUse();
  130. m_pPlayerList->GetMenu()->MakeReadyForUse();
  131. if ( g_pSpectatorGUI )
  132. {
  133. m_pConfigSettings->GetMenu()->SetBgColor( g_pSpectatorGUI->GetBlackBarColor() );
  134. m_pViewOptions->GetMenu()->SetBgColor( g_pSpectatorGUI->GetBlackBarColor() );
  135. m_pPlayerList->GetMenu()->SetBgColor( g_pSpectatorGUI->GetBlackBarColor() );
  136. }
  137. }
  138. //-----------------------------------------------------------------------------
  139. // Purpose: makes the GUI fill the screen
  140. //-----------------------------------------------------------------------------
  141. void CSpectatorMenu::PerformLayout()
  142. {
  143. int w,h;
  144. GetHudSize(w, h);
  145. // fill the screen
  146. SetSize(w,GetTall());
  147. }
  148. //-----------------------------------------------------------------------------
  149. // Purpose: Handles changes to combo boxes
  150. //-----------------------------------------------------------------------------
  151. void CSpectatorMenu::OnTextChanged(KeyValues *data)
  152. {
  153. Panel *panel = reinterpret_cast<vgui::Panel *>( data->GetPtr("panel") );
  154. vgui::ComboBox *box = dynamic_cast<vgui::ComboBox *>( panel );
  155. if( box == m_pConfigSettings) // don't change the text in the config setting combo
  156. {
  157. m_pConfigSettings->SetText("#Spec_Options");
  158. }
  159. else if ( box == m_pPlayerList )
  160. {
  161. KeyValues *kv = box->GetActiveItemUserData();
  162. if ( kv && GameResources() )
  163. {
  164. const char *player = kv->GetString("player");
  165. int currentPlayerNum = GetSpectatorTarget();
  166. const char *currentPlayerName = GameResources()->GetPlayerName( currentPlayerNum );
  167. if ( !FStrEq( currentPlayerName, player ) )
  168. {
  169. char command[128];
  170. Q_snprintf( command, sizeof(command), "spec_player \"%s\"", player );
  171. engine->ClientCmd( command );
  172. }
  173. }
  174. }
  175. }
  176. void CSpectatorMenu::OnCommand( const char *command )
  177. {
  178. if (!stricmp(command, "specnext") )
  179. {
  180. engine->ClientCmd("spec_next");
  181. }
  182. else if (!stricmp(command, "specprev") )
  183. {
  184. engine->ClientCmd("spec_prev");
  185. }
  186. }
  187. void CSpectatorMenu::FireGameEvent( IGameEvent * event )
  188. {
  189. const char *pEventName = event->GetName();
  190. if ( Q_strcmp( "spec_target_updated", pEventName ) == 0 )
  191. {
  192. IGameResources *gr = GameResources();
  193. if ( !gr )
  194. return;
  195. // make sure the player combo box is up to date
  196. int playernum = GetSpectatorTarget();
  197. if ( playernum < 1 || playernum > MAX_PLAYERS )
  198. return;
  199. const char *selectedPlayerName = gr->GetPlayerName( playernum );
  200. const char *currentPlayerName = "";
  201. KeyValues *kv = m_pPlayerList->GetActiveItemUserData();
  202. if ( kv )
  203. {
  204. currentPlayerName = kv->GetString( "player" );
  205. }
  206. if ( !FStrEq( currentPlayerName, selectedPlayerName ) )
  207. {
  208. for ( int i=0; i<m_pPlayerList->GetItemCount(); ++i )
  209. {
  210. KeyValues *pKv = m_pPlayerList->GetItemUserData( i );
  211. if ( pKv && FStrEq( pKv->GetString( "player" ), selectedPlayerName ) )
  212. {
  213. m_pPlayerList->ActivateItemByRow( i );
  214. break;
  215. }
  216. }
  217. }
  218. }
  219. }
  220. //-----------------------------------------------------------------------------
  221. // Purpose: when duck is pressed it hides the active part of the GUI
  222. //-----------------------------------------------------------------------------
  223. void CSpectatorMenu::OnKeyCodePressed(KeyCode code)
  224. {
  225. if ( code == m_iDuckKey )
  226. {
  227. // hide if DUCK is pressed again
  228. m_pViewPort->ShowPanel( this, false );
  229. }
  230. }
  231. void CSpectatorMenu::ShowPanel(bool bShow)
  232. {
  233. if ( BaseClass::IsVisible() == bShow )
  234. return;
  235. if ( bShow )
  236. {
  237. // Force relayout in case Steam Controller stuff has changed.
  238. InvalidateLayout( true, true );
  239. Activate();
  240. SetMouseInputEnabled( true );
  241. SetKeyBoardInputEnabled( true );
  242. }
  243. else
  244. {
  245. SetVisible( false );
  246. SetMouseInputEnabled( false );
  247. SetKeyBoardInputEnabled( false );
  248. }
  249. bool bIsEnabled = true;
  250. if ( engine->IsHLTV() && HLTVCamera()->IsPVSLocked() )
  251. {
  252. // when watching HLTV or Replay with a locked PVS, some elements are disabled
  253. bIsEnabled = false;
  254. }
  255. m_pLeftButton->SetVisible( bIsEnabled );
  256. m_pRightButton->SetVisible( bIsEnabled );
  257. m_pPlayerList->SetVisible( bIsEnabled );
  258. m_pViewOptions->SetVisible( bIsEnabled );
  259. }
  260. void CSpectatorMenu::Update( void )
  261. {
  262. IGameResources *gr = GameResources();
  263. Reset();
  264. if ( m_iDuckKey == BUTTON_CODE_INVALID )
  265. {
  266. m_iDuckKey = gameuifuncs->GetButtonCodeForBind( "duck" );
  267. }
  268. if ( !gr )
  269. return;
  270. int iPlayerIndex;
  271. for ( iPlayerIndex = 1 ; iPlayerIndex <= gpGlobals->maxClients; iPlayerIndex++ )
  272. {
  273. // does this slot in the array have a name?
  274. if ( !gr->IsConnected( iPlayerIndex ) )
  275. continue;
  276. if ( gr->IsLocalPlayer( iPlayerIndex ) )
  277. continue;
  278. if ( !gr->IsAlive( iPlayerIndex ) )
  279. continue;
  280. wchar_t playerText[ 80 ], playerName[ 64 ], *team, teamText[ 64 ];
  281. char localizeTeamName[64];
  282. char szPlayerIndex[16];
  283. g_pVGuiLocalize->ConvertANSIToUnicode( UTIL_SafeName( gr->GetPlayerName(iPlayerIndex) ), playerName, sizeof( playerName ) );
  284. const char * teamname = gr->GetTeamName( gr->GetTeam(iPlayerIndex) );
  285. if ( teamname )
  286. {
  287. Q_snprintf( localizeTeamName, sizeof( localizeTeamName ), "#%s", teamname );
  288. team=g_pVGuiLocalize->Find( localizeTeamName );
  289. if ( !team )
  290. {
  291. g_pVGuiLocalize->ConvertANSIToUnicode( teamname , teamText, sizeof( teamText ) );
  292. team = teamText;
  293. }
  294. g_pVGuiLocalize->ConstructString_safe( playerText, g_pVGuiLocalize->Find( "#Spec_PlayerItem_Team" ), 2, playerName, team );
  295. }
  296. else
  297. {
  298. g_pVGuiLocalize->ConstructString_safe( playerText, g_pVGuiLocalize->Find( "#Spec_PlayerItem" ), 1, playerName );
  299. }
  300. Q_snprintf( szPlayerIndex, sizeof( szPlayerIndex ), "%d", iPlayerIndex );
  301. KeyValues *kv = new KeyValues( "UserData", "player", gr->GetPlayerName( iPlayerIndex ), "index", szPlayerIndex );
  302. m_pPlayerList->AddItem( playerText, kv );
  303. kv->deleteThis();
  304. }
  305. // make sure the player combo box is up to date
  306. int playernum = GetSpectatorTarget();
  307. const char *selectedPlayerName = gr->GetPlayerName( playernum );
  308. for ( iPlayerIndex=0; iPlayerIndex<m_pPlayerList->GetItemCount(); ++iPlayerIndex )
  309. {
  310. KeyValues *kv = m_pPlayerList->GetItemUserData( iPlayerIndex );
  311. if ( kv && FStrEq( kv->GetString( "player" ), selectedPlayerName ) )
  312. {
  313. m_pPlayerList->ActivateItemByRow( iPlayerIndex );
  314. break;
  315. }
  316. }
  317. //=============================================================================
  318. // HPE_BEGIN:
  319. // [pfreese] make sure the view mode combo box is up to date - the spectator
  320. // mode can be changed multiple ways
  321. //=============================================================================
  322. int specmode = GetSpectatorMode();
  323. m_pViewOptions->SetText(s_SpectatorModes[specmode]);
  324. //=============================================================================
  325. // HPE_END
  326. //=============================================================================
  327. }
  328. //-----------------------------------------------------------------------------
  329. // main spectator panel
  330. //-----------------------------------------------------------------------------
  331. // Purpose: Constructor
  332. //-----------------------------------------------------------------------------
  333. CSpectatorGUI::CSpectatorGUI(IViewPort *pViewPort) : EditablePanel( NULL, PANEL_SPECGUI )
  334. {
  335. // m_bHelpShown = false;
  336. // m_bInsetVisible = false;
  337. // m_iDuckKey = KEY_NONE;
  338. SetSize( 10, 10 ); // Quiet "parent not sized yet" spew
  339. m_bSpecScoreboard = false;
  340. m_pViewPort = pViewPort;
  341. g_pSpectatorGUI = this;
  342. // initialize dialog
  343. SetVisible(false);
  344. SetProportional(true);
  345. // load the new scheme early!!
  346. SetScheme("ClientScheme");
  347. SetMouseInputEnabled( false );
  348. SetKeyBoardInputEnabled( false );
  349. m_pTopBar = new Panel( this, "topbar" );
  350. m_pBottomBarBlank = new Panel( this, "bottombarblank" );
  351. // m_pBannerImage = new ImagePanel( m_pTopBar, NULL );
  352. m_pPlayerLabel = new Label( this, "playerlabel", "" );
  353. m_pPlayerLabel->SetVisible( false );
  354. TextImage *image = m_pPlayerLabel->GetTextImage();
  355. if ( image )
  356. {
  357. HFont hFallbackFont = scheme()->GetIScheme( GetScheme() )->GetFont( "DefaultVerySmallFallBack", false );
  358. if ( INVALID_FONT != hFallbackFont )
  359. {
  360. image->SetUseFallbackFont( true, hFallbackFont );
  361. }
  362. }
  363. SetPaintBorderEnabled(false);
  364. SetPaintBackgroundEnabled(false);
  365. // m_pBannerImage->SetVisible(false);
  366. InvalidateLayout();
  367. }
  368. //-----------------------------------------------------------------------------
  369. // Purpose: Destructor
  370. //-----------------------------------------------------------------------------
  371. CSpectatorGUI::~CSpectatorGUI()
  372. {
  373. g_pSpectatorGUI = NULL;
  374. }
  375. //-----------------------------------------------------------------------------
  376. // Purpose: Sets the colour of the top and bottom bars
  377. //-----------------------------------------------------------------------------
  378. void CSpectatorGUI::ApplySchemeSettings(IScheme *pScheme)
  379. {
  380. KeyValues *pConditions = NULL;
  381. #ifdef TF_CLIENT_DLL
  382. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  383. {
  384. pConditions = new KeyValues( "conditions" );
  385. AddSubKeyNamed( pConditions, "if_mvm" );
  386. }
  387. #endif
  388. LoadControlSettings( GetResFile(), NULL, NULL, pConditions );
  389. if ( pConditions )
  390. {
  391. pConditions->deleteThis();
  392. }
  393. m_pBottomBarBlank->SetVisible( true );
  394. m_pTopBar->SetVisible( true );
  395. BaseClass::ApplySchemeSettings( pScheme );
  396. SetBgColor(Color( 0,0,0,0 ) ); // make the background transparent
  397. m_pTopBar->SetBgColor(GetBlackBarColor());
  398. m_pBottomBarBlank->SetBgColor(GetBlackBarColor());
  399. // m_pBottomBar->SetBgColor(Color( 0,0,0,0 ));
  400. SetPaintBorderEnabled(false);
  401. SetBorder( NULL );
  402. #ifdef CSTRIKE_DLL
  403. SetZPos(80); // guarantee it shows above the scope
  404. #endif
  405. }
  406. //-----------------------------------------------------------------------------
  407. // Purpose: makes the GUI fill the screen
  408. //-----------------------------------------------------------------------------
  409. void CSpectatorGUI::PerformLayout()
  410. {
  411. int w,h,x,y;
  412. GetHudSize(w, h);
  413. // fill the screen
  414. SetBounds(0,0,w,h);
  415. // stretch the bottom bar across the screen
  416. m_pBottomBarBlank->GetPos(x,y);
  417. m_pBottomBarBlank->SetSize( w, h - y );
  418. }
  419. //-----------------------------------------------------------------------------
  420. // Purpose: checks spec_scoreboard cvar to see if the scoreboard should be displayed
  421. //-----------------------------------------------------------------------------
  422. void CSpectatorGUI::OnThink()
  423. {
  424. BaseClass::OnThink();
  425. if ( IsVisible() )
  426. {
  427. if ( m_bSpecScoreboard != spec_scoreboard.GetBool() )
  428. {
  429. if ( !spec_scoreboard.GetBool() || !gViewPortInterface->GetActivePanel() )
  430. {
  431. m_bSpecScoreboard = spec_scoreboard.GetBool();
  432. gViewPortInterface->ShowPanel( PANEL_SCOREBOARD, m_bSpecScoreboard );
  433. }
  434. }
  435. #ifdef TF_CLIENT_DLL
  436. if ( TFGameRules() && TFGameRules()->ShowMatchSummary() )
  437. {
  438. SetVisible( false );
  439. }
  440. #endif
  441. }
  442. }
  443. //-----------------------------------------------------------------------------
  444. // Purpose: sets the image to display for the banner in the top right corner
  445. //-----------------------------------------------------------------------------
  446. void CSpectatorGUI::SetLogoImage(const char *image)
  447. {
  448. if ( m_pBannerImage )
  449. {
  450. m_pBannerImage->SetImage( scheme()->GetImage(image, false) );
  451. }
  452. }
  453. //-----------------------------------------------------------------------------
  454. // Purpose: Sets the text of a control by name
  455. //-----------------------------------------------------------------------------
  456. void CSpectatorGUI::SetLabelText(const char *textEntryName, const char *text)
  457. {
  458. Label *entry = dynamic_cast<Label *>(FindChildByName(textEntryName));
  459. if (entry)
  460. {
  461. entry->SetText(text);
  462. }
  463. }
  464. //-----------------------------------------------------------------------------
  465. // Purpose: Sets the text of a control by name
  466. //-----------------------------------------------------------------------------
  467. void CSpectatorGUI::SetLabelText(const char *textEntryName, wchar_t *text)
  468. {
  469. Label *entry = dynamic_cast<Label *>(FindChildByName(textEntryName));
  470. if (entry)
  471. {
  472. entry->SetText(text);
  473. }
  474. }
  475. //-----------------------------------------------------------------------------
  476. // Purpose: Sets the text of a control by name
  477. //-----------------------------------------------------------------------------
  478. void CSpectatorGUI::MoveLabelToFront(const char *textEntryName)
  479. {
  480. Label *entry = dynamic_cast<Label *>(FindChildByName(textEntryName));
  481. if (entry)
  482. {
  483. entry->MoveToFront();
  484. }
  485. }
  486. //-----------------------------------------------------------------------------
  487. // Purpose: shows/hides the buy menu
  488. //-----------------------------------------------------------------------------
  489. void CSpectatorGUI::ShowPanel(bool bShow)
  490. {
  491. if ( bShow && !IsVisible() )
  492. {
  493. InvalidateLayout( true, true );
  494. m_bSpecScoreboard = false;
  495. }
  496. SetVisible( bShow );
  497. if ( !bShow && m_bSpecScoreboard )
  498. {
  499. gViewPortInterface->ShowPanel( PANEL_SCOREBOARD, false );
  500. }
  501. }
  502. bool CSpectatorGUI::ShouldShowPlayerLabel( int specmode )
  503. {
  504. return ( (specmode == OBS_MODE_IN_EYE) || (specmode == OBS_MODE_CHASE) );
  505. }
  506. //-----------------------------------------------------------------------------
  507. // Purpose: Updates the gui, rearranges elements
  508. //-----------------------------------------------------------------------------
  509. void CSpectatorGUI::Update()
  510. {
  511. int wide, tall;
  512. int bx, by, bwide, btall;
  513. GetHudSize(wide, tall);
  514. m_pTopBar->GetBounds( bx, by, bwide, btall );
  515. IGameResources *gr = GameResources();
  516. int specmode = GetSpectatorMode();
  517. int playernum = GetSpectatorTarget();
  518. IViewPortPanel *overview = gViewPortInterface->FindPanelByName( PANEL_OVERVIEW );
  519. if ( overview && overview->IsVisible() )
  520. {
  521. int mx, my, mwide, mtall;
  522. VPANEL p = overview->GetVPanel();
  523. vgui::ipanel()->GetPos( p, mx, my );
  524. vgui::ipanel()->GetSize( p, mwide, mtall );
  525. if ( my < btall )
  526. {
  527. // reduce to bar
  528. m_pTopBar->SetSize( wide - (mx + mwide), btall );
  529. m_pTopBar->SetPos( (mx + mwide), 0 );
  530. }
  531. else
  532. {
  533. // full top bar
  534. m_pTopBar->SetSize( wide , btall );
  535. m_pTopBar->SetPos( 0, 0 );
  536. }
  537. }
  538. else
  539. {
  540. // full top bar
  541. m_pTopBar->SetSize( wide , btall ); // change width, keep height
  542. m_pTopBar->SetPos( 0, 0 );
  543. }
  544. m_pPlayerLabel->SetVisible( ShouldShowPlayerLabel(specmode) );
  545. // update player name filed, text & color
  546. if ( playernum > 0 && playernum <= gpGlobals->maxClients && gr )
  547. {
  548. Color c = gr->GetTeamColor( gr->GetTeam(playernum) ); // Player's team color
  549. m_pPlayerLabel->SetFgColor( c );
  550. wchar_t playerText[ 80 ], playerName[ 64 ], health[ 10 ];
  551. V_wcsncpy( playerText, L"Unable to find #Spec_PlayerItem*", sizeof( playerText ) );
  552. memset( playerName, 0x0, sizeof( playerName ) );
  553. g_pVGuiLocalize->ConvertANSIToUnicode( UTIL_SafeName(gr->GetPlayerName( playernum )), playerName, sizeof( playerName ) );
  554. int iHealth = gr->GetHealth( playernum );
  555. if ( iHealth > 0 && gr->IsAlive(playernum) )
  556. {
  557. _snwprintf( health, ARRAYSIZE( health ), L"%i", iHealth );
  558. g_pVGuiLocalize->ConstructString_safe( playerText, g_pVGuiLocalize->Find( "#Spec_PlayerItem_Team" ), 2, playerName, health );
  559. }
  560. else
  561. {
  562. g_pVGuiLocalize->ConstructString_safe( playerText, g_pVGuiLocalize->Find( "#Spec_PlayerItem" ), 1, playerName );
  563. }
  564. m_pPlayerLabel->SetText( playerText );
  565. }
  566. else
  567. {
  568. m_pPlayerLabel->SetText( L"" );
  569. }
  570. // update extra info field
  571. wchar_t szEtxraInfo[1024];
  572. wchar_t szTitleLabel[1024];
  573. char tempstr[128];
  574. if ( engine->IsHLTV() )
  575. {
  576. // set spectator number and HLTV title
  577. Q_snprintf(tempstr,sizeof(tempstr),"Spectators : %d", HLTVCamera()->GetNumSpectators() );
  578. g_pVGuiLocalize->ConvertANSIToUnicode(tempstr,szEtxraInfo,sizeof(szEtxraInfo));
  579. Q_strncpy( tempstr, HLTVCamera()->GetTitleText(), sizeof(tempstr) );
  580. g_pVGuiLocalize->ConvertANSIToUnicode(tempstr,szTitleLabel,sizeof(szTitleLabel));
  581. }
  582. else
  583. {
  584. // otherwise show map name
  585. Q_FileBase( engine->GetLevelName(), tempstr, sizeof(tempstr) );
  586. wchar_t wMapName[64];
  587. g_pVGuiLocalize->ConvertANSIToUnicode(tempstr,wMapName,sizeof(wMapName));
  588. g_pVGuiLocalize->ConstructString_safe( szEtxraInfo, g_pVGuiLocalize->Find("#Spec_Map" ),1, wMapName );
  589. g_pVGuiLocalize->ConvertANSIToUnicode( "" ,szTitleLabel,sizeof(szTitleLabel));
  590. }
  591. SetLabelText("extrainfo", szEtxraInfo );
  592. SetLabelText("titlelabel", szTitleLabel );
  593. }
  594. //-----------------------------------------------------------------------------
  595. // Purpose: Gets the res file we should use (depends on if we're in Steam Controller mode)
  596. //-----------------------------------------------------------------------------
  597. const char * CSpectatorGUI::GetResFile( void )
  598. {
  599. if ( ::input->IsSteamControllerActive() )
  600. {
  601. return "Resource/UI/Spectator_SC.res";
  602. }
  603. else
  604. {
  605. return "Resource/UI/Spectator.res";
  606. }
  607. }
  608. //-----------------------------------------------------------------------------
  609. // Purpose: Updates the timer label if one exists
  610. //-----------------------------------------------------------------------------
  611. void CSpectatorGUI::UpdateTimer()
  612. {
  613. wchar_t szText[ 63 ];
  614. int timer = 0;
  615. V_swprintf_safe ( szText, L"%d:%02d\n", (timer / 60), (timer % 60) );
  616. SetLabelText("timerlabel", szText );
  617. }
  618. static void ForwardSpecCmdToServer( const CCommand &args )
  619. {
  620. if ( engine->IsPlayingDemo() )
  621. return;
  622. if ( args.ArgC() == 1 )
  623. {
  624. // just forward the command without parameters
  625. engine->ServerCmd( args[ 0 ] );
  626. }
  627. else if ( args.ArgC() == 2 )
  628. {
  629. // forward the command with parameter
  630. // XXX(JohnS): Whyyyyy
  631. char command[128];
  632. Q_snprintf( command, sizeof(command), "%s \"%s\"", args[ 0 ], args[ 1 ] );
  633. engine->ServerCmd( command );
  634. }
  635. }
  636. CON_COMMAND_F( spec_next, "Spectate next player", FCVAR_CLIENTCMD_CAN_EXECUTE )
  637. {
  638. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  639. if ( !pPlayer || !pPlayer->IsObserver() )
  640. return;
  641. if ( engine->IsHLTV() )
  642. {
  643. // handle the command clientside
  644. if ( !HLTVCamera()->IsPVSLocked() )
  645. {
  646. HLTVCamera()->SpecNextPlayer( false );
  647. }
  648. }
  649. else
  650. {
  651. ForwardSpecCmdToServer( args );
  652. }
  653. }
  654. CON_COMMAND_F( spec_prev, "Spectate previous player", FCVAR_CLIENTCMD_CAN_EXECUTE )
  655. {
  656. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  657. if ( !pPlayer || !pPlayer->IsObserver() )
  658. return;
  659. if ( engine->IsHLTV() )
  660. {
  661. // handle the command clientside
  662. if ( !HLTVCamera()->IsPVSLocked() )
  663. {
  664. HLTVCamera()->SpecNextPlayer( true );
  665. }
  666. }
  667. else
  668. {
  669. ForwardSpecCmdToServer( args );
  670. }
  671. }
  672. CON_COMMAND_F( spec_mode, "Set spectator mode", FCVAR_CLIENTCMD_CAN_EXECUTE )
  673. {
  674. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  675. if ( !pPlayer || !pPlayer->IsObserver() )
  676. return;
  677. if ( engine->IsHLTV() )
  678. {
  679. if ( HLTVCamera()->IsPVSLocked() )
  680. {
  681. // in locked mode we can only switch between first and 3rd person
  682. HLTVCamera()->ToggleChaseAsFirstPerson();
  683. }
  684. else
  685. {
  686. // we can choose any mode, not loked to PVS
  687. int mode;
  688. if ( args.ArgC() == 2 )
  689. {
  690. // set specifc mode
  691. mode = Q_atoi( args[1] );
  692. }
  693. else
  694. {
  695. // set next mode
  696. mode = HLTVCamera()->GetMode()+1;
  697. if ( mode > LAST_PLAYER_OBSERVERMODE )
  698. mode = OBS_MODE_IN_EYE;
  699. else if ( mode == OBS_MODE_POI ) // PASSTIME skip POI mode since hltv doesn't have the entity data required to make it work
  700. mode = OBS_MODE_ROAMING;
  701. }
  702. // handle the command clientside
  703. HLTVCamera()->SetMode( mode );
  704. }
  705. // turn off auto director once user tried to change view settings
  706. HLTVCamera()->SetAutoDirector( false );
  707. }
  708. else
  709. {
  710. // we spectate on a game server, forward command
  711. ForwardSpecCmdToServer( args );
  712. }
  713. }
  714. CON_COMMAND_F( spec_player, "Spectate player by partial name, steamid, or userid", FCVAR_CLIENTCMD_CAN_EXECUTE )
  715. {
  716. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  717. if ( !pPlayer || !pPlayer->IsObserver() )
  718. return;
  719. if ( args.ArgC() != 2 )
  720. {
  721. ConMsg( "Usage: spec_player { steamid | #userid | partial name match }\n" );
  722. return;
  723. }
  724. if ( engine->IsHLTV() )
  725. {
  726. // we can only switch primary spectator targets is PVS isnt locked by auto-director
  727. if ( !HLTVCamera()->IsPVSLocked() )
  728. {
  729. HLTVCamera()->SpecPlayerByPredicate( args[1] );
  730. }
  731. }
  732. else
  733. {
  734. ForwardSpecCmdToServer( args );
  735. }
  736. }