Counter Strike : Global Offensive Source Code
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.

505 lines
14 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "hudelement.h"
  8. #include <vgui_controls/Panel.h>
  9. #include <vgui/ISurface.h>
  10. #include <vgui/ILocalize.h>
  11. #include "c_baseplayer.h"
  12. #include "voice_status.h"
  13. #include "clientmode_shared.h"
  14. #include "c_playerresource.h"
  15. #include "voice_common.h"
  16. #include "bitvec.h"
  17. #include "vgui_avatarimage.h"
  18. #include "engineinterface.h"
  19. #include "steam/steam_api.h"
  20. #include "tier0/memdbgon.h"
  21. extern ConVar sv_talk_enemy_dead;
  22. extern ConVar sv_talk_enemy_living;
  23. //=============================================================================
  24. // Icon for the local player using voice
  25. //=============================================================================
  26. class CHudVoiceSelfStatus : public CHudElement, public vgui::Panel
  27. {
  28. public:
  29. DECLARE_CLASS_SIMPLE( CHudVoiceSelfStatus, vgui::Panel );
  30. explicit CHudVoiceSelfStatus( const char *name );
  31. virtual bool ShouldDraw();
  32. virtual void Paint();
  33. virtual void VidInit();
  34. virtual void ApplySchemeSettings(vgui::IScheme *pScheme);
  35. private:
  36. CHudTexture *m_pVoiceIcon;
  37. Color m_clrIcon;
  38. };
  39. //DECLARE_HUDELEMENT( CHudVoiceSelfStatus );
  40. CHudVoiceSelfStatus::CHudVoiceSelfStatus( const char *pName ) :
  41. vgui::Panel( NULL, "HudVoiceSelfStatus" ), CHudElement( pName )
  42. {
  43. SetParent( GetClientMode()->GetViewport() );
  44. m_pVoiceIcon = NULL;
  45. SetHiddenBits( HIDEHUD_MISCSTATUS );
  46. m_clrIcon = Color(255,255,255,255);
  47. }
  48. void CHudVoiceSelfStatus::ApplySchemeSettings(vgui::IScheme *pScheme)
  49. {
  50. BaseClass::ApplySchemeSettings( pScheme );
  51. }
  52. void CHudVoiceSelfStatus::VidInit( void )
  53. {
  54. m_pVoiceIcon = HudIcons().GetIcon( "voice_self" );
  55. }
  56. bool CHudVoiceSelfStatus::ShouldDraw()
  57. {
  58. C_BasePlayer *player = C_BasePlayer::GetLocalPlayer();
  59. if ( !player )
  60. return false;
  61. if ( GetClientVoiceMgr()->IsLocalPlayerSpeaking( player->GetSplitScreenPlayerSlot() ) == false )
  62. return false;
  63. return CHudElement::ShouldDraw();
  64. }
  65. void CHudVoiceSelfStatus::Paint()
  66. {
  67. if( !m_pVoiceIcon )
  68. return;
  69. int x, y, w, h;
  70. GetBounds( x, y, w, h );
  71. C_BasePlayer *player = C_BasePlayer::GetLocalPlayer();
  72. if ( player &&
  73. GetClientVoiceMgr()->IsLocalPlayerSpeakingAboveThreshold( player->GetSplitScreenPlayerSlot() ) )
  74. {
  75. m_clrIcon[3] = 255;
  76. }
  77. else
  78. {
  79. // NOTE: Merge issue. This number should either be 0 or 255, dunno!
  80. m_clrIcon[3] = 0;
  81. }
  82. m_pVoiceIcon->DrawSelf( 0, 0, w, h, m_clrIcon );
  83. }
  84. //=============================================================================
  85. // Icons for other players using voice
  86. //=============================================================================
  87. class CHudVoiceStatus : public CHudElement, public vgui::Panel
  88. {
  89. public:
  90. DECLARE_CLASS_SIMPLE( CHudVoiceStatus, vgui::Panel );
  91. explicit CHudVoiceStatus( const char *name );
  92. ~CHudVoiceStatus( void );
  93. virtual bool ShouldDraw();
  94. virtual void Paint();
  95. virtual void VidInit();
  96. virtual void Init();
  97. virtual void OnThink();
  98. virtual void ApplySchemeSettings(vgui::IScheme *pScheme);
  99. protected:
  100. void ClearActiveList();
  101. int FindActiveSpeaker( int playerId );
  102. private:
  103. CHudTexture *m_pVoiceIcon;
  104. int m_iDeadImageID;
  105. Color m_clrIcon;
  106. struct ActiveSpeaker
  107. {
  108. int playerId;
  109. CAvatarImage* pAvatar;
  110. bool bSpeaking;
  111. float fAlpha;
  112. };
  113. CUtlLinkedList< ActiveSpeaker > m_SpeakingList;
  114. CPanelAnimationVar( vgui::HFont, m_NameFont, "Default", "Default" );
  115. CPanelAnimationVarAliasType( float, item_tall, "item_tall", "16", "proportional_float" );
  116. CPanelAnimationVarAliasType( float, item_wide, "item_wide", "160", "proportional_float" );
  117. CPanelAnimationVarAliasType( float, item_spacing, "item_spacing", "2", "proportional_float" );
  118. CPanelAnimationVarAliasType( bool, show_avatar, "show_avatar", "0", "bool" );
  119. CPanelAnimationVarAliasType( bool, show_friend, "show_friend", "1", "bool" );
  120. CPanelAnimationVarAliasType( float, avatar_ypos, "avatar_ypos", "0", "proportional_float" );
  121. CPanelAnimationVarAliasType( float, avatar_xpos, "avatar_xpos", "16", "proportional_float" );
  122. CPanelAnimationVarAliasType( float, avatar_tall, "avatar_tall", "16", "proportional_float" );
  123. CPanelAnimationVarAliasType( float, avatar_wide, "avatar_wide", "16", "proportional_float" );
  124. CPanelAnimationVarAliasType( bool, show_voice_icon, "show_voice_icon", "1", "bool" );
  125. CPanelAnimationVarAliasType( float, voice_icon_ypos, "icon_ypos", "0", "proportional_float" );
  126. CPanelAnimationVarAliasType( float, voice_icon_xpos, "icon_xpos", "24", "proportional_float" );
  127. CPanelAnimationVarAliasType( float, voice_icon_tall, "icon_tall", "16", "proportional_float" );
  128. CPanelAnimationVarAliasType( float, voice_icon_wide, "icon_wide", "16", "proportional_float" );
  129. CPanelAnimationVarAliasType( bool, show_dead_icon, "show_dead_icon", "1", "bool" );
  130. CPanelAnimationVarAliasType( float, dead_icon_ypos, "dead_ypos", "0", "proportional_float" );
  131. CPanelAnimationVarAliasType( float, dead_icon_xpos, "dead_xpos", "0", "proportional_float" );
  132. CPanelAnimationVarAliasType( float, dead_icon_tall, "dead_tall", "16", "proportional_float" );
  133. CPanelAnimationVarAliasType( float, dead_icon_wide, "dead_wide", "16", "proportional_float" );
  134. CPanelAnimationVarAliasType( float, text_xpos, "text_xpos", "40", "proportional_float" );
  135. CPanelAnimationVarAliasType( float, fade_in_time, "fade_in_time", "0.0", "float" );
  136. CPanelAnimationVarAliasType( float, fade_out_time, "fade_out_time", "0.0", "float" );
  137. };
  138. //DECLARE_HUDELEMENT( CHudVoiceStatus );
  139. CHudVoiceStatus::CHudVoiceStatus( const char *pName ) :
  140. vgui::Panel( NULL, "HudVoiceStatus" ), CHudElement( pName )
  141. {
  142. SetParent( GetClientMode()->GetViewport() );
  143. m_pVoiceIcon = NULL;
  144. SetHiddenBits( HIDEHUD_MISCSTATUS );
  145. m_clrIcon = Color(255,255,255,255);
  146. m_iDeadImageID = surface()->DrawGetTextureId( "hud/leaderboard_dead" );
  147. if ( m_iDeadImageID == -1 ) // we didn't find it, so create a new one
  148. {
  149. m_iDeadImageID = surface()->CreateNewTextureID();
  150. }
  151. surface()->DrawSetTextureFile( m_iDeadImageID, "hud/leaderboard_dead", true, false );
  152. }
  153. CHudVoiceStatus::~CHudVoiceStatus()
  154. {
  155. ClearActiveList();
  156. }
  157. void CHudVoiceStatus::ApplySchemeSettings(vgui::IScheme *pScheme)
  158. {
  159. BaseClass::ApplySchemeSettings( pScheme );
  160. }
  161. void CHudVoiceStatus::Init( void )
  162. {
  163. ClearActiveList();
  164. }
  165. void CHudVoiceStatus::VidInit( void )
  166. {
  167. m_pVoiceIcon = HudIcons().GetIcon( "voice_player" );
  168. }
  169. void CHudVoiceStatus::OnThink( void )
  170. {
  171. for ( int iPlayerIndex=1; iPlayerIndex<=gpGlobals->maxClients; iPlayerIndex++ )
  172. {
  173. int activeSpeakerIndex = FindActiveSpeaker(iPlayerIndex);
  174. bool bSpeaking = GetClientVoiceMgr()->IsPlayerSpeaking(iPlayerIndex);
  175. if (activeSpeakerIndex != m_SpeakingList.InvalidIndex() )
  176. {
  177. // update their speaking status
  178. m_SpeakingList[activeSpeakerIndex].bSpeaking = bSpeaking;
  179. }
  180. else
  181. {
  182. // [Forrest] Don't use UTIL_PlayerByIndex here. It may be null for some players when
  183. // a match starts because the server only passes full player info as it affects
  184. // the client.
  185. // if they are talking and not in the list, add them to the end
  186. if( bSpeaking )
  187. {
  188. ActiveSpeaker activeSpeaker;
  189. activeSpeaker.playerId = iPlayerIndex;
  190. activeSpeaker.bSpeaking = true;
  191. activeSpeaker.fAlpha = 0.0f;
  192. activeSpeaker.pAvatar = NULL;
  193. // [pfreese] If a player is now talking set up their avatar
  194. activeSpeaker.pAvatar = new CAvatarImage();
  195. #ifdef CSTRIKE_DLL
  196. // [jpaquin] this allows counter strike to display default avatars for bots. It can't be a virtual function on
  197. // C_BasePlayer because there would be no way to get a game specific default image if the player is null.
  198. extern vgui::IImage* GetDefaultAvatarImage( C_BasePlayer *pPlayer );
  199. activeSpeaker.pAvatar->SetDefaultImage( GetDefaultAvatarImage( UTIL_PlayerByIndex( activeSpeaker.playerId ) ) );
  200. #endif
  201. activeSpeaker.pAvatar->SetDrawFriend(show_friend);
  202. player_info_t pi;
  203. if ( engine->GetPlayerInfo( iPlayerIndex, &pi ) )
  204. {
  205. if ( steamapicontext != NULL && steamapicontext->SteamUtils() != NULL )
  206. {
  207. CSteamID steamIDForPlayer( pi.friendsID, 1, steamapicontext->SteamUtils()->GetConnectedUniverse(), k_EAccountTypeIndividual );
  208. activeSpeaker.pAvatar->SetAvatarSteamID( steamIDForPlayer, eAvatarSmall );
  209. }
  210. }
  211. activeSpeaker.pAvatar->SetAvatarSize( avatar_wide, avatar_tall);
  212. m_SpeakingList.AddToTail(activeSpeaker);
  213. }
  214. }
  215. }
  216. float fTime = gpGlobals->frametime;
  217. for ( int i = m_SpeakingList.Head(); i != m_SpeakingList.InvalidIndex(); )
  218. {
  219. ActiveSpeaker& activeSpeaker = m_SpeakingList[i];
  220. if (activeSpeaker.bSpeaking)
  221. {
  222. if ( fade_in_time > 0.0f )
  223. {
  224. activeSpeaker.fAlpha += fTime / fade_in_time;
  225. if ( activeSpeaker.fAlpha > 1.0f )
  226. activeSpeaker.fAlpha = 1.0f;
  227. }
  228. else
  229. {
  230. activeSpeaker.fAlpha = 1.0f;
  231. }
  232. }
  233. else
  234. {
  235. if ( fade_out_time > 0.0f )
  236. {
  237. activeSpeaker.fAlpha -= fTime / fade_out_time;
  238. }
  239. else
  240. {
  241. activeSpeaker.fAlpha = 0.0f;
  242. }
  243. if ( activeSpeaker.fAlpha <= 0.0f )
  244. {
  245. // completely faded, remove them them from the list
  246. delete activeSpeaker.pAvatar;
  247. int iNext = m_SpeakingList.Next(i);
  248. m_SpeakingList.Remove(i);
  249. i = iNext;
  250. continue;
  251. }
  252. }
  253. i = m_SpeakingList.Next(i);
  254. }
  255. }
  256. bool CHudVoiceStatus::ShouldDraw()
  257. {
  258. if ( IsInFreezeCam() == true )
  259. return false;
  260. return true;
  261. }
  262. void CHudVoiceStatus::Paint()
  263. {
  264. if( !m_pVoiceIcon )
  265. return;
  266. int x, y, w, h;
  267. GetBounds( x, y, w, h );
  268. // Heights to draw the current voice item at
  269. int ypos = h - item_tall;
  270. bool bAnySpeakers = ( m_SpeakingList.Count() > 0 );
  271. int iFontHeight = 0;
  272. if( bAnySpeakers )
  273. {
  274. surface()->DrawSetTextFont( m_NameFont );
  275. surface()->DrawSetTextColor( Color(255,255,255,255) );
  276. iFontHeight = surface()->GetFontTall( m_NameFont );
  277. }
  278. bool bHearEnemy = false;
  279. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer( );
  280. //draw everyone in the list!
  281. FOR_EACH_LL(m_SpeakingList, i)
  282. {
  283. int playerId = m_SpeakingList[i].playerId;
  284. bool bIsAlive = g_PR->IsAlive( playerId );
  285. float oldAlphaMultiplier = surface()->DrawGetAlphaMultiplier();
  286. surface()->DrawSetAlphaMultiplier(oldAlphaMultiplier * m_SpeakingList[i].fAlpha);
  287. Color c = g_PR->GetTeamColor( g_PR ? g_PR->GetTeam(playerId) : TEAM_UNASSIGNED );
  288. c[3] = 128;
  289. const char *pName = g_PR ? g_PR->GetPlayerName(playerId) : "unknown";
  290. wchar_t szconverted[ 64 ];
  291. // Add the location, if any
  292. bool usedLocation = false;
  293. if ( sv_talk_enemy_living.GetBool( ) && sv_talk_enemy_dead.GetBool( ) )
  294. {
  295. bHearEnemy = true;
  296. }
  297. else if ( !pLocalPlayer->IsAlive( ) && !bIsAlive )
  298. {
  299. bHearEnemy = sv_talk_enemy_dead.GetBool( );
  300. }
  301. else if ( pLocalPlayer->IsAlive( ) && bIsAlive )
  302. {
  303. bHearEnemy = sv_talk_enemy_living.GetBool( );
  304. }
  305. if ( bHearEnemy )
  306. {
  307. C_BasePlayer *pPlayer = UTIL_PlayerByIndex( playerId );
  308. if ( pPlayer )
  309. {
  310. const char *asciiLocation = pPlayer->GetLastKnownPlaceName();
  311. if ( asciiLocation && *asciiLocation )
  312. {
  313. const wchar_t *unicodeLocation = g_pVGuiLocalize->Find( asciiLocation );
  314. if ( unicodeLocation && *unicodeLocation )
  315. {
  316. wchar_t *formatStr = g_pVGuiLocalize->Find( "#Voice_UseLocation" );
  317. if ( formatStr )
  318. {
  319. wchar_t unicodeName[ 64 ];
  320. g_pVGuiLocalize->ConvertANSIToUnicode( pName, unicodeName, sizeof( unicodeName ) );
  321. g_pVGuiLocalize->ConstructString( szconverted, sizeof( szconverted ),
  322. formatStr, 2, unicodeName, unicodeLocation );
  323. usedLocation = true;
  324. }
  325. }
  326. }
  327. }
  328. }
  329. if ( !usedLocation )
  330. {
  331. g_pVGuiLocalize->ConvertANSIToUnicode( pName, szconverted, sizeof(szconverted) );
  332. }
  333. // Draw the item background
  334. surface()->DrawSetColor( c );
  335. surface()->DrawFilledRect( 0, ypos, item_wide, ypos + item_tall );
  336. if ( show_dead_icon && bIsAlive == false && m_iDeadImageID != -1 )
  337. {
  338. Vertex_t vert[4];
  339. float uv1 = 0.0f;
  340. float uv2 = 1.0f;
  341. // Draw the dead material
  342. surface()->DrawSetTexture( m_iDeadImageID );
  343. vert[0].Init( Vector2D( dead_icon_xpos, ypos + dead_icon_ypos ), Vector2D( uv1, uv1 ) );
  344. vert[1].Init( Vector2D( dead_icon_xpos + dead_icon_wide, ypos + dead_icon_ypos ), Vector2D( uv2, uv1 ) );
  345. vert[2].Init( Vector2D( dead_icon_xpos + dead_icon_wide, ypos + dead_icon_ypos + dead_icon_tall ), Vector2D( uv2, uv2 ) );
  346. vert[3].Init( Vector2D( dead_icon_xpos, ypos + dead_icon_ypos + dead_icon_tall ), Vector2D( uv1, uv2 ) );
  347. surface()->DrawSetColor(COLOR_WHITE);
  348. surface()->DrawTexturedPolygon( 4, vert );
  349. }
  350. // Draw the players icon
  351. if (show_avatar && m_SpeakingList[i].pAvatar)
  352. {
  353. m_SpeakingList[i].pAvatar->SetPos( avatar_xpos, ypos + avatar_ypos );
  354. m_SpeakingList[i].pAvatar->Paint();
  355. }
  356. // Draw the voice icon
  357. if (show_voice_icon)
  358. m_pVoiceIcon->DrawSelf( voice_icon_xpos, ypos + voice_icon_ypos, voice_icon_wide, voice_icon_tall, m_clrIcon );
  359. // Draw the player's name
  360. surface()->DrawSetTextColor(COLOR_WHITE);
  361. surface()->DrawSetTextPos( text_xpos, ypos + ( item_tall / 2 ) - ( iFontHeight / 2 ) );
  362. int iTextSpace = item_wide - text_xpos;
  363. // write as much of the name as will fit, truncate the rest and add ellipses
  364. int iNameLength = wcslen(szconverted);
  365. const wchar_t *pszconverted = szconverted;
  366. int iTextWidthCounter = 0;
  367. for( int j=0;j<iNameLength;j++ )
  368. {
  369. iTextWidthCounter += surface()->GetCharacterWidth( m_NameFont, pszconverted[j] );
  370. if( iTextWidthCounter > iTextSpace )
  371. {
  372. if( j > 3 )
  373. {
  374. szconverted[j-2] = '.';
  375. szconverted[j-1] = '.';
  376. szconverted[j] = '\0';
  377. }
  378. break;
  379. }
  380. }
  381. surface()->DrawPrintText( szconverted, wcslen(szconverted) );
  382. ypos -= ( item_spacing + item_tall );
  383. surface()->DrawSetAlphaMultiplier(oldAlphaMultiplier);
  384. }
  385. }
  386. int CHudVoiceStatus::FindActiveSpeaker( int playerId )
  387. {
  388. FOR_EACH_LL(m_SpeakingList, i)
  389. {
  390. if (m_SpeakingList[i].playerId == playerId)
  391. return i;
  392. }
  393. return m_SpeakingList.InvalidIndex();
  394. }
  395. void CHudVoiceStatus::ClearActiveList()
  396. {
  397. FOR_EACH_LL(m_SpeakingList, i)
  398. {
  399. delete m_SpeakingList[i].pAvatar;
  400. }
  401. m_SpeakingList.RemoveAll();
  402. }