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.

447 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include <cdll_client_int.h>
  9. #include "teammenu.h"
  10. #include <vgui/IScheme.h>
  11. #include <vgui/ILocalize.h>
  12. #include <vgui/ISurface.h>
  13. #include <KeyValues.h>
  14. #include <vgui_controls/ImageList.h>
  15. #include <filesystem.h>
  16. #include <vgui_controls/RichText.h>
  17. #include <vgui_controls/Label.h>
  18. #include <vgui_controls/Button.h>
  19. #include <vgui_controls/HTML.h>
  20. #include "IGameUIFuncs.h" // for key bindings
  21. #include <igameresources.h>
  22. #include <game/client/iviewport.h>
  23. #include <stdlib.h> // MAX_PATH define
  24. #include <stdio.h>
  25. #include "byteswap.h"
  26. // memdbgon must be the last include file in a .cpp file!!!
  27. #include "tier0/memdbgon.h"
  28. extern IGameUIFuncs *gameuifuncs; // for key binding details
  29. using namespace vgui;
  30. void UpdateCursorState();
  31. // void DuckMessage(const char *str);
  32. // helper function
  33. const char *GetStringTeamColor( int i )
  34. {
  35. switch( i )
  36. {
  37. case 0:
  38. return "team0";
  39. case 1:
  40. return "team1";
  41. case 2:
  42. return "team2";
  43. case 3:
  44. return "team3";
  45. case 4:
  46. default:
  47. return "team4";
  48. }
  49. }
  50. //-----------------------------------------------------------------------------
  51. // Purpose: Constructor
  52. //-----------------------------------------------------------------------------
  53. CTeamMenu::CTeamMenu(IViewPort *pViewPort) : Frame(NULL, PANEL_TEAM )
  54. {
  55. m_pViewPort = pViewPort;
  56. m_iJumpKey = BUTTON_CODE_INVALID; // this is looked up in Activate()
  57. m_iScoreBoardKey = BUTTON_CODE_INVALID; // this is looked up in Activate()
  58. // initialize dialog
  59. SetTitle("", true);
  60. // load the new scheme early!!
  61. SetScheme("ClientScheme");
  62. SetMoveable(false);
  63. SetSizeable(false);
  64. // hide the system buttons
  65. SetTitleBarVisible( false );
  66. SetProportional(true);
  67. // info window about this map
  68. m_pMapInfo = new RichText( this, "MapInfo" );
  69. #if defined( ENABLE_HTML_WINDOW )
  70. m_pMapInfoHTML = new HTML( this, "MapInfoHTML");
  71. #endif
  72. LoadControlSettings("Resource/UI/TeamMenu.res");
  73. InvalidateLayout();
  74. m_szMapName[0] = 0;
  75. }
  76. //-----------------------------------------------------------------------------
  77. // Purpose: Destructor
  78. //-----------------------------------------------------------------------------
  79. CTeamMenu::~CTeamMenu()
  80. {
  81. }
  82. //-----------------------------------------------------------------------------
  83. // Purpose: sets the text color of the map description field
  84. //-----------------------------------------------------------------------------
  85. void CTeamMenu::ApplySchemeSettings(IScheme *pScheme)
  86. {
  87. BaseClass::ApplySchemeSettings(pScheme);
  88. m_pMapInfo->SetFgColor( pScheme->GetColor("MapDescriptionText", Color(255, 255, 255, 0)) );
  89. if ( *m_szMapName )
  90. {
  91. LoadMapPage( m_szMapName ); // reload the map description to pick up the color
  92. }
  93. }
  94. //-----------------------------------------------------------------------------
  95. // Purpose: makes the user choose the auto assign option
  96. //-----------------------------------------------------------------------------
  97. void CTeamMenu::AutoAssign()
  98. {
  99. engine->ClientCmd("jointeam 0");
  100. OnClose();
  101. }
  102. //-----------------------------------------------------------------------------
  103. // Purpose: shows the team menu
  104. //-----------------------------------------------------------------------------
  105. void CTeamMenu::ShowPanel(bool bShow)
  106. {
  107. if ( BaseClass::IsVisible() == bShow )
  108. return;
  109. if ( bShow )
  110. {
  111. Activate();
  112. SetMouseInputEnabled( true );
  113. // get key bindings if shown
  114. if( m_iJumpKey == BUTTON_CODE_INVALID ) // you need to lookup the jump key AFTER the engine has loaded
  115. {
  116. m_iJumpKey = gameuifuncs->GetButtonCodeForBind( "jump" );
  117. }
  118. if ( m_iScoreBoardKey == BUTTON_CODE_INVALID )
  119. {
  120. m_iScoreBoardKey = gameuifuncs->GetButtonCodeForBind( "showscores" );
  121. }
  122. }
  123. else
  124. {
  125. SetVisible( false );
  126. SetMouseInputEnabled( false );
  127. }
  128. m_pViewPort->ShowBackGround( bShow );
  129. }
  130. //-----------------------------------------------------------------------------
  131. // Purpose: updates the UI with a new map name and map html page, and sets up the team buttons
  132. //-----------------------------------------------------------------------------
  133. void CTeamMenu::Update()
  134. {
  135. char mapname[MAX_MAP_NAME];
  136. Q_FileBase( engine->GetLevelName(), mapname, sizeof(mapname) );
  137. SetLabelText( "mapname", mapname );
  138. LoadMapPage( mapname );
  139. }
  140. //-----------------------------------------------------------------------------
  141. // Purpose: chooses and loads the text page to display that describes mapName map
  142. //-----------------------------------------------------------------------------
  143. void CTeamMenu::LoadMapPage( const char *mapName )
  144. {
  145. // Save off the map name so we can re-load the page in ApplySchemeSettings().
  146. Q_strncpy( m_szMapName, mapName, strlen( mapName ) + 1 );
  147. char mapRES[ MAX_PATH ];
  148. char uilanguage[ 64 ];
  149. uilanguage[0] = 0;
  150. engine->GetUILanguage( uilanguage, sizeof( uilanguage ) );
  151. Q_snprintf( mapRES, sizeof( mapRES ), "resource/maphtml/%s_%s.html", mapName, uilanguage );
  152. bool bFoundHTML = false;
  153. if ( !g_pFullFileSystem->FileExists( mapRES ) )
  154. {
  155. // try english
  156. Q_snprintf( mapRES, sizeof( mapRES ), "resource/maphtml/%s_english.html", mapName );
  157. }
  158. else
  159. {
  160. bFoundHTML = true;
  161. }
  162. if( bFoundHTML || g_pFullFileSystem->FileExists( mapRES ) )
  163. {
  164. // it's a local HTML file
  165. char localURL[ _MAX_PATH + 7 ];
  166. Q_strncpy( localURL, "file://", sizeof( localURL ) );
  167. char pPathData[ _MAX_PATH ];
  168. g_pFullFileSystem->GetLocalPath( mapRES, pPathData, sizeof(pPathData) );
  169. Q_strncat( localURL, pPathData, sizeof( localURL ), COPY_ALL_CHARACTERS );
  170. // force steam to dump a local copy
  171. g_pFullFileSystem->GetLocalCopy( pPathData );
  172. m_pMapInfo->SetVisible( false );
  173. #if defined( ENABLE_HTML_WINDOW )
  174. m_pMapInfoHTML->SetVisible( true );
  175. m_pMapInfoHTML->OpenURL( localURL, NULL );
  176. #endif
  177. InvalidateLayout();
  178. Repaint();
  179. return;
  180. }
  181. else
  182. {
  183. m_pMapInfo->SetVisible( true );
  184. #if defined( ENABLE_HTML_WINDOW )
  185. m_pMapInfoHTML->SetVisible( false );
  186. #endif
  187. }
  188. Q_snprintf( mapRES, sizeof( mapRES ), "maps/%s.txt", mapName);
  189. // if no map specific description exists, load default text
  190. if( !g_pFullFileSystem->FileExists( mapRES ) )
  191. {
  192. if ( g_pFullFileSystem->FileExists( "maps/default.txt" ) )
  193. {
  194. Q_snprintf ( mapRES, sizeof( mapRES ), "maps/default.txt");
  195. }
  196. else
  197. {
  198. m_pMapInfo->SetText( "" );
  199. return;
  200. }
  201. }
  202. FileHandle_t f = g_pFullFileSystem->Open( mapRES, "r" );
  203. // read into a memory block
  204. int fileSize = g_pFullFileSystem->Size(f);
  205. int dataSize = fileSize + sizeof( wchar_t );
  206. if ( dataSize % 2 )
  207. ++dataSize;
  208. wchar_t *memBlock = (wchar_t *)malloc(dataSize);
  209. memset( memBlock, 0x0, dataSize);
  210. int bytesRead = g_pFullFileSystem->Read(memBlock, fileSize, f);
  211. if ( bytesRead < fileSize )
  212. {
  213. // NULL-terminate based on the length read in, since Read() can transform \r\n to \n and
  214. // return fewer bytes than we were expecting.
  215. char *data = reinterpret_cast<char *>( memBlock );
  216. data[ bytesRead ] = 0;
  217. data[ bytesRead+1 ] = 0;
  218. }
  219. #ifndef WIN32
  220. if ( ((ucs2 *)memBlock)[0] == 0xFEFF )
  221. {
  222. // convert the win32 ucs2 data to wchar_t
  223. dataSize*=2;// need to *2 to account for ucs2 to wchar_t (4byte) growth
  224. wchar_t *memBlockConverted = (wchar_t *)malloc(dataSize);
  225. V_UCS2ToUnicode( (ucs2 *)memBlock, memBlockConverted, dataSize );
  226. free(memBlock);
  227. memBlock = memBlockConverted;
  228. }
  229. #else
  230. // null-terminate the stream (redundant, since we memset & then trimmed the transformed buffer already)
  231. memBlock[dataSize / sizeof(wchar_t) - 1] = 0x0000;
  232. #endif
  233. // ensure little-endian unicode reads correctly on all platforms
  234. CByteswap byteSwap;
  235. byteSwap.SetTargetBigEndian( false );
  236. byteSwap.SwapBufferToTargetEndian( memBlock, memBlock, dataSize/sizeof(wchar_t) );
  237. // check the first character, make sure this a little-endian unicode file
  238. if ( memBlock[0] != 0xFEFF )
  239. {
  240. // its a ascii char file
  241. m_pMapInfo->SetText( reinterpret_cast<char *>( memBlock ) );
  242. }
  243. else
  244. {
  245. m_pMapInfo->SetText( memBlock+1 );
  246. }
  247. // go back to the top of the text buffer
  248. m_pMapInfo->GotoTextStart();
  249. g_pFullFileSystem->Close( f );
  250. free(memBlock);
  251. InvalidateLayout();
  252. Repaint();
  253. }
  254. /*
  255. //-----------------------------------------------------------------------------
  256. // Purpose: sets the text on and displays the team buttons
  257. //-----------------------------------------------------------------------------
  258. void CTeamMenu::MakeTeamButtons(void)
  259. {
  260. int i = 0;
  261. for( i = 0; i< m_pTeamButtons.Count(); i++ )
  262. {
  263. m_pTeamButtons[i]->SetVisible(false);
  264. }
  265. i = 0;
  266. while( true )
  267. {
  268. const char *teamname = GameResources()->GetTeamName( i );
  269. if ( !teamname || !teamname[0] )
  270. return; // no more teams
  271. char buttonText[32];
  272. Q_snprintf( buttonText, sizeof(buttonText), "&%i %s", i +1, teamname );
  273. m_pTeamButtons[i]->SetText( buttonText );
  274. m_pTeamButtons[i]->SetCommand( new KeyValues("TeamButton", "team", i ) );
  275. IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
  276. m_pTeamButtons[i]->SetArmedColor(pScheme->GetColor(GetStringTeamColor(i), Color(255, 255, 255, 255)) , pScheme->GetColor("SelectionBG", Color(255, 255, 255, 0)) );
  277. m_pTeamButtons[i]->SetDepressedColor( pScheme->GetColor(GetStringTeamColor(i), Color(255, 255, 255, 255)), pScheme->GetColor("ButtonArmedBgColor", Color(255, 255, 255, 0)) );
  278. m_pTeamButtons[i]->SetDefaultColor( pScheme->GetColor(GetStringTeamColor(i), Color(255, 255, 255, 255)), pScheme->GetColor("ButtonDepressedBgColor", Color(255, 255, 255, 0)) );
  279. m_pTeamButtons[i]->SetVisible(true);
  280. i++;
  281. }
  282. }
  283. //-----------------------------------------------------------------------------
  284. // Purpose: When a team button is pressed it triggers this function to cause the player to join a team
  285. //-----------------------------------------------------------------------------
  286. void CTeamMenu::OnTeamButton( int team )
  287. {
  288. char cmd[64];
  289. if( team >= m_iNumTeams ) // its a special button
  290. {
  291. if( team == m_iNumTeams ) // first extra team is auto assign
  292. {
  293. Q_snprintf( cmd, sizeof( cmd ), "jointeam 5" );
  294. }
  295. else // next is spectate
  296. {
  297. // DuckMessage( "#Spec_Duck" );
  298. gViewPortInterface->ShowBackGround( false );
  299. }
  300. }
  301. else
  302. {
  303. Q_snprintf( cmd, sizeof( cmd ), "jointeam %i", team + 1 );
  304. //g_iTeamNumber = team + 1;
  305. }
  306. engine->ClientCmd(cmd);
  307. SetVisible( false );
  308. OnClose();
  309. } */
  310. //-----------------------------------------------------------------------------
  311. // Purpose: Sets the text of a control by name
  312. //-----------------------------------------------------------------------------
  313. void CTeamMenu::SetLabelText(const char *textEntryName, const char *text)
  314. {
  315. Label *entry = dynamic_cast<Label *>(FindChildByName(textEntryName));
  316. if (entry)
  317. {
  318. entry->SetText(text);
  319. }
  320. }
  321. void CTeamMenu::OnKeyCodePressed(KeyCode code)
  322. {
  323. int nDir = 0;
  324. switch ( code )
  325. {
  326. case KEY_XBUTTON_UP:
  327. case KEY_XSTICK1_UP:
  328. case KEY_XSTICK2_UP:
  329. case KEY_UP:
  330. case KEY_XBUTTON_LEFT:
  331. case KEY_XSTICK1_LEFT:
  332. case KEY_XSTICK2_LEFT:
  333. case KEY_LEFT:
  334. case STEAMCONTROLLER_DPAD_LEFT:
  335. nDir = -1;
  336. break;
  337. case KEY_XBUTTON_DOWN:
  338. case KEY_XSTICK1_DOWN:
  339. case KEY_XSTICK2_DOWN:
  340. case KEY_DOWN:
  341. case KEY_XBUTTON_RIGHT:
  342. case KEY_XSTICK1_RIGHT:
  343. case KEY_XSTICK2_RIGHT:
  344. case KEY_RIGHT:
  345. case STEAMCONTROLLER_DPAD_RIGHT:
  346. nDir = 1;
  347. break;
  348. }
  349. if ( m_iScoreBoardKey != BUTTON_CODE_INVALID && m_iScoreBoardKey == code )
  350. {
  351. gViewPortInterface->ShowPanel( PANEL_SCOREBOARD, true );
  352. gViewPortInterface->PostMessageToPanel( PANEL_SCOREBOARD, new KeyValues( "PollHideCode", "code", code ) );
  353. }
  354. else if ( nDir != 0 )
  355. {
  356. CUtlSortVector< SortedPanel_t, CSortedPanelYLess > vecSortedButtons;
  357. VguiPanelGetSortedChildButtonList( this, (void*)&vecSortedButtons, "&", 0 );
  358. if ( VguiPanelNavigateSortedChildButtonList( (void*)&vecSortedButtons, nDir ) != -1 )
  359. {
  360. // Handled!
  361. return;
  362. }
  363. }
  364. else
  365. {
  366. BaseClass::OnKeyCodePressed( code );
  367. }
  368. }