diff options
Diffstat (limited to 'game/client/cstrike/radio_status.cpp')
| -rw-r--r-- | game/client/cstrike/radio_status.cpp | 421 |
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 ); + } + } + } +} + |