//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Normal HUD mode // // $Workfile: $ // $Date: $ // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "clientmode_shared.h" #include "iinput.h" #include "view_shared.h" #include "iviewrender.h" #include "hud_basechat.h" #include "weapon_selection.h" #include #include #include #include #include "engine/IEngineSound.h" #include #include #include "vgui_int.h" #include "hud_macros.h" #include "hltvcamera.h" #include "particlemgr.h" #include "c_vguiscreen.h" #include "c_team.h" #include "c_rumble.h" #include "fmtstr.h" #include "achievementmgr.h" #include "c_playerresource.h" #include "cam_thirdperson.h" #include #include "hud_vote.h" #include "ienginevgui.h" #include "sourcevr/isourcevirtualreality.h" #if defined( _X360 ) #include "xbox/xbox_console.h" #endif #if defined( REPLAY_ENABLED ) #include "replay/replaycamera.h" #include "replay/ireplaysystem.h" #include "replay/iclientreplaycontext.h" #include "replay/ireplaymanager.h" #include "replay/replay.h" #include "replay/ienginereplay.h" #include "replay/vgui/replayreminderpanel.h" #include "replay/vgui/replaymessagepanel.h" #include "econ/econ_controls.h" #include "econ/confirm_dialog.h" extern IClientReplayContext *g_pClientReplayContext; extern ConVar replay_rendersetting_renderglow; #endif #if defined USES_ECON_ITEMS #include "econ_item_view.h" #endif #if defined( TF_CLIENT_DLL ) #include "c_tf_player.h" #include "econ_item_description.h" #include "c_tf_team.h" #endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #define ACHIEVEMENT_ANNOUNCEMENT_MIN_TIME 10 class CHudWeaponSelection; class CHudChat; class CHudVote; static vgui::HContext s_hVGuiContext = DEFAULT_VGUI_CONTEXT; ConVar cl_drawhud( "cl_drawhud", "1", FCVAR_CHEAT, "Enable the rendering of the hud" ); ConVar hud_takesshots( "hud_takesshots", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Auto-save a scoreboard screenshot at the end of a map." ); ConVar hud_freezecamhide( "hud_freezecamhide", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Hide the HUD during freeze-cam" ); ConVar cl_show_num_particle_systems( "cl_show_num_particle_systems", "0", FCVAR_CLIENTDLL, "Display the number of active particle systems." ); extern ConVar v_viewmodel_fov; extern ConVar voice_modenable; extern bool IsInCommentaryMode( void ); extern const char* GetWearLocalizationString( float flWear ); CON_COMMAND( cl_reload_localization_files, "Reloads all localization files" ) { g_pVGuiLocalize->ReloadLocalizationFiles(); } #ifdef VOICE_VOX_ENABLE void VoxCallback( IConVar *var, const char *oldString, float oldFloat ) { if ( engine && engine->IsConnected() ) { ConVarRef voice_vox( var->GetName() ); if ( voice_vox.GetBool() && voice_modenable.GetBool() ) { engine->ClientCmd_Unrestricted( "voicerecord_toggle on\n" ); } else { engine->ClientCmd_Unrestricted( "voicerecord_toggle off\n" ); } } } ConVar voice_vox( "voice_vox", "0", FCVAR_ARCHIVE, "Voice chat uses a vox-style always on", true, 0, true, 1, VoxCallback ); // --------------------------------------------------------------------------------- // // CVoxManager. // --------------------------------------------------------------------------------- // class CVoxManager : public CAutoGameSystem { public: CVoxManager() : CAutoGameSystem( "VoxManager" ) { } virtual void LevelInitPostEntity( void ) { if ( voice_vox.GetBool() && voice_modenable.GetBool() ) { engine->ClientCmd_Unrestricted( "voicerecord_toggle on\n" ); } } virtual void LevelShutdownPreEntity( void ) { if ( voice_vox.GetBool() ) { engine->ClientCmd_Unrestricted( "voicerecord_toggle off\n" ); } } }; static CVoxManager s_VoxManager; // --------------------------------------------------------------------------------- // #endif // VOICE_VOX_ENABLE CON_COMMAND( hud_reloadscheme, "Reloads hud layout and animation scripts." ) { ClientModeShared *mode = ( ClientModeShared * )GetClientModeNormal(); if ( !mode ) return; mode->ReloadScheme(true); } #ifdef _DEBUG CON_COMMAND_F( crash, "Crash the client. Optional parameter -- type of crash:\n 0: read from NULL\n 1: write to NULL\n 2: DmCrashDump() (xbox360 only)", FCVAR_CHEAT ) { int crashtype = 0; int dummy; if ( args.ArgC() > 1 ) { crashtype = Q_atoi( args[1] ); } switch (crashtype) { case 0: dummy = *((int *) NULL); Msg("Crashed! %d\n", dummy); // keeps dummy from optimizing out break; case 1: *((int *)NULL) = 42; break; #if defined( _X360 ) case 2: XBX_CrashDump(false); break; #endif default: Msg("Unknown variety of crash. You have now failed to crash. I hope you're happy.\n"); break; } } #endif // _DEBUG static void __MsgFunc_Rumble( bf_read &msg ) { unsigned char waveformIndex; unsigned char rumbleData; unsigned char rumbleFlags; waveformIndex = msg.ReadByte(); rumbleData = msg.ReadByte(); rumbleFlags = msg.ReadByte(); RumbleEffect( waveformIndex, rumbleData, rumbleFlags ); } static void __MsgFunc_VGUIMenu( bf_read &msg ) { char panelname[2048]; msg.ReadString( panelname, sizeof(panelname) ); bool bShow = msg.ReadByte()!=0; IViewPortPanel *viewport = gViewPortInterface->FindPanelByName( panelname ); if ( !viewport ) { // DevMsg("VGUIMenu: couldn't find panel '%s'.\n", panelname ); return; } int count = msg.ReadByte(); if ( count > 0 ) { KeyValues *keys = new KeyValues("data"); //Msg( "MsgFunc_VGUIMenu:\n" ); for ( int i=0; iSetString( name, data ); } // !KLUDGE! Whitelist of URL protocols formats for MOTD if ( !V_stricmp( panelname, PANEL_INFO ) // MOTD && keys->GetInt( "type", 0 ) == 2 // URL message type ) { const char *pszURL = keys->GetString( "msg", "" ); if ( Q_strncmp( pszURL, "http://", 7 ) != 0 && Q_strncmp( pszURL, "https://", 8 ) != 0 && Q_stricmp( pszURL, "about:blank" ) != 0 ) { Warning( "Blocking MOTD URL '%s'; must begin with 'http://' or 'https://' or be about:blank\n", pszURL ); keys->deleteThis(); return; } } viewport->SetData( keys ); keys->deleteThis(); } // is the server telling us to show the scoreboard (at the end of a map)? if ( Q_stricmp( panelname, "scores" ) == 0 ) { if ( hud_takesshots.GetBool() == true ) { gHUD.SetScreenShotTime( gpGlobals->curtime + 1.0 ); // take a screenshot in 1 second } IGameEvent *event = gameeventmanager->CreateEvent( "ds_screenshot" ); if ( event ) { event->SetFloat( "delay", 0.5f ); gameeventmanager->FireEventClientSide( event ); } } // is the server trying to show an MOTD panel? Check that it's allowed right now. ClientModeShared *mode = ( ClientModeShared * )GetClientModeNormal(); if ( Q_stricmp( panelname, PANEL_INFO ) == 0 && mode ) { if ( !mode->IsInfoPanelAllowed() ) { return; } else { mode->InfoPanelDisplayed(); } } gViewPortInterface->ShowPanel( viewport, bShow ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- ClientModeShared::ClientModeShared() { m_pViewport = NULL; m_pChatElement = NULL; m_pWeaponSelection = NULL; m_nRootSize[ 0 ] = m_nRootSize[ 1 ] = -1; #if defined( REPLAY_ENABLED ) m_pReplayReminderPanel = NULL; m_flReplayStartRecordTime = 0.0f; m_flReplayStopRecordTime = 0.0f; #endif } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- ClientModeShared::~ClientModeShared() { delete m_pViewport; } void ClientModeShared::ReloadScheme( bool flushLowLevel ) { // Invalidate the global cache first. if (flushLowLevel) { KeyValuesSystem()->InvalidateCache(); } BuildGroup::ClearResFileCache(); m_pViewport->ReloadScheme( "resource/ClientScheme.res" ); } //---------------------------------------------------------------------------- // Purpose: Let the client mode set some vgui conditions //----------------------------------------------------------------------------- void ClientModeShared::ComputeVguiResConditions( KeyValues *pkvConditions ) { if ( UseVR() ) { pkvConditions->FindKey( "if_vr", true ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void ClientModeShared::Init() { m_pChatElement = ( CBaseHudChat * )GET_HUDELEMENT( CHudChat ); Assert( m_pChatElement ); m_pWeaponSelection = ( CBaseHudWeaponSelection * )GET_HUDELEMENT( CHudWeaponSelection ); Assert( m_pWeaponSelection ); KeyValuesAD pConditions( "conditions" ); ComputeVguiResConditions( pConditions ); // Derived ClientMode class must make sure m_Viewport is instantiated Assert( m_pViewport ); m_pViewport->LoadControlSettings( "scripts/HudLayout.res", NULL, NULL, pConditions ); #if defined( REPLAY_ENABLED ) m_pReplayReminderPanel = GET_HUDELEMENT( CReplayReminderPanel ); Assert( m_pReplayReminderPanel ); #endif ListenForGameEvent( "player_connect_client" ); ListenForGameEvent( "player_disconnect" ); ListenForGameEvent( "player_team" ); ListenForGameEvent( "server_cvar" ); ListenForGameEvent( "player_changename" ); ListenForGameEvent( "teamplay_broadcast_audio" ); ListenForGameEvent( "achievement_earned" ); #if defined( TF_CLIENT_DLL ) ListenForGameEvent( "item_found" ); #endif #if defined( REPLAY_ENABLED ) ListenForGameEvent( "replay_startrecord" ); ListenForGameEvent( "replay_endrecord" ); ListenForGameEvent( "replay_replaysavailable" ); ListenForGameEvent( "replay_servererror" ); ListenForGameEvent( "game_newmap" ); #endif #ifndef _XBOX HLTVCamera()->Init(); #if defined( REPLAY_ENABLED ) ReplayCamera()->Init(); #endif #endif m_CursorNone = vgui::dc_none; HOOK_MESSAGE( VGUIMenu ); HOOK_MESSAGE( Rumble ); } void ClientModeShared::InitViewport() { } void ClientModeShared::VGui_Shutdown() { delete m_pViewport; m_pViewport = NULL; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void ClientModeShared::Shutdown() { } //----------------------------------------------------------------------------- // Purpose: // Input : frametime - // *cmd - //----------------------------------------------------------------------------- bool ClientModeShared::CreateMove( float flInputSampleTime, CUserCmd *cmd ) { // Let the player override the view. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); if(!pPlayer) return true; // Let the player at it return pPlayer->CreateMove( flInputSampleTime, cmd ); } //----------------------------------------------------------------------------- // Purpose: // Input : *pSetup - //----------------------------------------------------------------------------- void ClientModeShared::OverrideView( CViewSetup *pSetup ) { QAngle camAngles; // Let the player override the view. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); if(!pPlayer) return; pPlayer->OverrideView( pSetup ); if( ::input->CAM_IsThirdPerson() ) { const Vector& cam_ofs = g_ThirdPersonManager.GetCameraOffsetAngles(); Vector cam_ofs_distance = g_ThirdPersonManager.GetFinalCameraOffset(); cam_ofs_distance *= g_ThirdPersonManager.GetDistanceFraction(); camAngles[ PITCH ] = cam_ofs[ PITCH ]; camAngles[ YAW ] = cam_ofs[ YAW ]; camAngles[ ROLL ] = 0; Vector camForward, camRight, camUp; if ( g_ThirdPersonManager.IsOverridingThirdPerson() == false ) { engine->GetViewAngles( camAngles ); } // get the forward vector AngleVectors( camAngles, &camForward, &camRight, &camUp ); VectorMA( pSetup->origin, -cam_ofs_distance[0], camForward, pSetup->origin ); VectorMA( pSetup->origin, cam_ofs_distance[1], camRight, pSetup->origin ); VectorMA( pSetup->origin, cam_ofs_distance[2], camUp, pSetup->origin ); // Override angles from third person camera VectorCopy( camAngles, pSetup->angles ); } else if (::input->CAM_IsOrthographic()) { pSetup->m_bOrtho = true; float w, h; ::input->CAM_OrthographicSize( w, h ); w *= 0.5f; h *= 0.5f; pSetup->m_OrthoLeft = -w; pSetup->m_OrthoTop = -h; pSetup->m_OrthoRight = w; pSetup->m_OrthoBottom = h; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool ClientModeShared::ShouldDrawEntity(C_BaseEntity *pEnt) { return true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool ClientModeShared::ShouldDrawParticles( ) { #ifdef TF_CLIENT_DLL C_TFPlayer *pTFPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( pTFPlayer && !pTFPlayer->ShouldPlayerDrawParticles() ) return false; #endif // TF_CLIENT_DLL return true; } //----------------------------------------------------------------------------- // Purpose: Allow weapons to override mouse input (for binoculars) //----------------------------------------------------------------------------- void ClientModeShared::OverrideMouseInput( float *x, float *y ) { C_BaseCombatWeapon *pWeapon = GetActiveWeapon(); if ( pWeapon ) { pWeapon->OverrideMouseInput( x, y ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool ClientModeShared::ShouldDrawViewModel() { return true; } bool ClientModeShared::ShouldDrawDetailObjects( ) { return true; } //----------------------------------------------------------------------------- // Purpose: Returns true if VR mode should black out everything outside the HUD. // This is used for things like sniper scopes and full screen UI //----------------------------------------------------------------------------- bool ClientModeShared::ShouldBlackoutAroundHUD() { return enginevgui->IsGameUIVisible(); } //----------------------------------------------------------------------------- // Purpose: Allows the client mode to override mouse control stuff in sourcevr //----------------------------------------------------------------------------- HeadtrackMovementMode_t ClientModeShared::ShouldOverrideHeadtrackControl() { return HMM_NOOVERRIDE; } //----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool ClientModeShared::ShouldDrawCrosshair( void ) { return true; } //----------------------------------------------------------------------------- // Purpose: Don't draw the current view entity if we are using the fake viewmodel instead //----------------------------------------------------------------------------- bool ClientModeShared::ShouldDrawLocalPlayer( C_BasePlayer *pPlayer ) { if ( ( pPlayer->index == render->GetViewEntity() ) && !C_BasePlayer::ShouldDrawLocalPlayer() ) return false; return true; } //----------------------------------------------------------------------------- // Purpose: The mode can choose to not draw fog //----------------------------------------------------------------------------- bool ClientModeShared::ShouldDrawFog( void ) { return true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void ClientModeShared::AdjustEngineViewport( int& x, int& y, int& width, int& height ) { } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void ClientModeShared::PreRender( CViewSetup *pSetup ) { } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void ClientModeShared::PostRender() { // Let the particle manager simulate things that haven't been simulated. ParticleMgr()->PostRender(); } void ClientModeShared::PostRenderVGui() { } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void ClientModeShared::Update() { #if defined( REPLAY_ENABLED ) UpdateReplayMessages(); #endif if ( m_pViewport->IsVisible() != cl_drawhud.GetBool() ) { m_pViewport->SetVisible( cl_drawhud.GetBool() ); } UpdateRumbleEffects(); if ( cl_show_num_particle_systems.GetBool() ) { int nCount = 0; for ( int i = 0; i < g_pParticleSystemMgr->GetParticleSystemCount(); i++ ) { const char *pParticleSystemName = g_pParticleSystemMgr->GetParticleSystemNameFromIndex(i); CParticleSystemDefinition *pParticleSystem = g_pParticleSystemMgr->FindParticleSystem( pParticleSystemName ); if ( !pParticleSystem ) continue; for ( CParticleCollection *pCurCollection = pParticleSystem->FirstCollection(); pCurCollection != NULL; pCurCollection = pCurCollection->GetNextCollectionUsingSameDef() ) { ++nCount; } } engine->Con_NPrintf( 0, "# Active particle systems: %i", nCount ); } } //----------------------------------------------------------------------------- // This processes all input before SV Move messages are sent //----------------------------------------------------------------------------- void ClientModeShared::ProcessInput(bool bActive) { gHUD.ProcessInput( bActive ); } //----------------------------------------------------------------------------- // Purpose: We've received a keypress from the engine. Return 1 if the engine is allowed to handle it. //----------------------------------------------------------------------------- int ClientModeShared::KeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding ) { if ( engine->Con_IsVisible() ) return 1; // Should we start typing a message? if ( pszCurrentBinding && ( Q_strcmp( pszCurrentBinding, "messagemode" ) == 0 || Q_strcmp( pszCurrentBinding, "say" ) == 0 ) ) { if ( down ) { StartMessageMode( MM_SAY ); } return 0; } else if ( pszCurrentBinding && ( Q_strcmp( pszCurrentBinding, "messagemode2" ) == 0 || Q_strcmp( pszCurrentBinding, "say_team" ) == 0 ) ) { if ( down ) { StartMessageMode( MM_SAY_TEAM ); } return 0; } // If we're voting... #ifdef VOTING_ENABLED CHudVote *pHudVote = GET_HUDELEMENT( CHudVote ); if ( pHudVote && pHudVote->IsVisible() ) { if ( !pHudVote->KeyInput( down, keynum, pszCurrentBinding ) ) { return 0; } } #endif C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); // if ingame spectator mode, let spectator input intercept key event here if( pPlayer && ( pPlayer->GetObserverMode() > OBS_MODE_DEATHCAM ) && !HandleSpectatorKeyInput( down, keynum, pszCurrentBinding ) ) { return 0; } // Let game-specific hud elements get a crack at the key input if ( !HudElementKeyInput( down, keynum, pszCurrentBinding ) ) { return 0; } C_BaseCombatWeapon *pWeapon = GetActiveWeapon(); if ( pWeapon ) { return pWeapon->KeyInput( down, keynum, pszCurrentBinding ); } return 1; } //----------------------------------------------------------------------------- // Purpose: See if spectator input occurred. Return 0 if the key is swallowed. //----------------------------------------------------------------------------- int ClientModeShared::HandleSpectatorKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding ) { // we are in spectator mode, open spectator menu if ( down && pszCurrentBinding && Q_strcmp( pszCurrentBinding, "+duck" ) == 0 ) { m_pViewport->ShowPanel( PANEL_SPECMENU, true ); return 0; // we handled it, don't handle twice or send to server } else if ( down && pszCurrentBinding && Q_strcmp( pszCurrentBinding, "+attack" ) == 0 ) { engine->ClientCmd( "spec_next" ); return 0; } else if ( down && pszCurrentBinding && Q_strcmp( pszCurrentBinding, "+attack2" ) == 0 ) { engine->ClientCmd( "spec_prev" ); return 0; } else if ( down && pszCurrentBinding && Q_strcmp( pszCurrentBinding, "+jump" ) == 0 ) { engine->ClientCmd( "spec_mode" ); return 0; } else if ( down && pszCurrentBinding && Q_strcmp( pszCurrentBinding, "+strafe" ) == 0 ) { HLTVCamera()->SetAutoDirector( true ); #if defined( REPLAY_ENABLED ) ReplayCamera()->SetAutoDirector( true ); #endif return 0; } return 1; } //----------------------------------------------------------------------------- // Purpose: See if hud elements want key input. Return 0 if the key is swallowed //----------------------------------------------------------------------------- int ClientModeShared::HudElementKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding ) { if ( m_pWeaponSelection ) { if ( !m_pWeaponSelection->KeyInput( down, keynum, pszCurrentBinding ) ) { return 0; } } #if defined( REPLAY_ENABLED ) if ( m_pReplayReminderPanel ) { if ( m_pReplayReminderPanel->HudElementKeyInput( down, keynum, pszCurrentBinding ) ) { return 0; } } #endif return 1; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool ClientModeShared::DoPostScreenSpaceEffects( const CViewSetup *pSetup ) { #if defined( REPLAY_ENABLED ) if ( engine->IsPlayingDemo() ) { if ( !replay_rendersetting_renderglow.GetBool() ) return false; } #endif return true; } //----------------------------------------------------------------------------- // Purpose: // Output : vgui::Panel //----------------------------------------------------------------------------- vgui::Panel *ClientModeShared::GetMessagePanel() { if ( m_pChatElement && m_pChatElement->GetInputPanel() && m_pChatElement->GetInputPanel()->IsVisible() ) return m_pChatElement->GetInputPanel(); return NULL; } //----------------------------------------------------------------------------- // Purpose: The player has started to type a message //----------------------------------------------------------------------------- void ClientModeShared::StartMessageMode( int iMessageModeType ) { // Can only show chat UI in multiplayer!!! if ( gpGlobals->maxClients == 1 ) { return; } if ( m_pChatElement ) { m_pChatElement->StartMessageMode( iMessageModeType ); } } //----------------------------------------------------------------------------- // Purpose: // Input : *newmap - //----------------------------------------------------------------------------- void ClientModeShared::LevelInit( const char *newmap ) { m_pViewport->GetAnimationController()->StartAnimationSequence("LevelInit"); // Tell the Chat Interface if ( m_pChatElement ) { m_pChatElement->LevelInit( newmap ); } // we have to fake this event clientside, because clients connect after that IGameEvent *event = gameeventmanager->CreateEvent( "game_newmap" ); if ( event ) { event->SetString("mapname", newmap ); gameeventmanager->FireEventClientSide( event ); } // Create a vgui context for all of the in-game vgui panels... if ( s_hVGuiContext == DEFAULT_VGUI_CONTEXT ) { s_hVGuiContext = vgui::ivgui()->CreateContext(); } // Reset any player explosion/shock effects CLocalPlayerFilter filter; enginesound->SetPlayerDSP( filter, 0, true ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void ClientModeShared::LevelShutdown( void ) { // Reset the third person camera so we don't crash g_ThirdPersonManager.Init(); if ( m_pChatElement ) { m_pChatElement->LevelShutdown(); } if ( s_hVGuiContext != DEFAULT_VGUI_CONTEXT ) { vgui::ivgui()->DestroyContext( s_hVGuiContext ); s_hVGuiContext = DEFAULT_VGUI_CONTEXT; } // Reset any player explosion/shock effects CLocalPlayerFilter filter; enginesound->SetPlayerDSP( filter, 0, true ); } void ClientModeShared::Enable() { vgui::VPANEL pRoot = VGui_GetClientDLLRootPanel(); // Add our viewport to the root panel. if( pRoot != 0 ) { m_pViewport->SetParent( pRoot ); } // All hud elements should be proportional // This sets that flag on the viewport and all child panels m_pViewport->SetProportional( true ); m_pViewport->SetCursor( m_CursorNone ); vgui::surface()->SetCursor( m_CursorNone ); m_pViewport->SetVisible( true ); if ( m_pViewport->IsKeyBoardInputEnabled() ) { m_pViewport->RequestFocus(); } Layout(); } void ClientModeShared::Disable() { vgui::VPANEL pRoot = VGui_GetClientDLLRootPanel(); // Remove our viewport from the root panel. if( pRoot != 0 ) { m_pViewport->SetParent( (vgui::VPANEL)NULL ); } m_pViewport->SetVisible( false ); } void ClientModeShared::Layout() { vgui::VPANEL pRoot = VGui_GetClientDLLRootPanel(); int wide, tall; // Make the viewport fill the root panel. if( pRoot != 0 ) { vgui::ipanel()->GetSize(pRoot, wide, tall); bool changed = wide != m_nRootSize[ 0 ] || tall != m_nRootSize[ 1 ]; m_nRootSize[ 0 ] = wide; m_nRootSize[ 1 ] = tall; m_pViewport->SetBounds(0, 0, wide, tall); if ( changed ) { ReloadScheme(false); } } } float ClientModeShared::GetViewModelFOV( void ) { return v_viewmodel_fov.GetFloat(); } class CHudChat; bool PlayerNameNotSetYet( const char *pszName ) { if ( pszName && pszName[0] ) { // Don't show "unconnected" if we haven't got the players name yet if ( Q_strnicmp(pszName,"unconnected",11) == 0 ) return true; if ( Q_strnicmp(pszName,"NULLNAME",11) == 0 ) return true; } return false; } void ClientModeShared::FireGameEvent( IGameEvent *event ) { CBaseHudChat *hudChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat ); const char *eventname = event->GetName(); if ( Q_strcmp( "player_connect_client", eventname ) == 0 ) { if ( !hudChat ) return; if ( PlayerNameNotSetYet(event->GetString("name")) ) return; if ( !IsInCommentaryMode() ) { wchar_t wszLocalized[100]; wchar_t wszPlayerName[MAX_PLAYER_NAME_LENGTH]; g_pVGuiLocalize->ConvertANSIToUnicode( event->GetString("name"), wszPlayerName, sizeof(wszPlayerName) ); g_pVGuiLocalize->ConstructString_safe( wszLocalized, g_pVGuiLocalize->Find( "#game_player_joined_game" ), 1, wszPlayerName ); char szLocalized[100]; g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalized, szLocalized, sizeof(szLocalized) ); hudChat->Printf( CHAT_FILTER_JOINLEAVE, "%s", szLocalized ); } } else if ( Q_strcmp( "player_disconnect", eventname ) == 0 ) { C_BasePlayer *pPlayer = USERID2PLAYER( event->GetInt("userid") ); if ( !hudChat || !pPlayer ) return; if ( PlayerNameNotSetYet(event->GetString("name")) ) return; if ( !IsInCommentaryMode() ) { wchar_t wszPlayerName[MAX_PLAYER_NAME_LENGTH]; g_pVGuiLocalize->ConvertANSIToUnicode( pPlayer->GetPlayerName(), wszPlayerName, sizeof(wszPlayerName) ); wchar_t wszReason[64]; const char *pszReason = event->GetString( "reason" ); if ( pszReason && ( pszReason[0] == '#' ) && g_pVGuiLocalize->Find( pszReason ) ) { V_wcsncpy( wszReason, g_pVGuiLocalize->Find( pszReason ), sizeof( wszReason ) ); } else { g_pVGuiLocalize->ConvertANSIToUnicode( pszReason, wszReason, sizeof(wszReason) ); } wchar_t wszLocalized[100]; if (IsPC()) { g_pVGuiLocalize->ConstructString_safe( wszLocalized, g_pVGuiLocalize->Find( "#game_player_left_game" ), 2, wszPlayerName, wszReason ); } else { g_pVGuiLocalize->ConstructString_safe( wszLocalized, g_pVGuiLocalize->Find( "#game_player_left_game" ), 1, wszPlayerName ); } char szLocalized[100]; g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalized, szLocalized, sizeof(szLocalized) ); hudChat->Printf( CHAT_FILTER_JOINLEAVE, "%s", szLocalized ); } } else if ( Q_strcmp( "player_team", eventname ) == 0 ) { C_BasePlayer *pPlayer = USERID2PLAYER( event->GetInt("userid") ); if ( !hudChat ) return; bool bDisconnected = event->GetBool("disconnect"); if ( bDisconnected ) return; int team = event->GetInt( "team" ); bool bAutoTeamed = event->GetInt( "autoteam", false ); bool bSilent = event->GetInt( "silent", false ); const char *pszName = event->GetString( "name" ); if ( PlayerNameNotSetYet( pszName ) ) return; if ( !bSilent ) { wchar_t wszPlayerName[MAX_PLAYER_NAME_LENGTH]; g_pVGuiLocalize->ConvertANSIToUnicode( pszName, wszPlayerName, sizeof(wszPlayerName) ); bool bUsingCustomTeamName = false; #ifdef TF_CLIENT_DLL C_TFTeam *pTeam = GetGlobalTFTeam( team ); const wchar_t *wszTeam = pTeam ? pTeam->Get_Localized_Name() : L""; bUsingCustomTeamName = pTeam ? pTeam->IsUsingCustomTeamName() : false; #else wchar_t wszTeam[64]; C_Team *pTeam = GetGlobalTeam( team ); if ( pTeam ) { g_pVGuiLocalize->ConvertANSIToUnicode( pTeam->Get_Name(), wszTeam, sizeof(wszTeam) ); } else { _snwprintf ( wszTeam, sizeof( wszTeam ) / sizeof( wchar_t ), L"%d", team ); } #endif if ( !IsInCommentaryMode() ) { wchar_t wszLocalized[100]; if ( bAutoTeamed ) { g_pVGuiLocalize->ConstructString_safe( wszLocalized, bUsingCustomTeamName ? g_pVGuiLocalize->Find( "#game_player_joined_autoteam_party_leader" ) : g_pVGuiLocalize->Find( "#game_player_joined_autoteam" ), 2, wszPlayerName, wszTeam ); } else { g_pVGuiLocalize->ConstructString_safe( wszLocalized, bUsingCustomTeamName ? g_pVGuiLocalize->Find( "#game_player_joined_team_party_leader" ) : g_pVGuiLocalize->Find( "#game_player_joined_team" ), 2, wszPlayerName, wszTeam ); } char szLocalized[100]; g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalized, szLocalized, sizeof(szLocalized) ); hudChat->Printf( CHAT_FILTER_TEAMCHANGE, "%s", szLocalized ); } } if ( pPlayer && pPlayer->IsLocalPlayer() ) { // that's me pPlayer->TeamChange( team ); } } else if ( Q_strcmp( "player_changename", eventname ) == 0 ) { if ( !hudChat ) return; const char *pszOldName = event->GetString("oldname"); if ( PlayerNameNotSetYet(pszOldName) ) return; wchar_t wszOldName[MAX_PLAYER_NAME_LENGTH]; g_pVGuiLocalize->ConvertANSIToUnicode( pszOldName, wszOldName, sizeof(wszOldName) ); wchar_t wszNewName[MAX_PLAYER_NAME_LENGTH]; g_pVGuiLocalize->ConvertANSIToUnicode( event->GetString( "newname" ), wszNewName, sizeof(wszNewName) ); wchar_t wszLocalized[100]; g_pVGuiLocalize->ConstructString_safe( wszLocalized, g_pVGuiLocalize->Find( "#game_player_changed_name" ), 2, wszOldName, wszNewName ); char szLocalized[100]; g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalized, szLocalized, sizeof(szLocalized) ); hudChat->Printf( CHAT_FILTER_NAMECHANGE, "%s", szLocalized ); } else if (Q_strcmp( "teamplay_broadcast_audio", eventname ) == 0 ) { int team = event->GetInt( "team" ); bool bValidTeam = false; if ( (GetLocalTeam() && GetLocalTeam()->GetTeamNumber() == team) ) { bValidTeam = true; } //If we're in the spectator team then we should be getting whatever messages the person I'm spectating gets. if ( bValidTeam == false ) { CBasePlayer *pSpectatorTarget = UTIL_PlayerByIndex( GetSpectatorTarget() ); if ( pSpectatorTarget && (GetSpectatorMode() == OBS_MODE_IN_EYE || GetSpectatorMode() == OBS_MODE_CHASE || GetSpectatorMode() == OBS_MODE_POI) ) { if ( pSpectatorTarget->GetTeamNumber() == team ) { bValidTeam = true; } } } if ( team == 0 && GetLocalTeam() > 0 ) { bValidTeam = false; } if ( team == 255 ) { bValidTeam = true; } if ( bValidTeam == true ) { EmitSound_t et; et.m_pSoundName = event->GetString("sound"); et.m_nFlags = event->GetInt("additional_flags"); CLocalPlayerFilter filter; C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, et ); } } else if ( Q_strcmp( "server_cvar", eventname ) == 0 ) { if ( !IsInCommentaryMode() ) { wchar_t wszCvarName[64]; g_pVGuiLocalize->ConvertANSIToUnicode( event->GetString("cvarname"), wszCvarName, sizeof(wszCvarName) ); wchar_t wszCvarValue[64]; g_pVGuiLocalize->ConvertANSIToUnicode( event->GetString("cvarvalue"), wszCvarValue, sizeof(wszCvarValue) ); wchar_t wszLocalized[256]; g_pVGuiLocalize->ConstructString_safe( wszLocalized, g_pVGuiLocalize->Find( "#game_server_cvar_changed" ), 2, wszCvarName, wszCvarValue ); char szLocalized[256]; g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalized, szLocalized, sizeof(szLocalized) ); hudChat->Printf( CHAT_FILTER_SERVERMSG, "%s", szLocalized ); } } else if ( Q_strcmp( "achievement_earned", eventname ) == 0 ) { int iPlayerIndex = event->GetInt( "player" ); C_BasePlayer *pPlayer = UTIL_PlayerByIndex( iPlayerIndex ); int iAchievement = event->GetInt( "achievement" ); if ( !hudChat || !pPlayer ) return; if ( !IsInCommentaryMode() ) { CAchievementMgr *pAchievementMgr = dynamic_cast( engine->GetAchievementMgr() ); if ( !pAchievementMgr ) return; IAchievement *pAchievement = pAchievementMgr->GetAchievementByID( iAchievement ); if ( pAchievement ) { if ( !pPlayer->IsDormant() && pPlayer->ShouldAnnounceAchievement() ) { pPlayer->SetNextAchievementAnnounceTime( gpGlobals->curtime + ACHIEVEMENT_ANNOUNCEMENT_MIN_TIME ); // no particle effect if the local player is the one with the achievement or the player is dead if ( !pPlayer->IsLocalPlayer() && pPlayer->IsAlive() ) { //tagES using the "head" attachment won't work for CS and DoD pPlayer->ParticleProp()->Create( "achieved", PATTACH_POINT_FOLLOW, "head" ); } pPlayer->OnAchievementAchieved( iAchievement ); } if ( g_PR ) { wchar_t wszPlayerName[MAX_PLAYER_NAME_LENGTH]; g_pVGuiLocalize->ConvertANSIToUnicode( g_PR->GetPlayerName( iPlayerIndex ), wszPlayerName, sizeof( wszPlayerName ) ); const wchar_t *pchLocalizedAchievement = ACHIEVEMENT_LOCALIZED_NAME_FROM_STR( pAchievement->GetName() ); if ( pchLocalizedAchievement ) { wchar_t wszLocalizedString[128]; g_pVGuiLocalize->ConstructString_safe( wszLocalizedString, g_pVGuiLocalize->Find( "#Achievement_Earned" ), 2, wszPlayerName, pchLocalizedAchievement ); char szLocalized[128]; g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalizedString, szLocalized, sizeof( szLocalized ) ); hudChat->ChatPrintf( iPlayerIndex, CHAT_FILTER_SERVERMSG, "%s", szLocalized ); } } } } } #if defined( TF_CLIENT_DLL ) else if ( Q_strcmp( "item_found", eventname ) == 0 ) { int iPlayerIndex = event->GetInt( "player" ); entityquality_t iItemQuality = event->GetInt( "quality" ); int iMethod = event->GetInt( "method" ); int iItemDef = event->GetInt( "itemdef" ); bool bIsStrange = event->GetInt( "isstrange" ); bool bIsUnusual = event->GetInt( "isunusual" ); float flWear = event->GetFloat( "wear" ); C_BasePlayer *pPlayer = UTIL_PlayerByIndex( iPlayerIndex ); const GameItemDefinition_t *pItemDefinition = dynamic_cast( GetItemSchema()->GetItemDefinition( iItemDef ) ); if ( !pPlayer || !pItemDefinition || pItemDefinition->IsHidden() ) return; if ( g_PR ) { wchar_t wszPlayerName[MAX_PLAYER_NAME_LENGTH]; g_pVGuiLocalize->ConvertANSIToUnicode( g_PR->GetPlayerName( iPlayerIndex ), wszPlayerName, sizeof( wszPlayerName ) ); if ( iMethod < 0 || iMethod >= ARRAYSIZE( g_pszItemFoundMethodStrings ) ) { iMethod = 0; } const char *pszLocString = g_pszItemFoundMethodStrings[iMethod]; if ( pszLocString ) { wchar_t wszItemFound[256]; _snwprintf( wszItemFound, ARRAYSIZE( wszItemFound ), L"%ls", g_pVGuiLocalize->Find( pszLocString ) ); wchar_t *colorMarker = wcsstr( wszItemFound, L"::" ); const CEconItemRarityDefinition* pItemRarity = GetItemSchema()->GetRarityDefinition( pItemDefinition->GetRarity() ); if ( colorMarker ) { if ( pItemRarity ) { attrib_colors_t colorRarity = pItemRarity->GetAttribColor(); vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" ); vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( scheme ); Color color = pScheme->GetColor( GetColorNameForAttribColor( colorRarity ), Color( 255, 255, 255, 255 ) ); hudChat->SetCustomColor( color ); } else { const char *pszQualityColorString = EconQuality_GetColorString( (EEconItemQuality)iItemQuality ); if ( pszQualityColorString ) { hudChat->SetCustomColor( pszQualityColorString ); } } *(colorMarker+1) = COLOR_CUSTOM; } // TODO: Update the localization strings to only have two format parameters since that's all we need. locchar_t wszLocalizedString[256]; locchar_t szItemname[64] = LOCCHAR( "" ); locchar_t szRarity[64] = LOCCHAR( "" ); locchar_t szWear[64] = LOCCHAR( "" ); locchar_t szStrange[64] = LOCCHAR( "" ); locchar_t szUnusual[64] = LOCCHAR( "" ); loc_scpy_safe( szItemname, CConstructLocalizedString(g_pVGuiLocalize->Find("TFUI_InvTooltip_ItemFound_Itemname"), CEconItemLocalizedFullNameGenerator(GLocalizationProvider(), pItemDefinition, iItemQuality).GetFullName() ) ); /*g_pVGuiLocalize->ConstructString_safe( szItemname, LOCCHAR( "%s1 " ), 1, CEconItemLocalizedFullNameGenerator( GLocalizationProvider(), pItemDefinition, iItemQuality ).GetFullName() );*/ locchar_t tempName[MAX_ITEM_NAME_LENGTH]; // If items have rarity if ( pItemRarity ) { // Weapon Wear if ( !IsWearableSlot( pItemDefinition->GetDefaultLoadoutSlot() ) ) { loc_scpy_safe(szWear, CConstructLocalizedString( g_pVGuiLocalize->Find("TFUI_InvTooltip_ItemFound_Wear"), g_pVGuiLocalize->Find(GetWearLocalizationString(flWear) ) ) ); } // Rarity / grade loc_scpy_safe(szRarity, CConstructLocalizedString(g_pVGuiLocalize->Find("TFUI_InvTooltip_ItemFound_Rarity"), g_pVGuiLocalize->Find(pItemRarity->GetLocKey() ) ) ); } if ( bIsUnusual ) { loc_scpy_safe(szUnusual, CConstructLocalizedString(g_pVGuiLocalize->Find("TFUI_InvTooltip_ItemFound_Unusual"), g_pVGuiLocalize->Find("rarity4"))); } if ( bIsStrange ) { loc_scpy_safe(szStrange, CConstructLocalizedString(g_pVGuiLocalize->Find("TFUI_InvTooltip_ItemFound_Strange"), g_pVGuiLocalize->Find("strange"))); } // // Strange Unusual Item Grade loc_scpy_safe( wszLocalizedString, CConstructLocalizedString( g_pVGuiLocalize->Find( "TFUI_InvTooltip_ItemFound" ), szStrange, szUnusual, szItemname, szRarity, szWear ) ); loc_scpy_safe( tempName, wszLocalizedString ); g_pVGuiLocalize->ConstructString_safe( wszLocalizedString, wszItemFound, 3, wszPlayerName, tempName, L"" ); char szLocalized[256]; g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalizedString, szLocalized, sizeof( szLocalized ) ); hudChat->ChatPrintf( iPlayerIndex, CHAT_FILTER_SERVERMSG, "%s", szLocalized ); } } } #endif #if defined( REPLAY_ENABLED ) else if ( !V_strcmp( "replay_servererror", eventname ) ) { DisplayReplayMessage( event->GetString( "error", "#Replay_DefaultServerError" ), replay_msgduration_error.GetFloat(), true, NULL, false ); } else if ( !V_strcmp( "replay_startrecord", eventname ) ) { m_flReplayStartRecordTime = gpGlobals->curtime; } else if ( !V_strcmp( "replay_endrecord", eventname ) ) { m_flReplayStopRecordTime = gpGlobals->curtime; } else if ( !V_strcmp( "replay_replaysavailable", eventname ) ) { DisplayReplayMessage( "#Replay_ReplaysAvailable", replay_msgduration_replaysavailable.GetFloat(), false, NULL, false ); } else if ( !V_strcmp( "game_newmap", eventname ) ) { // Make sure the instance count is reset to 0. Sometimes the count stay in sync and we get replay messages displaying lower than they should. CReplayMessagePanel::RemoveAll(); } #endif else { DevMsg( 2, "Unhandled GameEvent in ClientModeShared::FireGameEvent - %s\n", event->GetName() ); } } void ClientModeShared::UpdateReplayMessages() { #if defined( REPLAY_ENABLED ) // Received a replay_startrecord event? if ( m_flReplayStartRecordTime != 0.0f ) { DisplayReplayMessage( "#Replay_StartRecord", replay_msgduration_startrecord.GetFloat(), true, "replay\\startrecord.mp3", false ); m_flReplayStartRecordTime = 0.0f; m_flReplayStopRecordTime = 0.0f; } // Received a replay_endrecord event? if ( m_flReplayStopRecordTime != 0.0f ) { DisplayReplayMessage( "#Replay_EndRecord", replay_msgduration_stoprecord.GetFloat(), true, "replay\\stoprecord.wav", false ); // Hide the replay reminder if ( m_pReplayReminderPanel ) { m_pReplayReminderPanel->Hide(); } m_flReplayStopRecordTime = 0.0f; } if ( !engine->IsConnected() ) { ClearReplayMessageList(); } #endif } void ClientModeShared::ClearReplayMessageList() { #if defined( REPLAY_ENABLED ) CReplayMessagePanel::RemoveAll(); #endif } void ClientModeShared::DisplayReplayMessage( const char *pLocalizeName, float flDuration, bool bUrgent, const char *pSound, bool bDlg ) { #if defined( REPLAY_ENABLED ) // Don't display during replay playback, and don't allow more than 4 at a time const bool bInReplay = g_pEngineClientReplay->IsPlayingReplayDemo(); if ( bInReplay || ( !bDlg && CReplayMessagePanel::InstanceCount() >= 4 ) ) return; // Use default duration? if ( flDuration == -1.0f ) { flDuration = replay_msgduration_misc.GetFloat(); } // Display a replay message if ( bDlg ) { if ( engine->IsInGame() ) { Panel *pPanel = new CReplayMessageDlg( pLocalizeName ); pPanel->SetVisible( true ); pPanel->MakePopup(); pPanel->MoveToFront(); pPanel->SetKeyBoardInputEnabled( true ); pPanel->SetMouseInputEnabled( true ); #if defined( TF_CLIENT_DLL ) TFModalStack()->PushModal( pPanel ); #endif } else { ShowMessageBox( "#Replay_GenericMsgTitle", pLocalizeName, "#GameUI_OK" ); } } else { CReplayMessagePanel *pMsgPanel = new CReplayMessagePanel( pLocalizeName, flDuration, bUrgent ); pMsgPanel->Show(); } // Play a sound if appropriate if ( pSound ) { surface()->PlaySound( pSound ); } #endif } void ClientModeShared::DisplayReplayReminder() { #if defined( REPLAY_ENABLED ) if ( m_pReplayReminderPanel && g_pReplay->IsRecording() && !::input->IsSteamControllerActive() ) { // Only display the panel if we haven't already requested a replay for the given life CReplay *pCurLifeReplay = static_cast< CReplay * >( g_pClientReplayContext->GetReplayManager()->GetReplayForCurrentLife() ); if ( pCurLifeReplay && !pCurLifeReplay->m_bRequestedByUser && !pCurLifeReplay->m_bSaved ) { m_pReplayReminderPanel->Show(); } } #endif } //----------------------------------------------------------------------------- // In-game VGUI context //----------------------------------------------------------------------------- void ClientModeShared::ActivateInGameVGuiContext( vgui::Panel *pPanel ) { vgui::ivgui()->AssociatePanelWithContext( s_hVGuiContext, pPanel->GetVPanel() ); vgui::ivgui()->ActivateContext( s_hVGuiContext ); } void ClientModeShared::DeactivateInGameVGuiContext() { vgui::ivgui()->ActivateContext( DEFAULT_VGUI_CONTEXT ); }