summaryrefslogtreecommitdiff
path: root/game/client/cstrike/radio_status.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/cstrike/radio_status.cpp')
-rw-r--r--game/client/cstrike/radio_status.cpp421
1 files changed, 421 insertions, 0 deletions
diff --git a/game/client/cstrike/radio_status.cpp b/game/client/cstrike/radio_status.cpp
new file mode 100644
index 0000000..6c50788
--- /dev/null
+++ b/game/client/cstrike/radio_status.cpp
@@ -0,0 +1,421 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include <string.h>
+#include <stdio.h>
+#include "voice_status.h"
+#include "radio_status.h"
+#include "c_playerresource.h"
+#include "cliententitylist.h"
+#include "c_baseplayer.h"
+#include "materialsystem/imesh.h"
+#include "view.h"
+#include "materialsystem/imaterial.h"
+#include "tier0/dbg.h"
+#include "cdll_int.h"
+#include "c_cs_player.h"
+#include "menu.h" // for CHudMenu defs
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+using namespace vgui;
+
+// ---------------------------------------------------------------------- //
+// The radio feedback manager for the client.
+// ---------------------------------------------------------------------- //
+static CRadioStatus s_RadioStatus;
+
+//
+//-----------------------------------------------------
+//
+
+// Stuff for the Radio Menus
+static void radio1_f( void );
+static void radio2_f( void );
+static void radio3_f( void );
+
+static ConCommand radio1( "radio1", radio1_f, "Opens a radio menu" );
+static ConCommand radio2( "radio2", radio2_f, "Opens a radio menu" );
+static ConCommand radio3( "radio3", radio3_f, "Opens a radio menu" );
+static int g_whichMenu = 0;
+
+//
+//--------------------------------------------------------------
+//
+// These methods will bring up the radio menus from the client side.
+// They mimic the old server commands of the same name, which used
+// to require a round-trip causing latency and unreliability in
+// menu responses. Only 1 message is sent to the server now which
+// includes both the menu name and the selected item. The server
+// is never informed that the menu has been displayed.
+//
+//--------------------------------------------------------------
+//
+void OpenRadioMenu( int index )
+{
+ // do not show the menu if the player is dead or is an observer
+ C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer();
+ if ( !pPlayer )
+ return;
+
+ if ( !pPlayer->IsAlive() || pPlayer->IsObserver() )
+ return;
+
+ CHudMenu *pMenu = (CHudMenu *) gHUD.FindElement( "CHudMenu" );
+ if ( !pMenu )
+ return;
+
+ g_whichMenu = index;
+
+ //
+ // the 0x23f and 0x3ff are masks that describes the keys that
+ // are valid. This will have to be changed if the menus change.
+ //
+ switch ( index )
+ {
+ case 1:
+ pMenu->ShowMenu( "#RadioA", 0x23f );
+ break;
+ case 2:
+ pMenu->ShowMenu( "#RadioB", 0x23f );
+ break;
+ case 3:
+ pMenu->ShowMenu( "#RadioC", 0x3ff );
+ break;
+ default:
+ g_whichMenu = 0;
+ }
+}
+
+static void radio1_f( void )
+{
+ OpenRadioMenu( 1 );
+}
+
+static void radio2_f( void )
+{
+ OpenRadioMenu( 2 );
+}
+
+static void radio3_f( void )
+{
+ OpenRadioMenu( 3 );
+}
+
+CON_COMMAND_F( menuselect, "menuselect", FCVAR_CLIENTCMD_CAN_EXECUTE )
+{
+ if ( args.ArgC() < 2 )
+ return;
+
+ if( g_whichMenu == 0 )
+ {
+ // if we didn't have a menu open, maybe a plugin did. send it on to the server.
+ const char *cmd = VarArgs( "menuselect %s", args[1] );
+ engine->ServerCmd( cmd );
+ return;
+ }
+
+ int whichEntry = atoi( args[ 1 ] );
+
+ switch( g_whichMenu )
+ {
+ case 1: //RadioA
+ {
+ switch( whichEntry )
+ {
+ case 1: // coverme
+ engine->ClientCmd( "coverme" );
+ break;
+ case 2: // takepoint
+ engine->ClientCmd( "takepoint" );
+ break;
+ case 3: // holdpos
+ engine->ClientCmd( "holdpos" );
+ break;
+ case 4: // regroup
+ engine->ClientCmd( "regroup" );
+ break;
+ case 5: // followme
+ engine->ClientCmd( "followme" );
+ break;
+ case 6: // takingfire
+ engine->ClientCmd( "takingfire" );
+ break;
+ }
+ }
+ break;
+
+ case 2: //RadioB
+ {
+ switch( whichEntry )
+ {
+ case 1: // go
+ engine->ClientCmd( "go" );
+ break;
+ case 2: // fallback
+ engine->ClientCmd( "fallback" );
+ break;
+ case 3: // sticktog
+ engine->ClientCmd( "sticktog" );
+ break;
+ case 4: // getinpos
+ engine->ClientCmd( "getinpos" );
+ break;
+ case 5: // stormfront
+ engine->ClientCmd( "stormfront" );
+ break;
+ case 6: // report
+ engine->ClientCmd( "report" );
+ break;
+ }
+ }
+ break;
+
+ case 3: //RadioC
+ {
+ switch( whichEntry )
+ {
+ case 1: // roger
+ engine->ClientCmd( "roger" );
+ break;
+ case 2: // enemyspot
+ engine->ClientCmd( "enemyspot" );
+ break;
+ case 3: // needbackup
+ engine->ClientCmd( "needbackup" );
+ break;
+ case 4: // sectorclear
+ engine->ClientCmd( "sectorclear" );
+ break;
+ case 5: // inposition
+ engine->ClientCmd( "inposition" );
+ break;
+ case 6: // reportingin
+ engine->ClientCmd( "reportingin" );
+ break;
+ case 7: // getout
+ engine->ClientCmd( "getout" );
+ break;
+ case 8: // negative
+ engine->ClientCmd( "negative" );
+ break;
+ case 9: // enemydown
+ engine->ClientCmd( "enemydown" );
+ break;
+ }
+ }
+ break;
+
+ default:
+ // if we didn't have a menu open, maybe a plugin did. send it on to the server.
+ const char *cmd = VarArgs( "menuselect %d", whichEntry );
+ engine->ServerCmd( cmd );
+ break;
+ }
+
+ // reset menu
+ g_whichMenu = 0;
+}
+
+//
+//-----------------------------------------------------
+//
+
+CRadioStatus* RadioManager()
+{
+ return &s_RadioStatus;
+}
+
+
+// ---------------------------------------------------------------------- //
+// CRadioStatus.
+// ---------------------------------------------------------------------- //
+
+CRadioStatus::CRadioStatus()
+{
+ m_pHeadLabelMaterial = NULL;
+ Q_memset(m_radioUntil, 0, sizeof(m_radioUntil));
+ Q_memset(m_voiceUntil, 0, sizeof(m_voiceUntil));
+}
+
+bool CRadioStatus::Init()
+{
+ if ( !m_pHeadLabelMaterial )
+ {
+ m_pHeadLabelMaterial = materials->FindMaterial( "sprites/radio", TEXTURE_GROUP_VGUI );
+ }
+
+ if ( IsErrorMaterial( m_pHeadLabelMaterial ) )
+ return false;
+
+ m_pHeadLabelMaterial->IncrementReferenceCount();
+
+ return true;
+}
+
+void CRadioStatus::Shutdown()
+{
+ if ( m_pHeadLabelMaterial )
+ m_pHeadLabelMaterial->DecrementReferenceCount();
+
+ m_pHeadLabelMaterial = NULL;
+}
+
+void CRadioStatus::LevelInitPostEntity()
+{
+ ExpireBotVoice( true );
+ Q_memset(m_radioUntil, 0, sizeof(m_radioUntil));
+ Q_memset(m_voiceUntil, 0, sizeof(m_voiceUntil));
+}
+
+void CRadioStatus::LevelShutdownPreEntity()
+{
+ ExpireBotVoice( true );
+ Q_memset(m_radioUntil, 0, sizeof(m_radioUntil));
+ Q_memset(m_voiceUntil, 0, sizeof(m_voiceUntil));
+}
+
+extern float g_flHeadIconSize;
+
+static float s_flHeadOffset = 3;
+static float s_flHeadIconSize = 7;
+
+void CRadioStatus::DrawHeadLabels()
+{
+ ExpireBotVoice();
+
+ if( !m_pHeadLabelMaterial )
+ return;
+
+ for(int i=0; i < VOICE_MAX_PLAYERS; i++)
+ {
+ if ( m_radioUntil[i] < gpGlobals->curtime )
+ continue;
+
+ IClientNetworkable *pClient = cl_entitylist->GetClientEntity( i+1 );
+
+ // Don't show an icon if the player is not in our PVS.
+ if ( !pClient || pClient->IsDormant() )
+ continue;
+
+ C_BasePlayer *pPlayer = dynamic_cast<C_BasePlayer*>(pClient);
+ if( !pPlayer )
+ continue;
+
+ // Don't show an icon for dead or spectating players (ie: invisible entities).
+ if( pPlayer->IsPlayerDead() )
+ continue;
+
+ // Place it above his head.
+ Vector vOrigin = pPlayer->WorldSpaceCenter();
+ vOrigin.z += GetClientVoiceMgr()->GetHeadLabelOffset() + s_flHeadOffset;
+
+ if ( GetClientVoiceMgr()->IsPlayerSpeaking( i+1 ) )
+ {
+ vOrigin.z += g_flHeadIconSize;
+ }
+
+ // Align it so it never points up or down.
+ Vector vUp( 0, 0, 1 );
+ Vector vRight = CurrentViewRight();
+ if ( fabs( vRight.z ) > 0.95 ) // don't draw it edge-on
+ continue;
+
+ vRight.z = 0;
+ VectorNormalize( vRight );
+
+
+ float flSize = s_flHeadIconSize;
+
+ CMatRenderContextPtr pRenderContext( materials );
+
+ pRenderContext->Bind( m_pHeadLabelMaterial );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh();
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
+
+ meshBuilder.Color3f( 1.0, 1.0, 1.0 );
+ meshBuilder.TexCoord2f( 0,0,0 );
+ meshBuilder.Position3fv( (vOrigin + (vRight * -flSize) + (vUp * flSize)).Base() );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color3f( 1.0, 1.0, 1.0 );
+ meshBuilder.TexCoord2f( 0,1,0 );
+ meshBuilder.Position3fv( (vOrigin + (vRight * flSize) + (vUp * flSize)).Base() );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color3f( 1.0, 1.0, 1.0 );
+ meshBuilder.TexCoord2f( 0,1,1 );
+ meshBuilder.Position3fv( (vOrigin + (vRight * flSize) + (vUp * -flSize)).Base() );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color3f( 1.0, 1.0, 1.0 );
+ meshBuilder.TexCoord2f( 0,0,1 );
+ meshBuilder.Position3fv( (vOrigin + (vRight * -flSize) + (vUp * -flSize)).Base() );
+ meshBuilder.AdvanceVertex();
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+}
+
+
+void CRadioStatus::UpdateRadioStatus(int entindex, float duration)
+{
+ if(entindex > 0 && entindex <= VOICE_MAX_PLAYERS)
+ {
+ int iClient = entindex - 1;
+ if(iClient < 0)
+ return;
+
+ m_radioUntil[iClient] = gpGlobals->curtime + duration;
+ }
+}
+
+
+void CRadioStatus::UpdateVoiceStatus(int entindex, float duration)
+{
+ if(entindex > 0 && entindex <= VOICE_MAX_PLAYERS)
+ {
+ int iClient = entindex - 1;
+ if(iClient < 0)
+ return;
+
+ m_voiceUntil[iClient] = gpGlobals->curtime + duration;
+ GetClientVoiceMgr()->UpdateSpeakerStatus( entindex, true );
+ }
+}
+
+void CRadioStatus::ExpireBotVoice( bool force )
+{
+ for(int i=0; i < VOICE_MAX_PLAYERS; i++)
+ {
+ if ( m_voiceUntil[i] > 0.0f )
+ {
+ bool expire = force;
+
+ C_CSPlayer *player = static_cast<C_CSPlayer*>( cl_entitylist->GetEnt(i+1) );
+ if ( !player )
+ {
+ // player left the game
+ expire = true;
+ }
+ else if ( m_voiceUntil[i] < gpGlobals->curtime )
+ {
+ // player is done speaking
+ expire = true;
+ }
+
+ if ( expire )
+ {
+ m_voiceUntil[i] = 0.0f;
+ GetClientVoiceMgr()->UpdateSpeakerStatus( i+1, false );
+ }
+ }
+ }
+}
+