Exemple #1
0
bool CHLTVDirector::SetCameraMan( int iPlayerIndex )
{
	if ( !tv_allow_camera_man.GetBool() )
		return false;

	if ( m_iCameraManIndex == iPlayerIndex )
		return true;

	// check if somebody else is already the camera man
	if ( m_iCameraManIndex != 0 && iPlayerIndex != 0 )
		return false;

	CBasePlayer *pPlayer = NULL;

	if ( iPlayerIndex > 0 )
	{
		pPlayer = UTIL_PlayerByIndex( iPlayerIndex );
		if ( !pPlayer || pPlayer->GetTeamNumber() != TEAM_SPECTATOR )
			return false;
	}

	m_iCameraManIndex = iPlayerIndex;

	// create event for director event history
	IGameEvent *event = gameeventmanager->CreateEvent( "hltv_cameraman" );
	if ( event )
	{
		event->SetInt("index", iPlayerIndex );
		gameeventmanager->FireEvent( event );
	}

	CRecipientFilter filter;

	for ( int i = 1; i <= gpGlobals->maxClients; i++ )
	{
		CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );

		if ( pPlayer && pPlayer->GetTeamNumber() == TEAM_SPECTATOR && !pPlayer->IsFakeClient() )
		{
			filter.AddRecipient( pPlayer );
		}
	}

	filter.MakeReliable();
	
	if ( iPlayerIndex > 0 )
	{
		// tell all spectators that the camera is avaliable again.
		char szText[200];
		Q_snprintf( szText, sizeof(szText), "SourceTV camera is now controlled by %s.", pPlayer->GetPlayerName() );
		UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, szText );
	}
	else
	{
		// tell all spectators that the camera is avaliable again.
		UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "SourceTV camera switched to auto-director mode." );
	}
	
	return true;
}
void CHL2MP_Player::DeathSound( const CTakeDamageInfo &info )
{
	if ( m_hRagdoll && m_hRagdoll->GetBaseAnimating()->IsDissolving() )
		 return;

	char szStepSound[128];

	Q_snprintf( szStepSound, sizeof( szStepSound ), "%s.Die", GetPlayerModelSoundPrefix() );

	const char *pModelName = STRING( GetModelName() );

	CSoundParameters params;
	if ( GetParametersForSound( szStepSound, params, pModelName ) == false )
		return;

	Vector vecOrigin = GetAbsOrigin();
	
	CRecipientFilter filter;
	filter.AddRecipientsByPAS( vecOrigin );

	EmitSound_t ep;
	ep.m_nChannel = params.channel;
	ep.m_pSoundName = params.soundname;
	ep.m_flVolume = params.volume;
	ep.m_SoundLevel = params.soundlevel;
	ep.m_nFlags = 0;
	ep.m_nPitch = params.pitch;
	ep.m_pOrigin = &vecOrigin;

	EmitSound( filter, entindex(), ep );
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CWarsWeapon::MakeTracer( const Vector &vecTracerSrc, const trace_t &tr, int iTracerType )
{
#if 0
	const char* tracer = "ASWUTracer";
	//if (GetActiveASWWeapon())
	//	tracer = GetActiveASWWeapon()->GetUTracerType();
#ifdef CLIENT_DLL
	CEffectData data;
	data.m_vOrigin = tr.endpos;
	data.m_hEntity = this;
	//data.m_nMaterial = m_iDamageAttributeEffects;

	DispatchEffect( tracer, data );
#else
	CRecipientFilter filter;
	filter.AddAllPlayers();
	if (gpGlobals->maxClients > 1 && GetCommander())
	{ 
		filter.RemoveRecipient(GetCommander());
	}

	UserMessageBegin( filter, tracer );
		WRITE_SHORT( entindex() );
		WRITE_FLOAT( tr.endpos.x );
		WRITE_FLOAT( tr.endpos.y );
		WRITE_FLOAT( tr.endpos.z );
		//WRITE_SHORT( m_iDamageAttributeEffects );
	MessageEnd();
#endif
#else
#ifdef CLIENT_DLL
	ASWUTracer( GetOwner(), tr.endpos, m_vTracerColor );
#endif
#endif // 0
}
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : origin - 
//			*soundname - 
//-----------------------------------------------------------------------------
void CMoveHelperServer::StartSound( const Vector& origin, const char *soundname )
{
	//MDB - Changing this to send to PAS, as the overloaded function below has done.
	//Also removed the UsePredictionRules, client does not yet play the equivalent sound

	CRecipientFilter filter;
	filter.AddRecipientsByPAS( origin );

	CBaseEntity::EmitSound( filter, m_pHostPlayer->entindex(), soundname );
}
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : step - 
//			fvol - 
//			force - force sound to play
//-----------------------------------------------------------------------------
void CDODPlayer::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force )
{
	if ( gpGlobals->maxClients > 1 && !sv_footsteps.GetFloat() )
		return;

#if defined( CLIENT_DLL )
	// during prediction play footstep sounds only once
	if ( prediction->InPrediction() && !prediction->IsFirstTimePredicted() )
		return;
#endif

	if ( !psurface )
		return;

	unsigned short stepSoundName = m_Local.m_nStepside ? psurface->sounds.stepleft : psurface->sounds.stepright;
	m_Local.m_nStepside = !m_Local.m_nStepside;

	if ( !stepSoundName )
		return;

	IPhysicsSurfaceProps *physprops = MoveHelper( )->GetSurfaceProps();
	const char *pSoundName = physprops->GetString( stepSoundName );
	CSoundParameters params;

	// we don't always know the model, so go by team
	char *pModelNameForGender = DOD_PLAYERMODEL_AXIS_RIFLEMAN;

	if( GetTeamNumber() == TEAM_ALLIES )
		pModelNameForGender = DOD_PLAYERMODEL_US_RIFLEMAN;

	if ( !CBaseEntity::GetParametersForSound( pSoundName, params, pModelNameForGender ) )
		return;	

	CRecipientFilter filter;
	filter.AddRecipientsByPAS( vecOrigin );

#ifndef CLIENT_DLL
	// im MP, server removed all players in origins PVS, these players 
	// generate the footsteps clientside
	if ( gpGlobals->maxClients > 1 )
		filter.RemoveRecipientsByPVS( vecOrigin );
#endif

	EmitSound_t ep;
	ep.m_nChannel = params.channel;
	ep.m_pSoundName = params.soundname;
	ep.m_flVolume = fvol;
	ep.m_SoundLevel = params.soundlevel;
	ep.m_nFlags = 0;
	ep.m_nPitch = params.pitch;
	ep.m_pOrigin = &vecOrigin;

	EmitSound( filter, entindex(), ep );
}
	void EmitAmbientSound( int entindex, const Vector& origin, const char *soundname, float flVolume, int iFlags, int iPitch, float soundtime /*= 0.0f*/, float *duration /*=NULL*/ )
	{
		// Pull data from parameters
		CSoundParameters params;

		if ( !soundemitterbase->GetParametersForSound( soundname, params, GENDER_NONE ) )
		{
			return;
		}

		if( iFlags & SND_CHANGE_PITCH )
		{
			params.pitch = iPitch;
		}

		if( iFlags & SND_CHANGE_VOL )
		{
			params.volume = flVolume;
		}

#if defined( CLIENT_DLL )
		enginesound->EmitAmbientSound( params.soundname, params.volume, params.pitch, iFlags, soundtime );
#else
		engine->EmitAmbientSound(entindex, origin, params.soundname, params.volume, params.soundlevel, iFlags, params.pitch, soundtime );
#endif

		bool needsCC = !( iFlags & ( SND_STOP | SND_CHANGE_VOL | SND_CHANGE_PITCH ) );

		float soundduration = 0.0f;
		
		if ( duration || needsCC )
		{
			soundduration = enginesound->GetSoundDuration( params.soundname );
			if ( duration )
			{
				*duration = soundduration;
			}
		}

		TraceEmitSound( "EmitAmbientSound:  '%s' emitted as '%s' (ent %i)\n",
			soundname, params.soundname, entindex );

		// We only want to trigger the CC on the start of the sound, not on any changes or halting of the sound
		if ( needsCC )
		{
			CRecipientFilter filter;
			filter.AddAllPlayers();
			filter.MakeReliable();

			CUtlVector< Vector > dummy;
			EmitCloseCaption( filter, entindex, false, soundname, dummy, soundduration, false );
		}

	}
	void EmitCloseCaption( IRecipientFilter& filter, int entindex, const CSoundParameters & params, const EmitSound_t & ep )
	{
		// No close captions in multiplayer...
		if ( gpGlobals->maxClients > 1 || (gpGlobals->maxClients==1 && !g_pClosecaption->GetBool()))
		{
			return;
		}

		if ( !ep.m_bEmitCloseCaption )
		{
			return;
		}

		// NOTE:  We must make a copy or else if the filter is owned by a SoundPatch, we'll end up destructively removing
		//  all players from it!!!!
		CRecipientFilter filterCopy;
		filterCopy.CopyFrom( (CRecipientFilter &)filter );

		// Remove any players who don't want close captions
		CBaseEntity::RemoveRecipientsIfNotCloseCaptioning( (CRecipientFilter &)filterCopy );

		// Anyone left?
		if ( filterCopy.GetRecipientCount() <= 0 )
		{
			return;
		}

		float duration = 0.0f;
		if ( ep.m_pflSoundDuration )
		{
			duration = *ep.m_pflSoundDuration;
		}
		else
		{
			duration = enginesound->GetSoundDuration( params.soundname );
		}

		bool fromplayer = false;
		CBaseEntity *ent = CBaseEntity::Instance( entindex );
		if ( ent )
		{
			while ( ent )
			{
				if ( ent->IsPlayer() )
				{
					fromplayer = true;
					break;
				}

				ent = ent->GetOwnerEntity();
			}
		}
		EmitCloseCaption( filter, entindex, fromplayer, ep.m_pSoundName, ep.m_UtlVecSoundOrigin, duration, ep.m_bWarnOnMissingCloseCaption );
	}
// if we've already started showing the credits and another player has joined up, show him them too
void CASW_Intro_Control::PlayerSpawned(CASW_Player *pPlayer)
{
    Msg("CASW_Intro_Control::PlayerSpawned, showncredits = %d\n", m_bShownCredits);
    if (m_bShownCredits && pPlayer)
    {
        Msg("Sending user message to this player telling him to show the credits\n");
        CRecipientFilter filter;
        filter.AddRecipient(pPlayer);
        UserMessageBegin( filter, "LaunchCredits" );
        MessageEnd();
    }
}
int DotaObjective::OnTakeDamage( const CTakeDamageInfo &inputInfo )
{
	if ( m_bMet )
		return 0;	

	if ( !(inputInfo.GetDamageType() & DMG_BULLET) )
	{
		CHL2MP_Player * playerAttacker = ToHL2MPPlayer( inputInfo.GetAttacker() );

		CreepMaker * maker = (CreepMaker*)gEntList.FindEntityByName( NULL, m_creepMakerName );
		if ( !maker )
			AssertMsg( false, "Objective can't find its creepmaker!\n" );
		
		CAI_BaseNPC * guardian = (CAI_BaseNPC*)gEntList.FindEntityByName( NULL, m_guardianName );	
		if( guardian && guardian->IsAlive() )
		{
			if( playerAttacker )
				ClientPrint( playerAttacker, HUD_PRINTTALK, UTIL_VarArgs("The guradian is alive in this lane, you can't hurt the gate.\n") );
		}
		else
		{
			m_timesHit++;
			m_timesHit = min(m_timesHit, OBJECTIVE_HEALTHI); // make sure times hit never goes above the maximum times an objective can be hit!

			CRecipientFilter user;
			user.AddRecipientsByTeam( this->GetTeam() );
			user.MakeReliable();
			char szText[200];
			Q_snprintf( szText, sizeof(szText), "Your ally gate is under attack from an enemy!" );
			UTIL_ClientPrintFilter( user, HUD_PRINTCENTER, szText );
			
			if( playerAttacker )
				ClientPrint( playerAttacker, HUD_PRINTTALK, UTIL_VarArgs("Gate has %i health left.\n", OBJECTIVE_HEALTHI - m_timesHit) );

			if (m_timesHit >= OBJECTIVE_HEALTHI)
			{
				TakeAction(DOBJ_ACTION_CLOSE);
			} else {
				IGameEvent *pEvent = gameeventmanager->CreateEvent( "objectivegate_attacked" );

				if ( pEvent )
				{
					pEvent->SetString( "lane", GetLane() );
					pEvent->SetInt( "team", this->GetTeamNumber() );
					pEvent->SetFloat( "health", (OBJECTIVE_HEALTHF - m_timesHit)/OBJECTIVE_HEALTHF );
					gameeventmanager->FireEvent( pEvent );
				}
			}
		}
	}

	return 0;
}
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : step - 
//			fvol - 
//			force - force sound to play
//-----------------------------------------------------------------------------
void CHL2MP_Player::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force )
{
	if ( gpGlobals->maxClients > 1 && !sv_footsteps.GetFloat() )
		return;

#if defined( CLIENT_DLL )
	// during prediction play footstep sounds only once
	if ( !prediction->IsFirstTimePredicted() )
		return;
#endif

	if ( GetFlags() & FL_DUCKING )
		return;

	m_Local.m_nStepside = !m_Local.m_nStepside;

	char szStepSound[128];

	if ( m_Local.m_nStepside )
	{
		Q_snprintf( szStepSound, sizeof( szStepSound ), "%s.RunFootstepLeft", g_ppszPlayerSoundPrefixNames[m_iPlayerSoundType] );
	}
	else
	{
		Q_snprintf( szStepSound, sizeof( szStepSound ), "%s.RunFootstepRight", g_ppszPlayerSoundPrefixNames[m_iPlayerSoundType] );
	}

	CSoundParameters params;
	if ( GetParametersForSound( szStepSound, params, NULL ) == false )
		return;

	CRecipientFilter filter;
	filter.AddRecipientsByPAS( vecOrigin );

#ifndef CLIENT_DLL
	// im MP, server removed all players in origins PVS, these players 
	// generate the footsteps clientside
	if ( gpGlobals->maxClients > 1 )
		filter.RemoveRecipientsByPVS( vecOrigin );
#endif

	EmitSound_t ep;
	ep.m_nChannel = CHAN_BODY;
	ep.m_pSoundName = params.soundname;
	ep.m_flVolume = fvol;
	ep.m_SoundLevel = params.soundlevel;
	ep.m_nFlags = 0;
	ep.m_nPitch = params.pitch;
	ep.m_pOrigin = &vecOrigin;

	EmitSound( filter, entindex(), ep );
}
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : step - 
//			fvol - 
//			force - force sound to play
//-----------------------------------------------------------------------------
void CHL2MP_Player::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force )
{ 
	if ( gpGlobals->maxClients > 1 && !sv_footsteps.GetFloat() )
		return;

#if defined( CLIENT_DLL )
	// during prediction play footstep sounds only once
	if ( !prediction->IsFirstTimePredicted() )
		return;
#endif

	if ( GetFlags() & FL_DUCKING )
		return;

	int nSide = m_Local.m_nStepside;
	m_Local.m_nStepside = !m_Local.m_nStepside;
	unsigned short stepSoundName = nSide ? psurface->sounds.stepleft : psurface->sounds.stepright;
	IPhysicsSurfaceProps *physprops = MoveHelper()->GetSurfaceProps();
	const char *pSoundName = physprops->GetString( stepSoundName );

	CSoundParameters params;
	if ( GetParametersForSound( pSoundName, params, NULL ) == false )
		return;

	CRecipientFilter filter;
	filter.AddRecipientsByPAS( vecOrigin );

#ifndef CLIENT_DLL
	// im MP, server removed all players in origins PVS, these players 
	// generate the footsteps clientside
	if ( gpGlobals->maxClients > 1 )
		filter.RemoveRecipientsByPVS( vecOrigin );
#endif

	EmitSound_t ep;
	ep.m_nChannel = CHAN_BODY;
	ep.m_pSoundName = params.soundname;
	ep.m_flVolume = fvol;
	ep.m_SoundLevel = params.soundlevel;
	ep.m_nFlags = 0;
	ep.m_nPitch = params.pitch;
	ep.m_pOrigin = &vecOrigin;

	EmitSound( filter, entindex(), ep );
}
	//-----------------------------------------------------------------------------
	// Purpose: Returning true means don't even call TE func
	// Input  : filter - 
	//			*suppress_host - 
	// Output : static bool
	//-----------------------------------------------------------------------------
	bool SuppressTE( CRecipientFilter& filter )
	{
		if ( GetSuppressHost() )
		{
			if ( !filter.IgnorePredictionCull() )
			{
				filter.RemoveRecipient( (CBasePlayer *)GetSuppressHost()  );
			}

			if ( !filter.GetRecipientCount() )
			{
				// Suppress it
				return true;
			}
		}

		// There's at least one recipient
		return false;
	}
Exemple #13
0
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTeam::AwardAchievement( int iAchievement )
{
	Assert( iAchievement >= 0 && iAchievement < 255 );	// must fit in short 

	CRecipientFilter filter;

	int iNumPlayers = GetNumPlayers();

	for ( int i=0;i<iNumPlayers;i++ )
	{
		if ( GetPlayer(i) )
		{
			filter.AddRecipient( GetPlayer(i) );
		}
	}

	UserMessageBegin( filter, "AchievementEvent" );
		WRITE_SHORT( iAchievement );
	MessageEnd();
}
void CDODPlayer::CheckProneMoveSound( int groundspeed, bool onground )
{
#ifdef CLIENT_DLL
	bool bShouldPlay = (groundspeed > 10) && (onground == true) && m_Shared.IsProne() && IsAlive();

	if ( m_bPlayingProneMoveSound && !bShouldPlay )
	{
		StopSound( "Player.MoveProne" );
		m_bPlayingProneMoveSound= false;
	}
	else if ( !m_bPlayingProneMoveSound && bShouldPlay )
	{
		CRecipientFilter filter;
		filter.AddRecipientsByPAS( WorldSpaceCenter() );
		EmitSound( filter, entindex(), "Player.MoveProne" );

		m_bPlayingProneMoveSound = true;
	}
#endif
}
//-----------------------------------------------------------------------------
// plays a sound
//-----------------------------------------------------------------------------
void CMoveHelperServer::StartSound( const Vector& origin, int channel, char const* sample, 
						float volume, soundlevel_t soundlevel, int fFlags, int pitch )
{

	CRecipientFilter filter;
	filter.AddRecipientsByPAS( origin );
	// FIXME, these sounds should not go to the host entity ( SND_NOTHOST )
	if ( gpGlobals->maxClients == 1 )
	{
		// Always send sounds down in SP

		EmitSound_t ep;
		ep.m_nChannel = channel;
		ep.m_pSoundName = sample;
		ep.m_flVolume = volume;
		ep.m_SoundLevel = soundlevel;
		ep.m_nFlags = fFlags;
		ep.m_nPitch = pitch;
		ep.m_pOrigin = &origin;

		CBaseEntity::EmitSound( filter, m_pHostPlayer->entindex(), ep );
	}
	else
	{
		filter.UsePredictionRules();

		EmitSound_t ep;
		ep.m_nChannel = channel;
		ep.m_pSoundName = sample;
		ep.m_flVolume = volume;
		ep.m_SoundLevel = soundlevel;
		ep.m_nFlags = fFlags;
		ep.m_nPitch = pitch;
		ep.m_pOrigin = &origin;

		CBaseEntity::EmitSound( filter, m_pHostPlayer->entindex(), ep );
	}
}
void CPortalGameMovement::PlayerRoughLandingEffects( float fvol )
{
	BaseClass::PlayerRoughLandingEffects( fvol );

#ifndef CLIENT_DLL
	if ( fvol >= 1.0 )
	{
		// Play the future shoes sound
		CRecipientFilter filter;
		filter.AddRecipientsByPAS( player->GetAbsOrigin() );

		CSoundParameters params;
		if ( CBaseEntity::GetParametersForSound( "PortalPlayer.FallRecover", params, NULL ) )
		{
			EmitSound_t ep( params );
			ep.m_nPitch = 125.0f - player->m_Local.m_flFallVelocity * 0.03f;					// lower pitch the harder they land
			ep.m_flVolume = MIN( player->m_Local.m_flFallVelocity * 0.00075f - 0.38, 1.0f );	// louder the harder they land

			CBaseEntity::EmitSound( filter, player->entindex(), ep );
		}
	}
#endif
}
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : src - 
//-----------------------------------------------------------------------------
void CRecipientFilter::CopyFrom( const CRecipientFilter& src )
{
	m_bReliable = src.IsReliable();
	m_bInitMessage = src.IsInitMessage();

	m_bUsingPredictionRules = src.IsUsingPredictionRules();
	m_bIgnorePredictionCull = src.IgnorePredictionCull();

	int c = src.GetRecipientCount();
	for ( int i = 0; i < c; ++i )
	{
		m_Recipients.AddToTail( src.GetRecipientIndex( i ) );
	}
}
void ClientPrintFilter(CBasePlayer *pPlayer, int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4 )
{
	CRecipientFilter filter;
	filter.AddRecipient( pPlayer );
	UTIL_ClientPrintFilter(filter, msg_dest, msg_name, param1, param2, param3, param4);
}
Exemple #19
0
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : step - 
//			fvol - 
//			force - force sound to play
//-----------------------------------------------------------------------------
void CBasePlayer::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force )
{
	if ( gpGlobals->maxClients > 1 && !sv_footsteps.GetFloat() )
		return;

#if defined( CLIENT_DLL )
	// during prediction play footstep sounds only once
	if ( prediction->InPrediction() && !prediction->IsFirstTimePredicted() )
		return;
#endif

	if ( !psurface )
		return;

	int nSide = m_Local.m_nStepside;
	unsigned short stepSoundName = nSide ? psurface->sounds.stepleft : psurface->sounds.stepright;
	if ( !stepSoundName )
		return;

	m_Local.m_nStepside = !nSide;

	CSoundParameters params;

	Assert( nSide == 0 || nSide == 1 );

	if ( m_StepSoundCache[ nSide ].m_usSoundNameIndex == stepSoundName )
	{
		params = m_StepSoundCache[ nSide ].m_SoundParameters;
	}
	else
	{
		IPhysicsSurfaceProps *physprops = MoveHelper()->GetSurfaceProps();
		const char *pSoundName = physprops->GetString( stepSoundName );
		if ( !CBaseEntity::GetParametersForSound( pSoundName, params, NULL ) )
			return;

		// Only cache if there's one option.  Otherwise we'd never here any other sounds
		if ( params.count == 1 )
		{
			m_StepSoundCache[ nSide ].m_usSoundNameIndex = stepSoundName;
			m_StepSoundCache[ nSide ].m_SoundParameters = params;
		}
	}

	CRecipientFilter filter;
	filter.AddRecipientsByPAS( vecOrigin );

#ifndef CLIENT_DLL
	// in MP, server removes all players in the vecOrigin's PVS, these players generate the footsteps client side
	if ( gpGlobals->maxClients > 1 )
	{
		filter.RemoveRecipientsByPVS( vecOrigin );
	}
#endif

	EmitSound_t ep;
	ep.m_nChannel = CHAN_BODY;
	ep.m_pSoundName = params.soundname;
	ep.m_flVolume = fvol;
	ep.m_SoundLevel = params.soundlevel;
	ep.m_nFlags = 0;
	ep.m_nPitch = params.pitch;
	ep.m_pOrigin = &vecOrigin;

	EmitSound( filter, entindex(), ep );
}
Exemple #20
0
	VoiceCommandMenuItem_t *CMultiplayRules::VoiceCommand( CBaseMultiplayerPlayer *pPlayer, int iMenu, int iItem )
	{
		// have the player speak the concept that is in a particular menu slot
		if ( !pPlayer )
			return NULL;

		if ( iMenu < 0 || iMenu >= m_VoiceCommandMenus.Count() )
			return NULL;

		if ( iItem < 0 || iItem >= m_VoiceCommandMenus.Element( iMenu ).Count() )
			return NULL;

		VoiceCommandMenuItem_t *pItem = &m_VoiceCommandMenus.Element( iMenu ).Element( iItem );

		Assert( pItem );

		char szResponse[AI_Response::MAX_RESPONSE_NAME];

		if ( pPlayer->CanSpeakVoiceCommand() )
		{
			CMultiplayer_Expresser *pExpresser = pPlayer->GetMultiplayerExpresser();
			Assert( pExpresser );
			pExpresser->AllowMultipleScenes();

			if ( pPlayer->SpeakConceptIfAllowed( pItem->m_iConcept, NULL, szResponse, AI_Response::MAX_RESPONSE_NAME ) )
			{
				// show a subtitle if we need to
				if ( pItem->m_bShowSubtitle )
				{
					CRecipientFilter filter;

					if ( pItem->m_bDistanceBasedSubtitle )
					{
						filter.AddRecipientsByPAS( pPlayer->WorldSpaceCenter() );

						// further reduce the range to a certain radius
						int i;
						for ( i = filter.GetRecipientCount()-1; i >= 0; i-- )
						{
							int index = filter.GetRecipientIndex(i);

							CBasePlayer *pListener = UTIL_PlayerByIndex( index );

							if ( pListener && pListener != pPlayer )
							{
								float flDist = ( pListener->WorldSpaceCenter() - pPlayer->WorldSpaceCenter() ).Length2D();

								if ( flDist > VOICE_COMMAND_MAX_SUBTITLE_DIST )
									filter.RemoveRecipientByPlayerIndex( index );
							}
						}
					}
					else
					{
						filter.AddAllPlayers();
					}

					// if we aren't a disguised spy
					if ( !pPlayer->ShouldShowVoiceSubtitleToEnemy() )
					{
						// remove players on other teams
						filter.RemoveRecipientsNotOnTeam( pPlayer->GetTeam() );
					}

					// Register this event in the mod-specific usermessages .cpp file if you hit this assert
					Assert( usermessages->LookupUserMessage( "VoiceSubtitle" ) != -1 );

					// Send a subtitle to anyone in the PAS
					UserMessageBegin( filter, "VoiceSubtitle" );
						WRITE_BYTE( pPlayer->entindex() );
						WRITE_BYTE( iMenu );
						WRITE_BYTE( iItem );
					MessageEnd();
				}

				pPlayer->NoteSpokeVoiceCommand( szResponse );
			}
			else
			{
				pItem = NULL;
			}

			pExpresser->DisallowMultipleScenes();
			return pItem;
		}

		return NULL;
	}
	void EmitCloseCaption( IRecipientFilter& filter, int entindex, bool fromplayer, char const *token, CUtlVector< Vector >& originlist, float duration, bool warnifmissing /*= false*/ )
	{
		// No close captions in multiplayer...
		if ( gpGlobals->maxClients > 1 || (gpGlobals->maxClients==1 && !g_pClosecaption->GetBool()))
		{
			return;
		}

		// A negative duration means fill it in from the wav file if possible
		if ( duration < 0.0f )
		{
			char const *wav = soundemitterbase->GetWavFileForSound( token, GENDER_NONE );
			if ( wav )
			{
				duration = enginesound->GetSoundDuration( wav );
			}
			else
			{
				duration = 2.0f;
			}
		}

		char lowercase[ 256 ];
		Q_strncpy( lowercase, token, sizeof( lowercase ) );
		Q_strlower( lowercase );
		if ( Q_strstr( lowercase, "\\" ) )
		{
			Hack_FixEscapeChars( lowercase );
		}

		// NOTE:  We must make a copy or else if the filter is owned by a SoundPatch, we'll end up destructively removing
		//  all players from it!!!!
		CRecipientFilter filterCopy;
		filterCopy.CopyFrom( (CRecipientFilter &)filter );

		// Remove any players who don't want close captions
		CBaseEntity::RemoveRecipientsIfNotCloseCaptioning( (CRecipientFilter &)filterCopy );

#if !defined( CLIENT_DLL )
		{
			// Defined in sceneentity.cpp
			bool AttenuateCaption( const char *token, const Vector& listener, CUtlVector< Vector >& soundorigins );

			if ( filterCopy.GetRecipientCount() > 0 )
			{
				int c = filterCopy.GetRecipientCount();
				for ( int i = c - 1 ; i >= 0; --i )
				{
					CBasePlayer *player = UTIL_PlayerByIndex( filterCopy.GetRecipientIndex( i ) );
					if ( !player )
						continue;

					Vector playerOrigin = player->GetAbsOrigin();

					if ( AttenuateCaption( lowercase, playerOrigin, originlist ) )
					{
						filterCopy.RemoveRecipient( player );
					}
				}
			}
		}
#endif
		// Anyone left?
		if ( filterCopy.GetRecipientCount() > 0 )
		{

#if !defined( CLIENT_DLL )

			byte byteflags = 0;
			if ( warnifmissing )
			{
				byteflags |= CLOSE_CAPTION_WARNIFMISSING;
			}
			if ( fromplayer )
			{
				byteflags |= CLOSE_CAPTION_FROMPLAYER;
			}

			CBaseEntity *pActor = CBaseEntity::Instance( entindex );
			if ( pActor )
			{
				char const *pszActorModel = STRING( pActor->GetModelName() );
				gender_t gender = soundemitterbase->GetActorGender( pszActorModel );

				if ( gender == GENDER_MALE )
				{
					byteflags |= CLOSE_CAPTION_GENDER_MALE;
				}
				else if ( gender == GENDER_FEMALE )
				{ 
					byteflags |= CLOSE_CAPTION_GENDER_FEMALE;
				}
			}

			// Send caption and duration hint down to client
			UserMessageBegin( filterCopy, "CloseCaption" );
				WRITE_STRING( lowercase );
				WRITE_SHORT( MIN( 255, (int)( duration * 10.0f ) ) ),
				WRITE_BYTE( byteflags ),
			MessageEnd();
#else
			// Direct dispatch
			CHudCloseCaption *cchud = GET_HUDELEMENT( CHudCloseCaption );
			if ( cchud )
			{
				cchud->ProcessCaption( lowercase, duration, fromplayer );
			}
#endif
		}
	}