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.
421 lines
9.5 KiB
421 lines
9.5 KiB
//========= 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 ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|