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.

438 lines
10 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include <string.h>
  9. #include <stdio.h>
  10. #include "voice_status.h"
  11. #include "radio_status.h"
  12. #include "c_playerresource.h"
  13. #include "cliententitylist.h"
  14. #include "c_baseplayer.h"
  15. #include "materialsystem/imesh.h"
  16. #include "view.h"
  17. #include "materialsystem/imaterial.h"
  18. #include "tier0/dbg.h"
  19. #include "cdll_int.h"
  20. #include "c_cs_player.h"
  21. #include "menu.h" // for CHudMenu defs
  22. #include "Scaleform/HUD/sfhud_radio.h"
  23. #include "cs_gamerules.h"
  24. // memdbgon must be the last include file in a .cpp file!!!
  25. #include "tier0/memdbgon.h"
  26. using namespace vgui;
  27. // ---------------------------------------------------------------------- //
  28. // The radio feedback manager for the client.
  29. // ---------------------------------------------------------------------- //
  30. static CRadioStatus s_RadioStatus;
  31. //
  32. //-----------------------------------------------------
  33. //
  34. ConVar radio_icons_use_particles( "radio_icons_use_particles", "1", FCVAR_NONE, "0 = classic style, 1 = particles" );
  35. // Stuff for the Radio Menus
  36. static void radio1_f( const CCommand &args );
  37. static void radio2_f( const CCommand &args );
  38. static void radio3_f( const CCommand &args );
  39. static ConCommand radio1( "radio1", radio1_f, "Opens a radio menu" );
  40. static ConCommand radio2( "radio2", radio2_f, "Opens a radio menu" );
  41. static ConCommand radio3( "radio3", radio3_f, "Opens a radio menu" );
  42. static int g_whichMenu = 0;
  43. //
  44. //--------------------------------------------------------------
  45. //
  46. // These methods will bring up the radio menus from the client side.
  47. // They mimic the old server commands of the same name, which used
  48. // to require a round-trip causing latency and unreliability in
  49. // menu responses. Only 1 message is sent to the server now which
  50. // includes both the menu name and the selected item. The server
  51. // is never informed that the menu has been displayed.
  52. //
  53. //--------------------------------------------------------------
  54. //
  55. void OpenRadioMenu( int index )
  56. {
  57. if ( CSGameRules() && CSGameRules()->IsPlayingTraining() )
  58. return;
  59. C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer();
  60. if ( pLocalPlayer && pLocalPlayer->GetObserverMode() > OBS_MODE_NONE )
  61. return;
  62. SFHudRadio* pRadio = GET_HUDELEMENT( SFHudRadio );
  63. if ( pRadio )
  64. {
  65. pRadio->ShowRadioGroup( index );
  66. g_whichMenu = index;
  67. }
  68. }
  69. static void radio1_f( const CCommand &args )
  70. {
  71. OpenRadioMenu( 1 );
  72. }
  73. static void radio2_f( const CCommand &args )
  74. {
  75. OpenRadioMenu( 2 );
  76. }
  77. static void radio3_f( const CCommand &args )
  78. {
  79. OpenRadioMenu( 3 );
  80. }
  81. CON_COMMAND_F( menuselect, "menuselect", FCVAR_CLIENTCMD_CAN_EXECUTE )
  82. {
  83. if ( args.ArgC() < 2 )
  84. return;
  85. if( g_whichMenu == 0 )
  86. {
  87. // if we didn't have a menu open, maybe a plugin did. send it on to the server.
  88. const char *cmd = VarArgs( "menuselect %s", args[1] );
  89. engine->ServerCmd( cmd );
  90. return;
  91. }
  92. int whichEntry = atoi( args[ 1 ] );
  93. switch( g_whichMenu )
  94. {
  95. case 1: //RadioA
  96. {
  97. switch( whichEntry )
  98. {
  99. case 1: // coverme
  100. engine->ClientCmd( "coverme" );
  101. break;
  102. case 2: // takepoint
  103. engine->ClientCmd( "takepoint" );
  104. break;
  105. case 3: // holdpos
  106. engine->ClientCmd( "holdpos" );
  107. break;
  108. case 4: // regroup
  109. engine->ClientCmd( "regroup" );
  110. break;
  111. case 5: // followme
  112. engine->ClientCmd( "followme" );
  113. break;
  114. case 6: // takingfire
  115. engine->ClientCmd( "takingfire" );
  116. break;
  117. }
  118. }
  119. break;
  120. case 2: //RadioB
  121. {
  122. switch( whichEntry )
  123. {
  124. case 1: // go
  125. engine->ClientCmd( "go" );
  126. break;
  127. case 2: // fallback
  128. engine->ClientCmd( "fallback" );
  129. break;
  130. case 3: // sticktog
  131. engine->ClientCmd( "sticktog" );
  132. break;
  133. case 4: // getinpos
  134. engine->ClientCmd( "getinpos" );
  135. break;
  136. case 5: // stormfront
  137. engine->ClientCmd( "stormfront" );
  138. break;
  139. case 6: // report
  140. engine->ClientCmd( "report" );
  141. break;
  142. }
  143. }
  144. break;
  145. case 3: //RadioC
  146. {
  147. switch( whichEntry )
  148. {
  149. case 1: // roger
  150. engine->ClientCmd( "roger" );
  151. break;
  152. case 2: // enemyspot
  153. engine->ClientCmd( "enemyspot" );
  154. break;
  155. case 3: // needbackup
  156. engine->ClientCmd( "needbackup" );
  157. break;
  158. case 4: // sectorclear
  159. engine->ClientCmd( "sectorclear" );
  160. break;
  161. case 5: // inposition
  162. engine->ClientCmd( "inposition" );
  163. break;
  164. case 6: // reportingin
  165. engine->ClientCmd( "reportingin" );
  166. break;
  167. case 7: // getout
  168. engine->ClientCmd( "getout" );
  169. break;
  170. case 8: // negative
  171. engine->ClientCmd( "negative" );
  172. break;
  173. case 9: // enemydown
  174. engine->ClientCmd( "enemydown" );
  175. break;
  176. }
  177. }
  178. break;
  179. default:
  180. // if we didn't have a menu open, maybe a plugin did. send it on to the server.
  181. const char *cmd = VarArgs( "menuselect %d", whichEntry );
  182. engine->ServerCmd( cmd );
  183. break;
  184. }
  185. // reset menu
  186. g_whichMenu = 0;
  187. }
  188. //
  189. //-----------------------------------------------------
  190. //
  191. CRadioStatus* RadioManager()
  192. {
  193. return &s_RadioStatus;
  194. }
  195. // ---------------------------------------------------------------------- //
  196. // CRadioStatus.
  197. // ---------------------------------------------------------------------- //
  198. CRadioStatus::CRadioStatus()
  199. {
  200. m_pHeadLabelMaterial = NULL;
  201. Q_memset(m_radioUntil, 0, sizeof(m_radioUntil));
  202. Q_memset(m_voiceUntil, 0, sizeof(m_voiceUntil));
  203. }
  204. bool CRadioStatus::Init()
  205. {
  206. if ( !m_pHeadLabelMaterial )
  207. {
  208. m_pHeadLabelMaterial = materials->FindMaterial( "sprites/radio", TEXTURE_GROUP_VGUI );
  209. }
  210. if ( IsErrorMaterial( m_pHeadLabelMaterial ) && !g_bTextMode )
  211. return false;
  212. m_pHeadLabelMaterial->IncrementReferenceCount();
  213. return true;
  214. }
  215. void CRadioStatus::Shutdown()
  216. {
  217. if ( m_pHeadLabelMaterial )
  218. m_pHeadLabelMaterial->DecrementReferenceCount();
  219. m_pHeadLabelMaterial = NULL;
  220. }
  221. void CRadioStatus::LevelInitPostEntity()
  222. {
  223. ExpireBotVoice( true );
  224. Q_memset(m_radioUntil, 0, sizeof(m_radioUntil));
  225. Q_memset(m_voiceUntil, 0, sizeof(m_voiceUntil));
  226. }
  227. void CRadioStatus::LevelShutdownPreEntity()
  228. {
  229. ExpireBotVoice( true );
  230. Q_memset(m_radioUntil, 0, sizeof(m_radioUntil));
  231. Q_memset(m_voiceUntil, 0, sizeof(m_voiceUntil));
  232. }
  233. static float s_flHeadIconSize = 7;
  234. void CRadioStatus::DrawHeadLabels()
  235. {
  236. ExpireBotVoice();
  237. ConVarRef voice_head_icon_height( "voice_head_icon_height" );
  238. if( !m_pHeadLabelMaterial )
  239. return;
  240. for(int i=0; i < VOICE_MAX_PLAYERS; i++)
  241. {
  242. if ( m_radioUntil[i] < gpGlobals->curtime )
  243. continue;
  244. IClientNetworkable *pClient = cl_entitylist->GetClientEntity( i+1 );
  245. C_CSPlayer *pPlayer = dynamic_cast< C_CSPlayer* >( pClient );
  246. if( !pPlayer )
  247. continue;
  248. // Don't show an icon if the player is not in our PVS.
  249. if ( !pClient || pClient->IsDormant() )
  250. {
  251. if ( radio_icons_use_particles.GetBool() )
  252. {
  253. pPlayer->UpdateRadioHeadIcon( false );
  254. }
  255. continue;
  256. }
  257. if( !UTIL_PlayerByIndex( i+1 ) )
  258. {
  259. if ( radio_icons_use_particles.GetBool() )
  260. {
  261. pPlayer->UpdateRadioHeadIcon( false );
  262. }
  263. continue;
  264. }
  265. // Don't show an icon for dead or spectating players (ie: invisible entities).
  266. if( pPlayer->IsPlayerDead() )
  267. {
  268. if ( radio_icons_use_particles.GetBool() )
  269. {
  270. pPlayer->UpdateRadioHeadIcon( false );
  271. }
  272. continue;
  273. }
  274. // Don't show an icon for players we can't hear
  275. if ( !GetClientVoiceMgr()->IsPlayerAudible( i+1 ) )
  276. continue;
  277. if ( radio_icons_use_particles.GetBool() )
  278. {
  279. pPlayer->UpdateRadioHeadIcon( true );
  280. return;
  281. }
  282. // Place it above his head.
  283. Vector vOrigin = pPlayer->EyePosition();
  284. vOrigin.z += GetClientVoiceMgr()->GetHeadLabelOffset();
  285. // Place this above the speaking bubble if he's currently speaking.
  286. if ( GetClientVoiceMgr()->IsPlayerSpeaking( i+1 ) )
  287. {
  288. vOrigin.z += GetClientVoiceMgr()->GetHeadLabelOffset();
  289. }
  290. // Align it so it never points up or down.
  291. Vector vUp( 0, 0, 1 );
  292. Vector vRight = CurrentViewRight();
  293. if ( fabs( vRight.z ) > 0.95 ) // don't draw it edge-on
  294. continue;
  295. vRight.z = 0;
  296. VectorNormalize( vRight );
  297. float flSize = s_flHeadIconSize;
  298. CMatRenderContextPtr pRenderContext( materials );
  299. pRenderContext->Bind( m_pHeadLabelMaterial );
  300. IMesh *pMesh = pRenderContext->GetDynamicMesh();
  301. CMeshBuilder meshBuilder;
  302. meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
  303. meshBuilder.Color3f( 1.0, 1.0, 1.0 );
  304. meshBuilder.TexCoord2f( 0,0,0 );
  305. meshBuilder.Position3fv( (vOrigin + (vRight * -flSize) + (vUp * flSize)).Base() );
  306. meshBuilder.AdvanceVertex();
  307. meshBuilder.Color3f( 1.0, 1.0, 1.0 );
  308. meshBuilder.TexCoord2f( 0,1,0 );
  309. meshBuilder.Position3fv( (vOrigin + (vRight * flSize) + (vUp * flSize)).Base() );
  310. meshBuilder.AdvanceVertex();
  311. meshBuilder.Color3f( 1.0, 1.0, 1.0 );
  312. meshBuilder.TexCoord2f( 0,1,1 );
  313. meshBuilder.Position3fv( (vOrigin + (vRight * flSize) + (vUp * -flSize)).Base() );
  314. meshBuilder.AdvanceVertex();
  315. meshBuilder.Color3f( 1.0, 1.0, 1.0 );
  316. meshBuilder.TexCoord2f( 0,0,1 );
  317. meshBuilder.Position3fv( (vOrigin + (vRight * -flSize) + (vUp * -flSize)).Base() );
  318. meshBuilder.AdvanceVertex();
  319. meshBuilder.End();
  320. pMesh->Draw();
  321. }
  322. }
  323. void CRadioStatus::UpdateRadioStatus(int entindex, float duration)
  324. {
  325. if(entindex > 0 && entindex <= VOICE_MAX_PLAYERS)
  326. {
  327. int iClient = entindex - 1;
  328. if(iClient < 0)
  329. return;
  330. m_radioUntil[iClient] = gpGlobals->curtime + duration;
  331. }
  332. }
  333. void CRadioStatus::UpdateVoiceStatus(int entindex, float duration)
  334. {
  335. if(entindex > 0 && entindex <= VOICE_MAX_PLAYERS)
  336. {
  337. int iClient = entindex - 1;
  338. if(iClient < 0)
  339. return;
  340. m_voiceUntil[iClient] = gpGlobals->curtime + duration;
  341. GetClientVoiceMgr()->UpdateSpeakerStatus( entindex, -1, true );
  342. }
  343. }
  344. void CRadioStatus::ExpireBotVoice( bool force )
  345. {
  346. for(int i=0; i < VOICE_MAX_PLAYERS; i++)
  347. {
  348. if ( m_voiceUntil[i] > 0.0f )
  349. {
  350. bool expire = force;
  351. C_CSPlayer *player = static_cast<C_CSPlayer*>( cl_entitylist->GetEnt(i+1) );
  352. if ( !player )
  353. {
  354. // player left the game
  355. expire = true;
  356. }
  357. else if ( m_voiceUntil[i] < gpGlobals->curtime )
  358. {
  359. // player is done speaking
  360. expire = true;
  361. }
  362. if ( expire )
  363. {
  364. m_voiceUntil[i] = 0.0f;
  365. GetClientVoiceMgr()->UpdateSpeakerStatus( i+1, -1, false );
  366. }
  367. }
  368. }
  369. }