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.

622 lines
15 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include <vgui_controls/Label.h>
  9. #include <vgui_controls/Button.h>
  10. #include <vgui_controls/ImagePanel.h>
  11. #include <vgui_controls/RichText.h>
  12. #include <vgui_controls/Frame.h>
  13. #include <game/client/iviewport.h>
  14. #include <KeyValues.h>
  15. #include <filesystem.h>
  16. #include "materialsystem/imaterialvar.h"
  17. #include "IGameUIFuncs.h" // for key bindings
  18. #include "tf_controls.h"
  19. #include "tf_imagepanel.h"
  20. #include "c_team_objectiveresource.h"
  21. #include "c_tf_objective_resource.h"
  22. #include "c_tf_player.h"
  23. #include "tf_shareddefs.h"
  24. #include "tf_roundinfo.h"
  25. #include "vgui/ISurface.h"
  26. #include <vgui/ILocalize.h>
  27. #include <vgui/IVGui.h>
  28. #include "engine/IEngineSound.h"
  29. using namespace vgui;
  30. const char *GetMapDisplayName( const char *mapName );
  31. class RoundInfoOverlay : public vgui::EditablePanel
  32. {
  33. public:
  34. DECLARE_CLASS_SIMPLE( RoundInfoOverlay, vgui::EditablePanel );
  35. RoundInfoOverlay( Panel *parent, const char *panelName ) : EditablePanel( parent, panelName )
  36. {
  37. m_iMode = 0;
  38. m_flModeChangeTime = -1;
  39. vgui::ivgui()->AddTickSignal( GetVPanel(), 100 );
  40. m_iBlueTeamTexture = vgui::surface()->CreateNewTextureID();
  41. vgui::surface()->DrawSetTextureFile(m_iBlueTeamTexture, "overviews/blueteam", true, false);
  42. m_iRedTeamTexture = vgui::surface()->CreateNewTextureID();
  43. vgui::surface()->DrawSetTextureFile(m_iRedTeamTexture, "overviews/redteam", true, false);
  44. m_iCapArrowTexture = vgui::surface()->CreateNewTextureID();
  45. vgui::surface()->DrawSetTextureFile(m_iCapArrowTexture, "overviews/caparrows", true, false);
  46. m_iFoundPoints = 0;
  47. m_iNextRoundPoints[0] = -1;
  48. m_iNextRoundPoints[1] = -1;
  49. m_iLastCappedPoint = -1;
  50. }
  51. virtual ~RoundInfoOverlay( void )
  52. {
  53. if ( vgui::surface() )
  54. {
  55. if ( m_iBlueTeamTexture != -1 )
  56. {
  57. vgui::surface()->DestroyTextureID( m_iBlueTeamTexture );
  58. m_iBlueTeamTexture = -1;
  59. }
  60. if ( m_iRedTeamTexture != -1 )
  61. {
  62. vgui::surface()->DestroyTextureID( m_iRedTeamTexture );
  63. m_iRedTeamTexture = -1;
  64. }
  65. if ( m_iCapArrowTexture != -1 )
  66. {
  67. vgui::surface()->DestroyTextureID( m_iCapArrowTexture );
  68. m_iCapArrowTexture = -1;
  69. }
  70. }
  71. }
  72. void Update( const char *szMapName );
  73. virtual void Paint();
  74. void SetState( int iPrevState, int iCurrentState, int iNextBattles );
  75. virtual void OnTick( void );
  76. void DrawTeamIcon( int x, int y, bool bBlueTeam, float flBloat = 1.0f );
  77. void DrawCapArrows( int x0, int y0, int x1, int y1 );
  78. private:
  79. // structure to hold a single control point
  80. typedef struct
  81. {
  82. char m_szName[64];
  83. int m_iXPos;
  84. int m_iYPos;
  85. bool m_bHideIcon;
  86. } roundinfo_control_point_t;
  87. CUtlVector < roundinfo_control_point_t > m_ControlPoints;
  88. int m_iPrevState;
  89. int m_iCurrentState;
  90. int m_iMiniRoundMask;
  91. // Time when we should change the text to the attack directive
  92. int m_iMode; // 0 - start, 1 - previous round victory anim, 2 - attack directive, new round
  93. float m_flModeChangeTime;
  94. int m_iBlueTeamTexture;
  95. int m_iRedTeamTexture;
  96. int m_iCapArrowTexture;
  97. int m_iLastCappedPoint;
  98. int m_iFoundPoints;
  99. int m_iNextRoundPoints[2];
  100. };
  101. DECLARE_BUILD_FACTORY( RoundInfoOverlay );
  102. void RoundInfoOverlay::Paint( void )
  103. {
  104. BaseClass::Paint();
  105. if ( m_ControlPoints.Count() <= 0 )
  106. {
  107. return;
  108. }
  109. // Draw the Cap Icons
  110. for ( int i=0; i<m_ControlPoints.Count(); i++ )
  111. {
  112. if ( m_ControlPoints[i].m_bHideIcon )
  113. continue;
  114. int x = m_ControlPoints[i].m_iXPos;
  115. int y = m_ControlPoints[i].m_iYPos;
  116. switch( m_iMode )
  117. {
  118. case 0: // Show previous state
  119. {
  120. if ( i != m_iLastCappedPoint )
  121. {
  122. bool bBlueTeam = ( m_iPrevState & (1<<i) );
  123. DrawTeamIcon( x, y, bBlueTeam );
  124. }
  125. }
  126. break;
  127. case 1: // Animate the point being capped
  128. {
  129. bool bWasBlueTeam = ( m_iPrevState & (1<<i) );
  130. if ( i == m_iLastCappedPoint )
  131. {
  132. float flTimeUntilChange = m_flModeChangeTime - gpGlobals->curtime;
  133. if ( flTimeUntilChange < 0.4f )
  134. {
  135. float flBloat = RemapVal( flTimeUntilChange, 0.0f, 0.4f, 1.0f, 2.5f );
  136. DrawTeamIcon( x, y, !bWasBlueTeam, flBloat );
  137. }
  138. }
  139. else
  140. {
  141. DrawTeamIcon( x, y, bWasBlueTeam );
  142. }
  143. }
  144. break;
  145. case 2: // Draw the current state and the next battle arrows
  146. {
  147. bool bPointInContention = (m_iNextRoundPoints[0] == i || m_iNextRoundPoints[1] == i );
  148. bool bBlueTeam = ( m_iCurrentState & (1<<i) );
  149. DrawTeamIcon( x, y, bBlueTeam, bPointInContention ? 1.4 : 1.0 ); // rescale? pop looks weird
  150. }
  151. break;
  152. }
  153. }
  154. if ( m_iMode == 2 )
  155. {
  156. if ( m_iFoundPoints == 2 )
  157. {
  158. if ( ( m_flModeChangeTime - gpGlobals->curtime ) < 3.5f )
  159. {
  160. DrawCapArrows( m_ControlPoints[m_iNextRoundPoints[0]].m_iXPos,
  161. m_ControlPoints[m_iNextRoundPoints[0]].m_iYPos,
  162. m_ControlPoints[m_iNextRoundPoints[1]].m_iXPos,
  163. m_ControlPoints[m_iNextRoundPoints[1]].m_iYPos );
  164. }
  165. }
  166. }
  167. }
  168. void RoundInfoOverlay::DrawCapArrows( int x0, int y0, int x1, int y1 )
  169. {
  170. vgui::surface()->DrawSetColor( Color(255,255,255,255) );
  171. vgui::surface()->DrawSetTexture( m_iCapArrowTexture );
  172. Vector2D a( x0, y0 );
  173. Vector2D b( x1, y1 );
  174. Vector2D dir = b - a;
  175. Vector2D perp( -dir.y, dir.x );
  176. perp.NormalizeInPlace();
  177. perp *= YRES(50);
  178. float bloat = sin(4*gpGlobals->curtime) * 0.1f;
  179. Vector2D edgepoint = a + dir * 0.25f;
  180. Vector2D edgepoint2 = b - dir * 0.25f;
  181. edgepoint -= 0.25f * dir * bloat;
  182. edgepoint2 += 0.25f * dir * bloat;
  183. float uv1 = 0.0f, uv2 = 1.0f;
  184. Vector2D uv12( uv1, uv2 );
  185. Vector2D uv11( uv1, uv1 );
  186. Vector2D uv21( uv2, uv1 );
  187. Vector2D uv22( uv2, uv2 );
  188. vgui::Vertex_t verts[4];
  189. verts[0].Init( edgepoint - perp * 0.5f, uv12 );
  190. verts[1].Init( edgepoint2 - perp * 0.5f, uv11 );
  191. verts[2].Init( edgepoint2 + perp * 0.5f, uv21 );
  192. verts[3].Init( edgepoint + perp * 0.5f, uv22 );
  193. vgui::surface()->DrawTexturedPolygon( 4, verts );
  194. }
  195. void RoundInfoOverlay::DrawTeamIcon( int x, int y, bool bBlueTeam, float flBloat /* = 1.0f */ )
  196. {
  197. float flWide = YRES(45) * flBloat;
  198. int xpos = x - flWide * 0.5f;
  199. int ypos = y - flWide * 0.5f;
  200. vgui::surface()->DrawSetColor( Color(255,255,255,255) );
  201. vgui::surface()->DrawSetTexture( bBlueTeam ? m_iBlueTeamTexture : m_iRedTeamTexture );
  202. vgui::surface()->DrawTexturedRect( xpos, ypos, xpos + flWide, ypos + flWide );
  203. }
  204. void RoundInfoOverlay::Update( const char *szMapName )
  205. {
  206. KeyValues *kvCapPoints = NULL;
  207. char strFullpath[MAX_PATH];
  208. Q_strncpy( strFullpath, "resource/roundinfo/", MAX_PATH ); // Assume we must play out of the media directory
  209. Q_strncat( strFullpath, szMapName, MAX_PATH );
  210. #ifdef _X360
  211. char *pExt = Q_stristr( strFullpath, ".360" );
  212. if ( pExt )
  213. {
  214. *pExt = '\0';
  215. }
  216. #endif
  217. Q_strncat( strFullpath, ".res", MAX_PATH ); // Assume we're a .res extension type
  218. if ( g_pFullFileSystem->FileExists( strFullpath ), "MOD" )
  219. {
  220. kvCapPoints = new KeyValues( strFullpath );
  221. if ( kvCapPoints )
  222. {
  223. if ( kvCapPoints->LoadFromFile( g_pFullFileSystem, strFullpath ) )
  224. {
  225. m_ControlPoints.RemoveAll();
  226. for ( KeyValues *pData = kvCapPoints->GetFirstSubKey(); pData != NULL; pData = pData->GetNextKey() )
  227. {
  228. roundinfo_control_point_t point;
  229. Q_snprintf( point.m_szName, sizeof(point.m_szName), "%s", pData->GetName() );
  230. // These x,y coords are relative to a 640x480 parent panel.
  231. int wide, tall;
  232. GetSize( wide, tall );
  233. // can't use XRES, YRES because of widescreen
  234. point.m_iXPos = (int)( (float)pData->GetInt( "x", 0 ) * ( ( float )wide / 560.0f ) );
  235. point.m_iYPos = (int)( (float)pData->GetInt( "y", 0 ) * ( ( float )tall / 280.0f ) );
  236. point.m_bHideIcon = ( pData->GetInt( "hideicon", 0 ) > 0 );
  237. m_ControlPoints.AddToTail( point );
  238. }
  239. }
  240. kvCapPoints->deleteThis();
  241. }
  242. }
  243. }
  244. void RoundInfoOverlay::SetState( int iPrevState, int iCurrentState, int iNextBattles )
  245. {
  246. m_iPrevState = iPrevState;
  247. m_iCurrentState = iCurrentState;
  248. m_iMiniRoundMask = iNextBattles;
  249. m_iMode = 0;
  250. m_flModeChangeTime = gpGlobals->curtime + 0.5f;
  251. // Find the two points that are being fought over
  252. m_iFoundPoints = 0;
  253. for ( int i=0;i<8 && m_iFoundPoints<2;i++ )
  254. {
  255. if ( m_iMiniRoundMask & (1<<i) )
  256. {
  257. m_iNextRoundPoints[m_iFoundPoints] = i;
  258. m_iFoundPoints++;
  259. }
  260. }
  261. // Make sure the blue point is in m_iNextRoundPoints[0]
  262. if ( m_iFoundPoints >= 2 )
  263. {
  264. if ( !( m_iCurrentState & (1<<m_iNextRoundPoints[0]) ) )
  265. {
  266. // The first point is red! swap them
  267. int temp = m_iNextRoundPoints[0];
  268. m_iNextRoundPoints[0] = m_iNextRoundPoints[1];
  269. m_iNextRoundPoints[1] = temp;
  270. }
  271. }
  272. m_iLastCappedPoint = -1;
  273. // Find the index of the point that was just capped
  274. int iMaskedCappedPoint = m_iCurrentState ^ m_iPrevState;
  275. if ( iMaskedCappedPoint != 0 )
  276. {
  277. int iIndex = 0;
  278. // Find the index of the point that changed
  279. while ( !( iMaskedCappedPoint & 0x1 ) )
  280. {
  281. iMaskedCappedPoint = iMaskedCappedPoint>>1;
  282. iIndex++;
  283. }
  284. m_iLastCappedPoint = iIndex;
  285. }
  286. }
  287. ConVar tf_roundinfo_pause( "tf_roundinfo_pause", "0", FCVAR_DEVELOPMENTONLY );
  288. void RoundInfoOverlay::OnTick( void )
  289. {
  290. // Stop ticking when our parent is invisible
  291. Panel *parent = GetParent();
  292. if ( m_iMode >= 0 && ( !parent || !parent->IsVisible() ) )
  293. {
  294. m_iMode = -1;
  295. return;
  296. }
  297. BaseClass::OnTick();
  298. if ( tf_roundinfo_pause.GetBool() == false && m_flModeChangeTime <= gpGlobals->curtime )
  299. {
  300. switch( m_iMode )
  301. {
  302. case 0:
  303. {
  304. // start showing previous round anim
  305. if ( m_iCurrentState != m_iPrevState )
  306. {
  307. m_iMode = 1;
  308. m_flModeChangeTime = gpGlobals->curtime + 1.5f;
  309. }
  310. else
  311. {
  312. m_iMode = 2;
  313. m_flModeChangeTime = gpGlobals->curtime + 4.0f;
  314. }
  315. }
  316. break;
  317. case 1:
  318. {
  319. // start showing next round plan
  320. m_iMode = 2;
  321. m_flModeChangeTime = gpGlobals->curtime + 4.0f;
  322. CLocalPlayerFilter filter;
  323. C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Hud.EndRoundScored" );
  324. }
  325. break;
  326. case 2:
  327. {
  328. // we're done, hide the panel
  329. //GetParent()->OnCommand( "continue" );
  330. //m_iMode = -1;
  331. }
  332. break;
  333. default:
  334. break;
  335. }
  336. }
  337. }
  338. //-----------------------------------------------------------------------------
  339. // Purpose: Constructor
  340. //-----------------------------------------------------------------------------
  341. CTFRoundInfo::CTFRoundInfo( IViewPort *pViewPort ) : Frame( NULL, PANEL_ROUNDINFO )
  342. {
  343. m_pViewPort = pViewPort;
  344. // load the new scheme early!!
  345. SetScheme( "ClientScheme" );
  346. SetTitleBarVisible( false );
  347. SetMinimizeButtonVisible( false );
  348. SetMaximizeButtonVisible( false );
  349. SetCloseButtonVisible( false );
  350. SetSizeable( false );
  351. SetMoveable( false );
  352. SetProportional( true );
  353. SetVisible( false );
  354. SetKeyBoardInputEnabled( true );
  355. m_pTitle = new CExLabel( this, "RoundTitle", " " );
  356. m_pMapImage = new ImagePanel( this, "MapImage" );
  357. #ifdef _X360
  358. m_pFooter = new CTFFooter( this, "Footer" );
  359. #else
  360. m_pContinue = new CExButton( this, "RoundContinue", "#TF_Continue" );
  361. #endif
  362. m_pOverlay = new RoundInfoOverlay( this, "Overlay" );
  363. ListenForGameEvent( "game_newmap" );
  364. }
  365. //-----------------------------------------------------------------------------
  366. // Purpose:
  367. //-----------------------------------------------------------------------------
  368. void CTFRoundInfo::PerformLayout()
  369. {
  370. BaseClass::PerformLayout();
  371. }
  372. //-----------------------------------------------------------------------------
  373. // Purpose:
  374. //-----------------------------------------------------------------------------
  375. void CTFRoundInfo::ApplySchemeSettings( vgui::IScheme *pScheme )
  376. {
  377. LoadControlSettings( "Resource/UI/RoundInfo.res" );
  378. BaseClass::ApplySchemeSettings( pScheme );
  379. Update();
  380. }
  381. //-----------------------------------------------------------------------------
  382. // Purpose:
  383. //-----------------------------------------------------------------------------
  384. void CTFRoundInfo::ShowPanel( bool bShow )
  385. {
  386. if ( IsVisible() == bShow )
  387. return;
  388. if ( bShow )
  389. {
  390. // look for the textures we want to use and don't show the roundinfo panel if any are missing
  391. char temp[255];
  392. Q_snprintf( temp, sizeof( temp ), "VGUI/%s", m_szMapImage );
  393. IMaterial *pMapMaterial = materials->FindMaterial( temp, TEXTURE_GROUP_VGUI, false );
  394. // are we missing any of the images we want to show?
  395. if ( pMapMaterial && !IsErrorMaterial( pMapMaterial ) )
  396. {
  397. Activate();
  398. }
  399. else
  400. {
  401. SetVisible( false );
  402. }
  403. }
  404. else
  405. {
  406. SetVisible( false );
  407. }
  408. }
  409. //-----------------------------------------------------------------------------
  410. // Purpose:
  411. //-----------------------------------------------------------------------------
  412. void CTFRoundInfo::OnCommand( const char *command )
  413. {
  414. if ( !Q_strcmp( command, "continue" ) )
  415. {
  416. m_pViewPort->ShowPanel( this, false );
  417. }
  418. else
  419. {
  420. BaseClass::OnCommand( command );
  421. }
  422. }
  423. //-----------------------------------------------------------------------------
  424. // Purpose:
  425. //-----------------------------------------------------------------------------
  426. void CTFRoundInfo::UpdateImage( ImagePanel *pImagePanel, const char *pszImageName )
  427. {
  428. if ( pImagePanel && ( Q_strlen( pszImageName ) > 0 ) )
  429. {
  430. char szTemp[255];
  431. Q_snprintf( szTemp, sizeof( szTemp ), "VGUI/%s", pszImageName );
  432. IMaterial *pTemp = materials->FindMaterial( szTemp, TEXTURE_GROUP_VGUI, false );
  433. if ( pTemp && !IsErrorMaterial( pTemp ) )
  434. {
  435. pImagePanel->SetImage( pszImageName );
  436. }
  437. }
  438. }
  439. //-----------------------------------------------------------------------------
  440. // Purpose:
  441. //-----------------------------------------------------------------------------
  442. void CTFRoundInfo::Update()
  443. {
  444. char szMapName[MAX_MAP_NAME];
  445. Q_FileBase( engine->GetLevelName(), szMapName, sizeof(szMapName) );
  446. Q_strlower( szMapName );
  447. SetDialogVariable( "mapname", GetMapDisplayName( szMapName ) );
  448. if ( m_pMapImage )
  449. {
  450. char temp[255];
  451. Q_snprintf( temp, sizeof(temp), "../overviews/%s", szMapName );
  452. Q_strncpy( m_szMapImage, temp, sizeof( m_szMapImage ) );
  453. UpdateImage( m_pMapImage, m_szMapImage );
  454. }
  455. if ( m_pOverlay )
  456. {
  457. m_pOverlay->Update( szMapName );
  458. }
  459. #ifndef _X360
  460. if ( m_pContinue )
  461. {
  462. m_pContinue->RequestFocus();
  463. }
  464. #endif
  465. }
  466. //-----------------------------------------------------------------------------
  467. // Purpose:
  468. //-----------------------------------------------------------------------------
  469. void CTFRoundInfo::OnKeyCodePressed( KeyCode code )
  470. {
  471. if( code == KEY_SPACE ||
  472. code == KEY_ENTER ||
  473. code == KEY_XBUTTON_A ||
  474. code == KEY_XBUTTON_B ||
  475. code == STEAMCONTROLLER_A ||
  476. code == STEAMCONTROLLER_B )
  477. {
  478. OnCommand( "continue" );
  479. }
  480. else
  481. {
  482. BaseClass::OnKeyCodePressed( code );
  483. }
  484. }
  485. //-----------------------------------------------------------------------------
  486. // Purpose:
  487. //-----------------------------------------------------------------------------
  488. void CTFRoundInfo::SetData( KeyValues *data )
  489. {
  490. if ( m_pOverlay )
  491. {
  492. m_pOverlay->SetState( data->GetInt( "prev" ), data->GetInt( "cur" ), data->GetInt( "round" ) );
  493. }
  494. }
  495. //-----------------------------------------------------------------------------
  496. // Purpose:
  497. //-----------------------------------------------------------------------------
  498. void CTFRoundInfo::FireGameEvent( IGameEvent *event )
  499. {
  500. if ( Q_strcmp( event->GetName(), "game_newmap" ) == 0 )
  501. {
  502. Update();
  503. }
  504. }