summaryrefslogtreecommitdiff
path: root/game/client/tf/clientmode_tf.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/tf/clientmode_tf.cpp')
-rw-r--r--game/client/tf/clientmode_tf.cpp2301
1 files changed, 2301 insertions, 0 deletions
diff --git a/game/client/tf/clientmode_tf.cpp b/game/client/tf/clientmode_tf.cpp
new file mode 100644
index 0000000..b0a5afe
--- /dev/null
+++ b/game/client/tf/clientmode_tf.cpp
@@ -0,0 +1,2301 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// The copyright to the contents herein is the property of Valve, L.L.C.
+// The contents may be used and/or copied only with the written permission of
+// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
+// the agreement/contract under which the contents have been supplied.
+//
+// $Header: $
+// $NoKeywords: $
+//
+//=============================================================================
+#include "cbase.h"
+#include "hud.h"
+#include "clientmode_tf.h"
+#include "cdll_client_int.h"
+#include "iinput.h"
+#include "iviewrender.h"
+#include "vgui/ISurface.h"
+#include "vgui/IPanel.h"
+#include "GameUI/IGameUI.h"
+#include <vgui_controls/AnimationController.h>
+#include "ivmodemanager.h"
+#include "buymenu.h"
+#include "filesystem.h"
+#include "vgui/IVGui.h"
+#include "hud_chat.h"
+#include "view_shared.h"
+#include "view.h"
+#include "ivrenderview.h"
+#include "model_types.h"
+#include "iefx.h"
+#include "dlight.h"
+#include <imapoverview.h>
+#include "c_playerresource.h"
+#include <KeyValues.h>
+#include "text_message.h"
+#include "panelmetaclassmgr.h"
+#include "c_tf_player.h"
+#include "ienginevgui.h"
+#include "in_buttons.h"
+#include "voice_status.h"
+#include "tf_gamerules.h"
+#include "tf_hud_menu_engy_build.h"
+#include "tf_hud_menu_engy_destroy.h"
+#include "tf_hud_menu_spy_disguise.h"
+#include "tf_statsummary.h"
+#include "tf_hud_freezepanel.h"
+#include "item_quickswitch.h"
+#include "hud_macros.h"
+#include "vgui/ILocalize.h"
+#include "glow_outline_effect.h"
+#include "vgui/IInput.h"
+#include "tf_hud_mainmenuoverride.h"
+#include "tf_controls.h"
+#include "econ_notifications.h"
+#include "rtime.h"
+#include "econ_item_description.h"
+#include "c_tf_playerresource.h"
+#include "c_team.h"
+#include "tf_hud_menu_eureka_teleport.h"
+#include "tf_hud_menu_taunt_selection.h"
+#include "tf_hud_inspectpanel.h"
+#include "engine/IEngineSound.h"
+#ifdef STAGING_ONLY
+#include "tf_hud_menu_spy_build.h"
+#endif // STAGING_ONLY
+
+#include "quest_objective_manager.h"
+#include "econ_item_system.h"
+#include "tf_mann_vs_machine_stats.h"
+#include "tf_hud_mann_vs_machine_status.h"
+#include "player_vs_environment/c_tf_upgrades.h"
+
+#include "steam/isteamfriends.h"
+#include "steamworks_gamestats.h"
+#include "confirm_dialog.h"
+#include "ServerBrowser/blacklisted_server_manager.h"
+#include "tf_quickplay_shared.h"
+#include "sourcevr/isourcevirtualreality.h"
+#include "client_virtualreality.h"
+
+#include "econ_gcmessages.h"
+
+#if defined( _X360 )
+#include "tf_clientscoreboard.h"
+#endif
+
+#include "gc_clientsystem.h"
+#include "tf_gcmessages.h"
+#include "tf_gc_client.h"
+
+#include "tf_wardata.h"
+
+#include "debugoverlay_shared.h"
+
+#include "hud_vote.h"
+#include "c_tf_notification.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+void __MsgFunc_AutoBalanceVolunteer( bf_read &msg );
+void __MsgFunc_AutoBalanceVolunteer_Cancel( bf_read &msg );
+void __MsgFunc_PlayerIgnitedInv( bf_read &msg );
+void __MsgFunc_PlayerIgnited( bf_read &msg );
+void __MsgFunc_Damage( bf_read &msg );
+void __MsgFunc_HudArenaNotify( bf_read &msg );
+void __MsgFunc_UpdateAchievement( bf_read &msg );
+void __MsgFunc_DamageDodged( bf_read &msg );
+void __MsgFunc_PlayerJarated( bf_read &msg );
+void __MsgFunc_PlayerExtinguished( bf_read &msg );
+void __MsgFunc_BreakModel( bf_read &msg );
+void __MsgFunc_BreakModel_Pumpkin( bf_read &msg );
+void __MsgFunc_BreakModelRocketDud( bf_read &msg );
+void __MsgFunc_CheapBreakModel( bf_read &msg );
+void __MsgFunc_PlayerJaratedFade( bf_read &msg );
+void __MsgFunc_PlayerShieldBlocked( bf_read &msg );
+void __MsgFunc_PlayerBonusPoints( bf_read &msg );
+void __MsgFunc_SpawnFlyingBird( bf_read &msg );
+void __MsgFunc_PlayerGodRayEffect( bf_read &msg );
+void __MsgFunc_PlayerTeleportHomeEffect( bf_read &msg );
+void __MsgFunc_RDTeamPointsChanged( bf_read &msg );
+void __MsgFunc_PlayerLoadoutUpdated( bf_read &msg );
+void __MsgFunc_PlayerTauntSoundLoopStart( bf_read &msg );
+void __MsgFunc_PlayerTauntSoundLoopEnd( bf_read &msg );
+void __MsgFunc_ForcePlayerViewAngles( bf_read &msg );
+void __MsgFunc_BonusDucks( bf_read &msg );
+void __MsgFunc_PlayerPickupWeapon( bf_read &msg );
+void __MsgFunc_QuestObjectiveCompleted( bf_read &msg );
+
+#if !defined(NO_STEAM)
+extern ConVar cl_steamscreenshots;
+#endif
+
+extern ISoundEmitterSystemBase *soundemitterbase;
+
+static Color colorEyeballBossText( 134, 80, 172, 255 );
+static Color colorMerasmusText( 112, 176, 74, 255 );
+
+ConVar default_fov( "default_fov", "75", FCVAR_CHEAT );
+ConVar fov_desired( "fov_desired", "75", FCVAR_ARCHIVE | FCVAR_USERINFO, "Sets the base field-of-view.", true, 20.0, true, MAX_FOV );
+
+#define TF_HIGHFIVE_HINT_MAXDIST 512.0f
+#define TF_HIGHFIVE_HINT_MAXHINTS 3
+#define TF_HIGHFIVE_HINT_MINTIMEBETWEEN 10.0f
+ConVar tf_highfive_hintcount( "tf_highfive_hintcount", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_ARCHIVE, "Counts the number of times the high five hint has been displayed", true, 0, false, 0 );
+
+ConVar tf_taunt_always_show_hint( "tf_taunt_always_show_hint", "1", FCVAR_CLIENTDLL );
+extern ConVar tf_allow_all_team_partner_taunt;
+extern ConVar tf_mvm_buybacks_method;
+extern ConVar tf_autobalance_query_lifetime;
+extern ConVar tf_autobalance_xp_bonus;
+extern ConVar cl_notifications_show_ingame;
+
+extern ConVar sc_look_sensitivity_scale;
+
+extern bool TournamentHudElementKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding );
+extern bool ArenaClassLayoutKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding );
+extern bool CoachingHandlesKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding );
+extern bool ItemTestHandlesKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding );
+extern bool ShouldScoreBoardHandleKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding );
+
+static bool TrainingHandlesKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding )
+{
+ if ( TFGameRules() != NULL && TFGameRules()->IsInTraining() && TFGameRules()->IsWaitingForTrainingContinue() )
+ {
+ if ( down && keynum == KEY_SPACE )
+ {
+ engine->ClientCmd_Unrestricted( "training_continue" );
+ return true;
+ }
+ }
+ return false;
+}
+
+
+static bool HalloweenHandlesKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding )
+{
+ C_TFPlayer *pPlayer = ToTFPlayer( C_BasePlayer::GetLocalPlayer() );
+ if ( pPlayer )
+ {
+ // don't do anything while dancing
+ if ( pPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_THRILLER ) )
+ {
+ return true;
+ }
+
+ // only allow +attack
+ if ( pPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
+ {
+ if ( pszCurrentBinding )
+ {
+ if ( FStrEq( pszCurrentBinding, "+attack" ) )
+ {
+ engine->ServerCmd( "boo" );
+ return true;
+ }
+ else if ( FStrEq( pszCurrentBinding, "+attack2" ) || FStrEq( pszCurrentBinding, "+attack3" ) )
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+
+static bool TauntHandlesKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding )
+{
+ static const char *pszStopTauntKeys[] =
+ {
+ "+jump",
+ "+taunt",
+ "weapon_taunt",
+ };
+
+ C_TFPlayer *pPlayer = ToTFPlayer( C_BasePlayer::GetLocalPlayer() );
+ if ( pPlayer )
+ {
+ if ( pPlayer->m_Shared.InCond( TF_COND_TAUNTING ) )
+ {
+ for ( int i=0; i<ARRAYSIZE( pszStopTauntKeys ); ++i )
+ {
+ if ( down && pszCurrentBinding && FStrEq( pszCurrentBinding, pszStopTauntKeys[i] ) )
+ {
+ // Halloween Hackery
+ if ( i == 0 && pPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
+ {
+ continue;
+ }
+
+ engine->ClientCmd( "stop_taunt" );
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+
+// Sets convars to tag the current mapname and the player in your crosshairs.
+// The player tagged will be overridden for killcam shots to be the killer
+static void ScreenshotTaggingKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding )
+{
+ if ( pszCurrentBinding && ( FStrEq( pszCurrentBinding, "screenshot" ) || FStrEq( pszCurrentBinding, "jpeg" ) ) )
+ {
+ // Tag the player in the crosshairs
+ C_TFPlayer *pPlayer = ToTFPlayer( C_BasePlayer::GetLocalPlayer() );
+ if ( pPlayer )
+ {
+ C_TFPlayer *pCrosshairs = ToTFPlayer( UTIL_PlayerByIndex( pPlayer->GetIDTarget() ) );
+ if ( pCrosshairs )
+ {
+ CSteamID steamID;
+ if ( pCrosshairs->GetSteamID( &steamID ) )
+ {
+ ConVarRef cl_screenshotusertag( "cl_screenshotusertag" );
+ if ( cl_screenshotusertag.IsValid() )
+ {
+ cl_screenshotusertag.SetValue( (int)steamID.GetAccountID() );
+ }
+ }
+ }
+ }
+
+ // Tag the current map
+ ConVarRef cl_screenshotlocation( "cl_screenshotlocation" );
+ if ( cl_screenshotlocation.IsValid() )
+ {
+ char szMapName[MAX_MAP_NAME];
+ Q_FileBase( engine->GetLevelName(), szMapName, sizeof(szMapName) );
+ Q_strlower( szMapName );
+ cl_screenshotlocation.SetValue( GetMapDisplayName( szMapName ) );
+ }
+ }
+}
+
+
+static void EnableSteamScreenshots( bool bEnable )
+{
+#if !defined(NO_STEAM)
+ if ( steamapicontext && steamapicontext->SteamScreenshots() )
+ {
+ ConVarRef cl_savescreenshotstosteam( "cl_savescreenshotstosteam" );
+ if ( cl_savescreenshotstosteam.IsValid() )
+ {
+ cl_savescreenshotstosteam.SetValue( bEnable );
+ steamapicontext->SteamScreenshots()->HookScreenshots( bEnable );
+ }
+ }
+#endif
+}
+
+#if !defined(NO_STEAM)
+void SteamScreenshotsCallBack( IConVar *var, const char *pOldString, float flOldValue )
+{
+ EnableSteamScreenshots( cl_steamscreenshots.GetBool() );
+}
+ConVar cl_steamscreenshots( "cl_steamscreenshots", "1", FCVAR_ARCHIVE, "Enable/disable saving screenshots to Steam", SteamScreenshotsCallBack );
+#endif
+
+
+void HUDMinModeChangedCallBack( IConVar *var, const char *pOldString, float flOldValue )
+{
+ engine->ExecuteClientCmd( "hud_reloadscheme" );
+}
+ConVar cl_hud_minmode( "cl_hud_minmode", "0", FCVAR_ARCHIVE, "Set to 1 to turn on the advanced minimalist HUD mode.", HUDMinModeChangedCallBack );
+
+IClientMode *g_pClientMode = NULL;
+
+// --------------------------------------------------------------------------------- //
+// CTFModeManager.
+// --------------------------------------------------------------------------------- //
+
+class CTFModeManager : public IVModeManager
+{
+public:
+ virtual void Init();
+ virtual void SwitchMode( bool commander, bool force ) {}
+ virtual void LevelInit( const char *newmap );
+ virtual void LevelShutdown( void );
+ virtual void ActivateMouse( bool isactive ) {}
+};
+
+static CTFModeManager g_ModeManager;
+IVModeManager *modemanager = ( IVModeManager * )&g_ModeManager;
+
+
+// --------------------------------------------------------------------------------- //
+// CTFModeManager implementation.
+// --------------------------------------------------------------------------------- //
+
+#define SCREEN_FILE "scripts/vgui_screens.txt"
+
+void CTFModeManager::Init()
+{
+ g_pClientMode = GetClientModeNormal();
+
+ PanelMetaClassMgr()->LoadMetaClassDefinitionFile( SCREEN_FILE );
+
+ // Load the objects.txt file.
+ LoadObjectInfos( ::filesystem );
+
+ GetClientVoiceMgr()->SetHeadLabelOffset( 40 );
+
+ EnableSteamScreenshots( true );
+}
+
+void CTFModeManager::LevelInit( const char *newmap )
+{
+ g_pClientMode->LevelInit( newmap );
+
+ ConVarRef voice_steal( "voice_steal" );
+
+ if ( voice_steal.IsValid() )
+ {
+ voice_steal.SetValue( 1 );
+ }
+}
+
+void CTFModeManager::LevelShutdown( void )
+{
+ g_pClientMode->LevelShutdown();
+
+ extern void CL_Training_LevelShutdown();
+ extern void CL_Coaching_LevelShutdown();
+ extern void CL_Consumables_LevelShutdown();
+ extern void CL_Halloween_LevelShutdown();
+ CL_Training_LevelShutdown();
+ CL_Coaching_LevelShutdown();
+ CL_Consumables_LevelShutdown();
+ CL_Halloween_LevelShutdown();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+ClientModeTFNormal::ClientModeTFNormal()
+{
+ m_pMenuEngyBuild = NULL;
+ m_pMenuEngyDestroy = NULL;
+ m_pMenuSpyDisguise = NULL;
+ m_pEurekaTeleportMenu = NULL;
+ m_pMenuTauntSelection = NULL;
+#ifdef STAGING_ONLY
+ m_pMenuSpyBuild = NULL;
+#endif // STAGING_ONLY
+ m_pGameUI = NULL;
+ m_pFreezePanel = NULL;
+ m_pQuickSwitch = NULL;
+ m_pInspectPanel = NULL;
+ m_wasConnectedLastUpdate = false;
+ m_lastServerIP = 0;
+ m_lastServerPort = 0;
+ m_lastServerName = NULL;
+ m_lastServerConnectTime = 0;
+ m_pTeamGoalTournament = NULL;
+
+#if defined( _X360 )
+ m_pScoreboard = NULL;
+#endif
+
+ HOOK_MESSAGE( AutoBalanceVolunteer );
+ HOOK_MESSAGE( AutoBalanceVolunteer_Cancel );
+
+ // Hook global message handlers
+ HOOK_MESSAGE( PlayerIgnited );
+ HOOK_MESSAGE( PlayerIgnitedInv );
+ HOOK_MESSAGE( Damage );
+ HOOK_MESSAGE( HudArenaNotify );
+ HOOK_MESSAGE( UpdateAchievement );
+ HOOK_MESSAGE( DamageDodged );
+ HOOK_MESSAGE( PlayerJarated );
+ HOOK_MESSAGE( PlayerExtinguished );
+ HOOK_MESSAGE( BreakModel );
+ HOOK_MESSAGE( CheapBreakModel );
+ HOOK_MESSAGE( BreakModel_Pumpkin );
+ HOOK_MESSAGE( BreakModelRocketDud );
+ HOOK_MESSAGE( PlayerJaratedFade );
+ HOOK_MESSAGE( PlayerShieldBlocked );
+ HOOK_MESSAGE( PlayerBonusPoints );
+ HOOK_MESSAGE( SpawnFlyingBird );
+ HOOK_MESSAGE( PlayerGodRayEffect );
+ HOOK_MESSAGE( PlayerTeleportHomeEffect );
+ HOOK_MESSAGE( RDTeamPointsChanged );
+ HOOK_MESSAGE( PlayerLoadoutUpdated );
+ HOOK_MESSAGE( PlayerTauntSoundLoopStart );
+ HOOK_MESSAGE( PlayerTauntSoundLoopEnd );
+ HOOK_MESSAGE( ForcePlayerViewAngles );
+ HOOK_MESSAGE( BonusDucks );
+ HOOK_MESSAGE( PlayerPickupWeapon );
+ HOOK_MESSAGE( QuestObjectiveCompleted );
+
+#if !defined(NO_STEAM)
+ m_CallbackScreenshotRequested.Register( this, &ClientModeTFNormal::OnScreenshotRequested );
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: If you don't know what a destructor is by now, you are probably going to get fired
+//-----------------------------------------------------------------------------
+ClientModeTFNormal::~ClientModeTFNormal()
+{
+}
+
+// See interface.h/.cpp for specifics: basically this ensures that we actually Sys_UnloadModule the dll and that we don't call Sys_LoadModule
+// over and over again.
+static CDllDemandLoader g_GameUI( "GameUI" );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ClientModeTFNormal::Init()
+{
+ m_pMenuEngyBuild = ( CHudMenuEngyBuild * )GET_HUDELEMENT( CHudMenuEngyBuild );
+ Assert( m_pMenuEngyBuild );
+
+ m_pMenuEngyDestroy = ( CHudMenuEngyDestroy * )GET_HUDELEMENT( CHudMenuEngyDestroy );
+ Assert( m_pMenuEngyDestroy );
+
+ m_pMenuSpyDisguise = ( CHudMenuSpyDisguise * )GET_HUDELEMENT( CHudMenuSpyDisguise );
+ Assert( m_pMenuSpyDisguise );
+
+ m_pFreezePanel = ( CTFFreezePanel * )GET_HUDELEMENT( CTFFreezePanel );
+ Assert( m_pFreezePanel );
+
+ m_pQuickSwitch = ( CItemQuickSwitchPanel * )GET_HUDELEMENT( CItemQuickSwitchPanel );
+ Assert( m_pQuickSwitch );
+
+ m_pMenuTauntSelection = ( CHudMenuTauntSelection * )GET_HUDELEMENT( CHudMenuTauntSelection );
+ Assert( m_pMenuTauntSelection );
+
+ m_pMenuUpgradePanel = ( CHudUpgradePanel* )GET_HUDELEMENT( CHudUpgradePanel );
+
+#ifdef STAGING_ONLY
+ m_pMenuSpyBuild = ( CHudMenuSpyBuild * )GET_HUDELEMENT( CHudMenuSpyBuild );
+ Assert( m_pMenuSpyBuild );
+#endif // STAGING_ONLY
+
+ m_pMenuSpell = ( CHudSpellMenu * )GET_HUDELEMENT( CHudSpellMenu);
+ Assert( m_pMenuSpell );
+
+ m_pEurekaTeleportMenu = ( CHudEurekaEffectTeleportMenu * )GET_HUDELEMENT( CHudEurekaEffectTeleportMenu );
+ Assert( m_pEurekaTeleportMenu );
+
+ m_pTeamGoalTournament = (CHudTeamGoalTournament *)GET_HUDELEMENT( CHudTeamGoalTournament );
+ Assert( m_pTeamGoalTournament );
+
+ m_pInspectPanel = (CHudInspectPanel *)GET_HUDELEMENT( CHudInspectPanel );
+ Assert( m_pInspectPanel );
+
+ m_wasConnectedLastUpdate = false;
+
+ m_lastServerIP = 0;
+ m_lastServerPort = 0;
+ m_lastServerName = NULL;
+ m_lastServerConnectTime = 0;
+
+ m_flNextAllowedHighFiveHintTime = 0.0f;
+
+ m_bInfoPanelShown = false;
+ m_bRestrictInfoPanel = false;
+
+ CreateInterfaceFn gameUIFactory = g_GameUI.GetFactory();
+ if ( gameUIFactory )
+ {
+ m_pGameUI = (IGameUI *) gameUIFactory(GAMEUI_INTERFACE_VERSION, NULL );
+ if ( NULL != m_pGameUI )
+ {
+ // insert stats summary panel as the loading background dialog
+ CTFStatsSummaryPanel *pPanel = GStatsSummaryPanel();
+ pPanel->InvalidateLayout( false, true );
+ pPanel->SetVisible( false );
+ pPanel->MakePopup( false );
+ m_pGameUI->SetLoadingBackgroundDialog( pPanel->GetVPanel() );
+
+ IViewPortPanel *pMMOverride = ( gViewPortInterface->FindPanelByName( PANEL_MAINMENUOVERRIDE ) );
+ if ( pMMOverride )
+ {
+ ((CHudMainMenuOverride*)pMMOverride)->AttachToGameUI();
+ }
+ }
+ }
+
+#if defined( _X360 )
+ m_pScoreboard = (CTFClientScoreBoardDialog *)( gViewPortInterface->FindPanelByName( PANEL_SCOREBOARD ) );
+ Assert( m_pScoreboard );
+#endif
+
+ ListenForGameEvent( "localplayer_changeclass" );
+
+#ifdef TF_RAID_MODE
+ ListenForGameEvent( "raid_spawn_mob" );
+ ListenForGameEvent( "raid_spawn_squad" );
+#endif // TF_RAID_MODE
+
+ ListenForGameEvent( "player_upgraded" );
+ ListenForGameEvent( "player_buyback" );
+ ListenForGameEvent( "player_death" );
+ ListenForGameEvent( "player_used_powerup_bottle" );
+ MannVsMachineStats_Init();
+
+ ListenForGameEvent( "pve_win_panel" );
+
+ ListenForGameEvent( "arena_win_panel" );
+ ListenForGameEvent( "teamplay_win_panel" );
+ ListenForGameEvent( "server_spawn" );
+
+ ListenForGameEvent( "pumpkin_lord_summoned" );
+ ListenForGameEvent( "pumpkin_lord_killed" );
+ ListenForGameEvent( "eyeball_boss_summoned" );
+ ListenForGameEvent( "eyeball_boss_stunned" );
+ ListenForGameEvent( "eyeball_boss_killed" );
+ ListenForGameEvent( "eyeball_boss_killer" );
+ ListenForGameEvent( "eyeball_boss_escape_imminent" );
+ ListenForGameEvent( "eyeball_boss_escaped" );
+
+ ListenForGameEvent( "merasmus_summoned" );
+ ListenForGameEvent( "merasmus_killed" );
+ ListenForGameEvent( "merasmus_escape_warning" );
+ ListenForGameEvent( "merasmus_escaped" );
+
+ ListenForGameEvent( "player_highfive_start" );
+ ListenForGameEvent( "player_highfive_cancel" );
+ ListenForGameEvent( "player_highfive_success" );
+
+ ListenForGameEvent( "client_beginconnect" );
+
+ ListenForGameEvent( "player_teleported" );
+ ListenForGameEvent( "scorestats_accumulated_reset" );
+ ListenForGameEvent( "scorestats_accumulated_update" );
+
+ extern void Training_Init();
+ Training_Init();
+
+ BaseClass::Init();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ClientModeTFNormal::Shutdown()
+{
+ DestroyStatsSummaryPanel();
+}
+
+void ClientModeTFNormal::InitViewport()
+{
+ m_pViewport = new TFViewport();
+ m_pViewport->Start( gameuifuncs, gameeventmanager );
+}
+
+
+void ClientModeTFNormal::LevelInit( const char *newmap )
+{
+ BaseClass::LevelInit( newmap );
+
+ m_bInfoPanelShown = false;
+}
+
+IClientMode *GetClientModeNormal()
+{
+ static ClientModeTFNormal g_ClientModeNormal;
+
+ return &g_ClientModeNormal;
+}
+
+
+ClientModeTFNormal* GetClientModeTFNormal()
+{
+ Assert( dynamic_cast< ClientModeTFNormal* >( GetClientModeNormal() ) );
+
+ return static_cast< ClientModeTFNormal* >( GetClientModeNormal() );
+}
+
+extern ConVar v_viewmodel_fov;
+ConVar v_viewmodel_fov_demo( "viewmodel_fov_demo", "54", FCVAR_ARCHIVE );
+float ClientModeTFNormal::GetViewModelFOV( void )
+{
+ // If we're playing back a demo, we clamp the viewmodel fov
+ if ( engine->IsPlayingDemo() )
+ return v_viewmodel_fov_demo.GetFloat();
+
+ return v_viewmodel_fov.GetFloat();
+}
+
+extern ConVar r_drawviewmodel;
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool ClientModeTFNormal::ShouldDrawViewModel()
+{
+ C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pPlayer )
+ {
+ if ( pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) )
+ return false;
+ }
+
+ if ( !r_drawviewmodel.GetBool() )
+ return false;
+
+ return true;
+}
+
+ConVar tf_hud_no_crosshair_on_scope_zoom( "tf_hud_no_crosshair_on_scope_zoom", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
+
+bool ClientModeTFNormal::ShouldDrawCrosshair()
+{
+ C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( !pPlayer )
+ return false;
+
+ if ( pPlayer->GetPlayerClass() &&
+ pPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_SNIPER &&
+ pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) &&
+ tf_hud_no_crosshair_on_scope_zoom.GetBool() )
+ {
+ return false;
+ }
+
+ return ClientModeShared::ShouldDrawCrosshair();
+}
+
+
+//-----------------------------------------------------------------------------
+// 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 ClientModeTFNormal::ShouldBlackoutAroundHUD()
+{
+ C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( !pPlayer )
+ return true;
+
+ return ClientModeShared::ShouldBlackoutAroundHUD();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Allows the client mode to override mouse control stuff in sourcevr
+//-----------------------------------------------------------------------------
+HeadtrackMovementMode_t ClientModeTFNormal::ShouldOverrideHeadtrackControl()
+{
+ C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( !pPlayer )
+ return HMM_NOOVERRIDE;
+
+ // TODO: check if these actually all work right.
+ switch ( pPlayer->GetObserverMode() )
+ {
+ case OBS_MODE_DEATHCAM : // Turned into OBS_MODE_CHASE in VR
+ case OBS_MODE_ROAMING :
+ case OBS_MODE_FIXED : // Checked - works.
+ case OBS_MODE_CHASE : // Checked - works.
+ case OBS_MODE_POI : // PASSTIME NOT CHECKED
+ case OBS_MODE_FREEZECAM : // Turned into OBS_MODE_CHASE in VR
+ return HMM_SHOOTMOVEMOUSE_LOOKFACE;
+ case OBS_MODE_IN_EYE : // Checked - works.
+ return HMM_NOOVERRIDE;
+ }
+
+ return ClientModeShared::ShouldOverrideHeadtrackControl();
+}
+
+
+int ClientModeTFNormal::GetDeathMessageStartHeight( void )
+{
+ return m_pViewport->GetDeathMessageStartHeight();
+}
+
+void ClientModeTFNormal::FireGameEvent( IGameEvent *event )
+{
+ const char *eventname = event->GetName();
+
+ if ( !eventname || !eventname[0] )
+ return;
+
+ CBaseHudChat *pHUDChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat );
+ if ( FStrEq( "player_changename", eventname ) )
+ {
+ return; // server sends a colorized text string for this
+ }
+ else if ( FStrEq( "localplayer_changeclass", eventname ) )
+ {
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pLocalPlayer && pLocalPlayer->GetPlayerClass() )
+ {
+ int iClass = pLocalPlayer->GetPlayerClass()->GetClassIndex();
+
+ // have the player to exec a <class>.cfg file for the class they have selected
+ char szCmd[128];
+ Q_snprintf( szCmd, sizeof( szCmd ), "exec %s.cfg", GetPlayerClassData( iClass )->m_szClassName );
+ engine->ExecuteClientCmd( szCmd );
+ }
+ }
+
+#ifdef TF_RAID_MODE
+ else if ( FStrEq( "raid_spawn_mob", eventname ) )
+ {
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pLocalPlayer )
+ {
+ pLocalPlayer->EmitSound( "Raid.MobSpawn" );
+ }
+ }
+ else if ( FStrEq( "raid_spawn_squad", eventname ) )
+ {
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pLocalPlayer )
+ {
+ pLocalPlayer->EmitSound( "Raid.SquadSpawn" );
+ }
+ }
+#endif // TF_RAID_MODE
+
+ else if ( FStrEq( "player_connect_client", eventname ) || FStrEq( "player_disconnect", eventname ) )
+ {
+ // ignore these
+ if ( TFGameRules() && TFGameRules()->IsPVEModeActive() && event->GetInt( "bot" ) != 0 )
+ return;
+ }
+ else if ( FStrEq( "server_cvar", eventname ) )
+ {
+ if ( TFGameRules() && TFGameRules()->IsPVEModeActive() && !Q_strcmp( event->GetString("cvarname"), "tf_bot_count" ) )
+ return;
+ }
+ else if ( FStrEq( "player_buyback", eventname ) )
+ {
+ int idxPlayer = event->GetInt( "player" );
+ KeyValuesAD pKeyValues( "data" );
+ if ( g_TF_PR )
+ {
+ const char *pszString = tf_mvm_buybacks_method.GetBool() ? "#TF_PVE_Player_BuyBack_Fixed" : "#TF_PVE_Player_BuyBack";
+
+ pKeyValues->SetString( "player", g_TF_PR->GetPlayerName( idxPlayer ) );
+ pKeyValues->SetInt( "credits", event->GetInt( "cost", 0 ) );
+ PrintTextToChatPlayer( idxPlayer, pszString, pKeyValues );
+
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pLocalPlayer )
+ {
+ pLocalPlayer->EmitSound( "MVM.PlayerBoughtIn" );
+ }
+ }
+ }
+ else if ( FStrEq( "player_death", eventname ) )
+ {
+ // Make sure they're not doing a dead ringer fake death
+ if ( ( event->GetInt( "death_flags" ) & TF_DEATH_FEIGN_DEATH ) == 0 )
+ {
+ if ( TFGameRules() && ( TFGameRules()->State_Get() == GR_STATE_RND_RUNNING ) && ( TFGameRules()->IsMannVsMachineMode() || TFGameRules()->IsCompetitiveMode() ) )
+ {
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pLocalPlayer )
+ {
+ int nVictimIndex = event->GetInt( "victim_entindex" );
+ int nVictimTeam = g_TF_PR->GetTeam( nVictimIndex );
+
+ // See if there are any other players still alive
+ bool bSomeAlive = false;
+ for ( int playerIndex = 1; playerIndex <= MAX_PLAYERS; playerIndex++ )
+ {
+ if ( !g_TF_PR->IsConnected( playerIndex ) )
+ continue;
+
+ if ( nVictimIndex == playerIndex )
+ continue;
+
+ if ( nVictimTeam != g_TF_PR->GetTeam( playerIndex ) )
+ continue;
+
+ if ( g_TF_PR->IsAlive( playerIndex ) )
+ {
+ // Found one
+ bSomeAlive = true;
+ break;
+ }
+ }
+
+ if ( !bSomeAlive )
+ {
+ if ( TFGameRules()->IsMannVsMachineMode() )
+ {
+ if ( nVictimTeam == TF_TEAM_PVE_DEFENDERS )
+ {
+ // Inform the team that everyone is dead
+ pLocalPlayer->EmitSound( "Announcer.MVM_All_Dead" );
+ }
+ }
+ else
+ {
+ const char *pszSound = NULL;
+
+ if ( ( RandomInt( 1, 100 ) <= 20 ) || ( pLocalPlayer->GetTeamNumber() < FIRST_GAME_TEAM ) )
+ {
+ pszSound = ( nVictimTeam == TF_TEAM_RED ) ? "Announcer.TeamWipeRed" : "Announcer.TeamWipeBlu";
+ }
+ else if ( pLocalPlayer->GetTeamNumber() == nVictimTeam )
+ {
+ pszSound = "Announcer.YourTeamWiped";
+ }
+ else
+ {
+ pszSound = "Announcer.TheirTeamWiped";
+ }
+
+ if ( pszSound )
+ {
+ pLocalPlayer->EmitSound( pszSound );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if ( FStrEq( "player_used_powerup_bottle", eventname ) )
+ {
+ int idxPlayer = event->GetInt( "player" );
+ KeyValuesAD pKeyValues( "data" );
+ if ( g_TF_PR )
+ {
+ pKeyValues->SetString( "player", g_TF_PR->GetPlayerName( idxPlayer ) );
+ const char *pText = NULL;
+ switch ( event->GetInt( "type" ) )
+ {
+ case POWERUP_BOTTLE_CRITBOOST: pText = "#TF_PVE_Player_UsedCritsBottle"; break;
+ case POWERUP_BOTTLE_UBERCHARGE: pText = "#TF_PVE_Player_UsedUberBottle"; break;
+ case POWERUP_BOTTLE_RECALL: pText = "#TF_PVE_Player_UsedRecallBottle"; break;
+ case POWERUP_BOTTLE_REFILL_AMMO: pText = "#TF_PVE_Player_UsedRefillAmmoBottle"; break;
+ case POWERUP_BOTTLE_BUILDINGS_INSTANT_UPGRADE: pText = "#TF_PVE_Player_UsedBuildingUpgrade"; break;
+ case POWERUP_BOTTLE_RADIUS_STEALTH: pText = "#TF_PVE_Player_UsedRadiusStealth"; break;
+#ifdef STAGING_ONLY
+ case POWERUP_BOTTLE_SEE_CASH_THROUGH_WALL: pText = "#TF_PVE_Player_SeeCashThroughWall"; break;
+#endif
+ }
+ if ( pText != NULL )
+ {
+ PrintTextToChatPlayer( idxPlayer, pText, pKeyValues );
+ }
+
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pLocalPlayer )
+ {
+ pLocalPlayer->EmitSound( "MVM.PlayerUsedPowerup" );
+ }
+ }
+ }
+ else if (
+ FStrEq( "pve_win_panel", eventname ) ||
+ FStrEq( "arena_win_panel", eventname ) ||
+ FStrEq( "teamplay_win_panel", eventname ) )
+ {
+#if defined( REPLAY_ENABLED )
+ DisplayReplayReminder();
+#endif
+ }
+ else if ( FStrEq( "server_spawn", eventname ) )
+ {
+ uint32 newServerIP = 0;
+ int newServerPort = event->GetInt( "port" );
+
+ const char *pzAddress = event->GetString( "address" );
+ if ( pzAddress )
+ {
+ CUtlStringList IPs;
+ V_SplitString( pzAddress, ".", IPs );
+
+ if ( IPs.Count() == 4 )
+ {
+ byte ip[4];
+ for ( int i=0; i<IPs.Count() && i<4; ++i )
+ {
+ ip[i] = (byte) Q_atoi( IPs[i] );
+ }
+ newServerIP = (ip[0]<<24) + (ip[1]<<16) + (ip[2]<<8) + ip[3];
+ }
+ }
+
+ if ( m_lastServerConnectTime == 0 || newServerIP != m_lastServerIP || newServerPort != m_lastServerPort )
+ {
+ // we are connecting, or have connected to a different server
+ m_lastServerIP = newServerIP;
+ m_lastServerPort = newServerPort;
+
+ const char *hostname = event->GetString( "hostname" );
+ if ( hostname )
+ {
+ if ( m_lastServerName )
+ {
+ delete [] m_lastServerName;
+ m_lastServerName = NULL;
+ }
+
+ int hostnameLength = V_strlen( hostname )+1;
+
+ m_lastServerName = new char[ hostnameLength ];
+
+ V_strncpy( m_lastServerName, hostname, hostnameLength );
+ }
+
+ m_lastServerConnectTime = GetSteamWorksSGameStatsUploader().GetTimeSinceEpoch();
+ }
+
+ m_flNextAllowedHighFiveHintTime = 0.0f;
+
+ // Play Sound and flash window if joining a game from a lobby
+ CTFGSLobby *pLobby = GTFGCClientSystem()->GetLobby();
+ if ( pLobby )
+ {
+ engine->FlashWindow();
+
+ // If minimized, Blink and play noise
+ if ( engine->IsActiveApp() )
+ {
+ vgui::surface()->PlaySound( "ui/vote_started.wav" );
+ }
+ else
+ {
+ char fullpath[ 512 ];
+ g_pFullFileSystem->RelativePathToFullPath( "sound/ui/vote_started.wav", "GAME", fullpath, sizeof( fullpath ) );
+ PlayOutOfGameSound( fullpath );
+ }
+ }
+ }
+ else if ( FStrEq( "pumpkin_lord_summoned", eventname ) )
+ {
+ if ( !TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
+ {
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pLocalPlayer )
+ {
+ pLocalPlayer->EmitSound( "Halloween.HeadlessBossSpawn" );
+ }
+
+ CEconNotification *pNotification = new CEconNotification();
+ pNotification->SetText( "#TF_Halloween_Boss_Appeared" );
+ pNotification->SetLifetime( 5.0f );
+ pNotification->SetSoundFilename( "vo/null.mp3" );
+ NotificationQueue_Add( pNotification );
+ }
+ }
+ else if ( FStrEq( "pumpkin_lord_killed", eventname ) )
+ {
+ if ( !TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
+ {
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pLocalPlayer )
+ {
+ pLocalPlayer->EmitSound( "Halloween.HeadlessBossDeath" );
+ }
+
+ CEconNotification *pNotification = new CEconNotification();
+ pNotification->SetText( "#TF_Halloween_Boss_Killed" );
+ pNotification->SetLifetime( 5.0f );
+ pNotification->SetSoundFilename( "vo/null.mp3" );
+ NotificationQueue_Add( pNotification );
+ }
+ }
+ else if ( FStrEq( "eyeball_boss_summoned", eventname ) )
+ {
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pLocalPlayer )
+ {
+ pLocalPlayer->EmitSound( "Halloween.MonoculusBossSpawn" );
+ }
+
+ KeyValuesAD keyValues( "eyeball_boss" );
+ keyValues->SetColor( "custom_color", colorEyeballBossText );
+
+ CEconNotification *pNotification = new CEconNotification();
+
+ const int iLevel = event->GetInt( "level" );
+
+ if ( iLevel > 1 )
+ {
+ wchar_t *pszBaseString = g_pVGuiLocalize->Find( "TF_Halloween_Eyeball_Boss_LevelUp_Appeared" );
+ if ( pszBaseString )
+ {
+ KeyValuesAD pKeyValues( "data" );
+ pKeyValues->SetInt( "level", iLevel );
+
+ wchar_t wTemp[256];
+ g_pVGuiLocalize->ConstructString_safe( wTemp, pszBaseString, pKeyValues );
+
+ static char szAnsi[1024];
+ g_pVGuiLocalize->ConvertUnicodeToANSI( wTemp, szAnsi, sizeof(szAnsi) );
+
+ pNotification->SetText( szAnsi );
+ }
+ }
+ else
+ {
+ pNotification->SetText( "#TF_Halloween_Eyeball_Boss_Appeared" );
+ }
+
+ pNotification->SetLifetime( 5.0f );
+ pNotification->SetSoundFilename( "vo/null.mp3" );
+ pNotification->SetKeyValues( keyValues );
+ NotificationQueue_Add( pNotification );
+ }
+ else if ( FStrEq( "eyeball_boss_stunned", eventname ) )
+ {
+ int iPlayerIndex = event->GetInt( "player_entindex" );
+ if ( pHUDChat )
+ {
+ KeyValuesAD pKeyValues( "data" );
+ if ( g_TF_PR )
+ {
+ pKeyValues->SetString( "player", g_TF_PR->GetPlayerName( iPlayerIndex ) );
+
+ pHUDChat->SetCustomColor( colorEyeballBossText );
+
+ const int iLevel = event->GetInt( "level" );
+ if ( iLevel > 1 )
+ {
+ pKeyValues->SetInt( "level", iLevel );
+ PrintTextToChatPlayer( iPlayerIndex, "#TF_Halloween_Eyeball_Boss_LevelUp_Stun", pKeyValues );
+ }
+ else
+ {
+ PrintTextToChatPlayer( iPlayerIndex, "#TF_Halloween_Eyeball_Boss_Stun", pKeyValues );
+ }
+ }
+ }
+ }
+ else if ( FStrEq( "eyeball_boss_killed", eventname ) )
+ {
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pLocalPlayer )
+ {
+ pLocalPlayer->EmitSound( "Halloween.MonoculusBossDeath" );
+ }
+
+ KeyValuesAD keyValues( "eyeball_boss" );
+ keyValues->SetColor( "custom_color", colorEyeballBossText );
+
+ CEconNotification *pNotification = new CEconNotification();
+
+ const int iLevel = event->GetInt( "level" );
+
+ if ( iLevel > 1 )
+ {
+ wchar_t *pszBaseString = g_pVGuiLocalize->Find( "TF_Halloween_Eyeball_Boss_LevelUp_Killed" );
+ if ( pszBaseString )
+ {
+ KeyValuesAD pKeyValues( "data" );
+ pKeyValues->SetInt( "level", iLevel );
+
+ wchar_t wTemp[256];
+ g_pVGuiLocalize->ConstructString_safe( wTemp, pszBaseString, pKeyValues );
+
+ static char szAnsi[1024];
+ g_pVGuiLocalize->ConvertUnicodeToANSI( wTemp, szAnsi, sizeof(szAnsi) );
+
+ pNotification->SetText( szAnsi );
+ }
+ }
+ else
+ {
+ pNotification->SetText( "#TF_Halloween_Eyeball_Boss_Killed" );
+ }
+
+ pNotification->SetLifetime( 5.0f );
+ pNotification->SetSoundFilename( "vo/null.mp3" );
+ pNotification->SetKeyValues( keyValues );
+ NotificationQueue_Add( pNotification );
+ }
+ else if ( FStrEq( "eyeball_boss_killer", eventname ) )
+ {
+ int iPlayerIndex = event->GetInt( "player_entindex" );
+ if ( pHUDChat )
+ {
+ KeyValuesAD pKeyValues( "data" );
+ if ( g_TF_PR )
+ {
+ pKeyValues->SetString( "player", g_TF_PR->GetPlayerName( iPlayerIndex ) );
+
+ pHUDChat->SetCustomColor( colorEyeballBossText );
+
+ const int iLevel = event->GetInt( "level" );
+
+ if ( iLevel > 1 )
+ {
+ pKeyValues->SetInt( "level", iLevel );
+
+ PrintTextToChatPlayer( iPlayerIndex, "#TF_Halloween_Eyeball_Boss_LevelUp_Killers", pKeyValues );
+ }
+ else
+ {
+ PrintTextToChatPlayer( iPlayerIndex, "#TF_Halloween_Eyeball_Boss_Killers", pKeyValues );
+ }
+ }
+ }
+ }
+ else if ( FStrEq( "eyeball_boss_escape_imminent", eventname ) )
+ {
+ int nSecondsRemaining = event->GetInt( "time_remaining" );
+
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pLocalPlayer )
+ {
+ if ( nSecondsRemaining <= 10 )
+ pLocalPlayer->EmitSound( "Halloween.EyeballBossEscapeImminent" );
+ else
+ pLocalPlayer->EmitSound( "Halloween.EyeballBossEscapeSoon" );
+ }
+
+ if ( pHUDChat )
+ {
+ char szEyeballBossEscaping[128];
+ pHUDChat->SetCustomColor( colorEyeballBossText );
+
+ const int iLevel = event->GetInt( "level" );
+
+ if ( iLevel > 1 )
+ {
+ KeyValuesAD pKeyValues( "data" );
+ pKeyValues->SetInt( "level", iLevel );
+
+ V_sprintf_safe( szEyeballBossEscaping, "#TF_Halloween_Eyeball_Boss_LevelUp_Escaping_In_%i", nSecondsRemaining );
+
+ PrintTextToChat( szEyeballBossEscaping, pKeyValues );
+ }
+ else
+ {
+ V_sprintf_safe( szEyeballBossEscaping, "#TF_Halloween_Eyeball_Boss_Escaping_In_%i", nSecondsRemaining );
+
+ PrintTextToChat( szEyeballBossEscaping );
+ }
+ }
+ }
+ else if ( FStrEq( "eyeball_boss_escaped", eventname ) )
+ {
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pLocalPlayer )
+ {
+ pLocalPlayer->EmitSound( "Halloween.EyeballBossEscaped" );
+ }
+
+ KeyValuesAD keyValues( "eyeball_boss" );
+ keyValues->SetColor( "custom_color", colorEyeballBossText );
+
+ CEconNotification *pNotification = new CEconNotification();
+
+ const int iLevel = event->GetInt( "level" );
+
+ if ( iLevel > 1 )
+ {
+ wchar_t *pszBaseString = g_pVGuiLocalize->Find( "TF_Halloween_Eyeball_Boss_LevelUp_Escaped" );
+ if ( pszBaseString )
+ {
+ KeyValuesAD pKeyValues( "data" );
+ pKeyValues->SetInt( "level", iLevel );
+
+ wchar_t wTemp[256];
+ g_pVGuiLocalize->ConstructString_safe( wTemp, pszBaseString, pKeyValues );
+
+ static char szAnsi[1024];
+ g_pVGuiLocalize->ConvertUnicodeToANSI( wTemp, szAnsi, sizeof(szAnsi) );
+
+ pNotification->SetText( szAnsi );
+ }
+ }
+ else
+ {
+ pNotification->SetText( "#TF_Halloween_Eyeball_Boss_Escaped" );
+ }
+
+ pNotification->SetLifetime( 5.0f );
+ pNotification->SetSoundFilename( "vo/null.mp3" );
+ pNotification->SetKeyValues( keyValues );
+ NotificationQueue_Add( pNotification );
+ }
+ else if ( FStrEq( "merasmus_summoned", eventname ) )
+ {
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pLocalPlayer )
+ {
+ pLocalPlayer->EmitSound( "Halloween.MerasmusBossSpawn" );
+ }
+
+ KeyValuesAD keyValues( "merasmus" );
+ keyValues->SetColor( "custom_color", colorMerasmusText );
+
+ CEconNotification *pNotification = new CEconNotification();
+
+ const int iLevel = event->GetInt( "level" );
+
+ if ( iLevel > 1 )
+ {
+ wchar_t *pszBaseString = g_pVGuiLocalize->Find( "TF_Halloween_Merasmus_LevelUp_Appeared" );
+ if ( pszBaseString )
+ {
+ KeyValuesAD pKeyValues( "data" );
+ pKeyValues->SetInt( "level", iLevel );
+
+ wchar_t wTemp[256];
+ g_pVGuiLocalize->ConstructString_safe( wTemp, pszBaseString, pKeyValues );
+
+ static char szAnsi[1024];
+ g_pVGuiLocalize->ConvertUnicodeToANSI( wTemp, szAnsi, sizeof(szAnsi) );
+
+ pNotification->SetText( szAnsi );
+ }
+ }
+ else
+ {
+ pNotification->SetText( "#TF_Halloween_Merasmus_Appeared" );
+ }
+
+ pNotification->SetLifetime( 5.0f );
+ pNotification->SetSoundFilename( "vo/null.mp3" );
+ pNotification->SetKeyValues( keyValues );
+ NotificationQueue_Add( pNotification );
+ }
+ else if ( FStrEq( "merasmus_killed", eventname ) )
+ {
+ KeyValuesAD keyValues( "merasmus" );
+ keyValues->SetColor( "custom_color", colorMerasmusText );
+
+ CEconNotification *pNotification = new CEconNotification();
+
+ const int iLevel = event->GetInt( "level" );
+
+ if ( iLevel > 1 )
+ {
+ wchar_t *pszBaseString = g_pVGuiLocalize->Find( "TF_Halloween_Merasmus_LevelUp_Killed" );
+ if ( pszBaseString )
+ {
+ KeyValuesAD pKeyValues( "data" );
+ pKeyValues->SetInt( "level", iLevel );
+
+ wchar_t wTemp[256];
+ g_pVGuiLocalize->ConstructString_safe( wTemp, pszBaseString, pKeyValues );
+
+ static char szAnsi[1024];
+ g_pVGuiLocalize->ConvertUnicodeToANSI( wTemp, szAnsi, sizeof(szAnsi) );
+
+ pNotification->SetText( szAnsi );
+ }
+ }
+ else
+ {
+ pNotification->SetText( "#TF_Halloween_Merasmus_Killed" );
+ }
+
+ pNotification->SetLifetime( 5.0f );
+ pNotification->SetSoundFilename( "vo/null.mp3" );
+ pNotification->SetKeyValues( keyValues );
+ NotificationQueue_Add( pNotification );
+ }
+ else if ( FStrEq( "merasmus_escape_warning", eventname ) )
+ {
+ int nSecondsRemaining = event->GetInt( "time_remaining" );
+
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pLocalPlayer )
+ {
+ if ( nSecondsRemaining <= 10 )
+ pLocalPlayer->EmitSound( "Halloween.EyeballBossEscapeImminent" );
+ else
+ pLocalPlayer->EmitSound( "Halloween.EyeballBossEscapeSoon" );
+ }
+
+ if ( pHUDChat )
+ {
+ char szEyeballBossEscaping[128];
+ pHUDChat->SetCustomColor( colorMerasmusText );
+
+ const int iLevel = event->GetInt( "level" );
+
+ if ( iLevel > 1 )
+ {
+ KeyValuesAD pKeyValues( "data" );
+ pKeyValues->SetInt( "level", iLevel );
+
+ V_sprintf_safe( szEyeballBossEscaping, "#TF_Halloween_Merasmus_LevelUp_Escaping_In_%i", nSecondsRemaining );
+
+ PrintTextToChat( szEyeballBossEscaping, pKeyValues );
+ }
+ else
+ {
+ V_sprintf_safe( szEyeballBossEscaping, "#TF_Halloween_Merasmus_Escaping_In_%i", nSecondsRemaining );
+
+ PrintTextToChat( szEyeballBossEscaping );
+ }
+ }
+ }
+ else if ( FStrEq( "merasmus_escaped", eventname ) )
+ {
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pLocalPlayer )
+ {
+ pLocalPlayer->EmitSound( "Halloween.EyeballBossEscaped" );
+ }
+
+ KeyValuesAD keyValues( "merasmus" );
+ keyValues->SetColor( "custom_color", colorMerasmusText );
+
+ CEconNotification *pNotification = new CEconNotification();
+
+ const int iLevel = event->GetInt( "level" );
+
+ if ( iLevel > 1 )
+ {
+ wchar_t *pszBaseString = g_pVGuiLocalize->Find( "TF_Halloween_Merasmus_LevelUp_Escaped" );
+ if ( pszBaseString )
+ {
+ KeyValuesAD pKeyValues( "data" );
+ pKeyValues->SetInt( "level", iLevel );
+
+ wchar_t wTemp[256];
+ g_pVGuiLocalize->ConstructString_safe( wTemp, pszBaseString, pKeyValues );
+
+ static char szAnsi[1024];
+ g_pVGuiLocalize->ConvertUnicodeToANSI( wTemp, szAnsi, sizeof(szAnsi) );
+
+ pNotification->SetText( szAnsi );
+ }
+ }
+ else
+ {
+ pNotification->SetText( "#TF_Halloween_Merasmus_Escaped" );
+ }
+
+ pNotification->SetLifetime( 5.0f );
+ pNotification->SetSoundFilename( "vo/null.mp3" );
+ pNotification->SetKeyValues( keyValues );
+ NotificationQueue_Add( pNotification );
+ }
+ else if ( FStrEq( "player_highfive_start", eventname ) )
+ {
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+
+ // Don't show a hint if we're dead, we've already done it the maximum amount of times, or if it's too soon.
+ if ( pLocalPlayer && pLocalPlayer->IsAlive() &&
+ ( tf_taunt_always_show_hint.GetBool() ||
+ ( tf_highfive_hintcount.GetInt() < TF_HIGHFIVE_HINT_MAXHINTS && gpGlobals->curtime > m_flNextAllowedHighFiveHintTime ) ) )
+ {
+ int entindex = event->GetInt( "entindex" );
+ C_BasePlayer *pHighFiveInitiator = UTIL_PlayerByIndex( entindex );
+ if ( pHighFiveInitiator && pHighFiveInitiator != pLocalPlayer && ( pHighFiveInitiator->GetTeamNumber() == pLocalPlayer->GetTeamNumber() || tf_allow_all_team_partner_taunt.GetBool() ) )
+ {
+ // check that this player isn't too far away
+ Vector vecStart = pLocalPlayer->EyePosition();
+ Vector vecEnd = pHighFiveInitiator->EyePosition();
+ float flLength = (vecEnd - vecStart).Length();
+
+ if ( flLength < TF_HIGHFIVE_HINT_MAXDIST )
+ {
+ // check that we have line of sight to this player
+ trace_t tr;
+ UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, pLocalPlayer, COLLISION_GROUP_NONE, &tr );
+
+ if ( !tr.startsolid && tr.m_pEnt != NULL && tr.m_pEnt == pHighFiveInitiator )
+ {
+ IGameEvent *pEvent = gameeventmanager->CreateEvent( "show_annotation" );
+ if ( pEvent )
+ {
+ Vector location = pHighFiveInitiator->GetAbsOrigin();
+
+ pEvent->SetString( "text", "#TF_HighFive_Hint" );
+ pEvent->SetInt( "id", TF_HIGHFIVE_HINT_MASK | entindex );
+ pEvent->SetFloat( "worldPosX", location.x );
+ pEvent->SetFloat( "worldPosY", location.y );
+ pEvent->SetFloat( "worldPosZ", location.z + 48.0f );
+ pEvent->SetFloat( "lifetime", 10.0f );
+ pEvent->SetInt( "follow_entindex", entindex );
+
+ gameeventmanager->FireEventClientSide( pEvent );
+ tf_highfive_hintcount.SetValue( tf_highfive_hintcount.GetInt() + 1 );
+
+ m_flNextAllowedHighFiveHintTime = gpGlobals->curtime + TF_HIGHFIVE_HINT_MINTIMEBETWEEN;
+ }
+ }
+ }
+ }
+ }
+ }
+ else if ( FStrEq( "player_highfive_cancel", eventname ) )
+ {
+ int entindex = event->GetInt( "entindex" );
+ IGameEvent *pEvent = gameeventmanager->CreateEvent( "hide_annotation" );
+ if ( pEvent )
+ {
+ pEvent->SetInt( "id", TF_HIGHFIVE_HINT_MASK | entindex );
+ gameeventmanager->FireEventClientSide( pEvent );
+ }
+ }
+ else if ( FStrEq( "player_highfive_success", eventname ) )
+ {
+ int entindex = event->GetInt( "initiator_entindex" );
+ IGameEvent *pEvent = gameeventmanager->CreateEvent( "hide_annotation" );
+ if ( pEvent )
+ {
+ pEvent->SetInt( "id", TF_HIGHFIVE_HINT_MASK | entindex );
+ gameeventmanager->FireEventClientSide( pEvent );
+ }
+ }
+ else if ( FStrEq( "client_beginconnect", eventname ) )
+ {
+ const char *pchSource = event->GetString( "source" );
+ m_bRestrictInfoPanel = pchSource && ( FStrEq( "matchmaking", pchSource ) || !Q_strncmp( pchSource, "quickplay_", 10 ) );
+
+ m_bInfoPanelShown = false;
+ }
+ else if ( FStrEq( "player_teleported", eventname ) )
+ {
+ int iUserID = event->GetInt( "userid" );
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if( pLocalPlayer && pLocalPlayer->GetUserID() == iUserID && UseVR() )
+ {
+ g_ClientVirtualReality.AlignTorsoAndViewToWeapon();
+ }
+ }
+ else if ( FStrEq( "scorestats_accumulated_reset", eventname ) )
+ {
+ if ( g_TF_PR && TFGameRules() && !TFGameRules()->IsMannVsMachineMode() )
+ {
+ g_TF_PR->ResetPlayerScoreStats();
+ }
+ }
+ else if ( FStrEq( "scorestats_accumulated_update", eventname ) )
+ {
+ if ( g_TF_PR && TFGameRules() && !TFGameRules()->IsMannVsMachineMode() )
+ {
+ g_TF_PR->UpdatePlayerScoreStats();
+ }
+ }
+
+ BaseClass::FireGameEvent( event );
+}
+
+void ClientModeTFNormal::PostRenderVGui()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool ClientModeTFNormal::CreateMove( float flInputSampleTime, CUserCmd *cmd )
+{
+ return BaseClass::CreateMove( flInputSampleTime, cmd );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: See if hud elements want key input. Return 0 if the key is swallowed
+//-----------------------------------------------------------------------------
+int ClientModeTFNormal::HudElementKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding )
+{
+ // Let scoreboard handle input first because on X360 we need gamertags and
+ // gamercards accessible at all times when gamertag is visible.
+#if defined( _X360 )
+ if ( m_pScoreboard )
+ {
+ if ( !m_pScoreboard->HudElementKeyInput( down, keynum, pszCurrentBinding ) )
+ {
+ return 0;
+ }
+ }
+#endif
+
+ // Applies basic tags if we're going to take a screenshot
+ ScreenshotTaggingKeyInput( down, keynum, pszCurrentBinding );
+
+ if ( TrainingHandlesKeyInput( down, keynum, pszCurrentBinding ) )
+ {
+ return 0;
+ }
+
+ if ( CoachingHandlesKeyInput( down, keynum, pszCurrentBinding ) )
+ {
+ return 0;
+ }
+
+ if ( ItemTestHandlesKeyInput( down, keynum, pszCurrentBinding ) )
+ {
+ return 0;
+ }
+
+ if ( HalloweenHandlesKeyInput( down, keynum, pszCurrentBinding ) )
+ {
+ return 0;
+ }
+
+ if ( TauntHandlesKeyInput( down, keynum, pszCurrentBinding ) )
+ {
+ return 0;
+ }
+
+ // check for hud menus
+ if ( m_pMenuEngyBuild )
+ {
+ if ( !m_pMenuEngyBuild->HudElementKeyInput( down, keynum, pszCurrentBinding ) )
+ {
+ return 0;
+ }
+ }
+
+ if ( m_pMenuEngyDestroy )
+ {
+ if ( !m_pMenuEngyDestroy->HudElementKeyInput( down, keynum, pszCurrentBinding ) )
+ {
+ return 0;
+ }
+ }
+
+ if ( m_pMenuSpyDisguise )
+ {
+ if ( !m_pMenuSpyDisguise->HudElementKeyInput( down, keynum, pszCurrentBinding ) )
+ {
+ return 0;
+ }
+ }
+
+ if ( m_pFreezePanel )
+ {
+ m_pFreezePanel->HudElementKeyInput( down, keynum, pszCurrentBinding );
+ }
+
+ if ( m_pQuickSwitch )
+ {
+ if ( !m_pQuickSwitch->HudElementKeyInput( down, keynum, pszCurrentBinding ) )
+ {
+ return 0;
+ }
+ }
+
+ if ( m_pMenuTauntSelection )
+ {
+ if ( !m_pMenuTauntSelection->HudElementKeyInput( down, keynum, pszCurrentBinding ) )
+ {
+ return 0;
+ }
+ }
+
+#ifdef STAGING_ONLY
+ if ( m_pMenuSpyBuild )
+ {
+ if ( !m_pMenuSpyBuild->HudElementKeyInput( down, keynum, pszCurrentBinding ) )
+ {
+ return 0;
+ }
+ }
+#endif // STAGING_ONLY
+
+ if ( m_pEurekaTeleportMenu )
+ {
+ if ( !m_pEurekaTeleportMenu->HudElementKeyInput( down, keynum, pszCurrentBinding ) )
+ {
+ return 0;
+ }
+ }
+
+ if ( m_pInspectPanel )
+ {
+ if ( !m_pInspectPanel->HudElementKeyInput( down, keynum, pszCurrentBinding ) )
+ {
+ return 0;
+ }
+ }
+
+ if ( m_pTeamGoalTournament )
+ {
+ if ( !m_pTeamGoalTournament->HudElementKeyInput( down, keynum, pszCurrentBinding ) )
+ {
+ return 0;
+ }
+ }
+
+ if ( TournamentHudElementKeyInput( down, keynum, pszCurrentBinding ) == true )
+ return 0;
+
+ if ( ArenaClassLayoutKeyInput( down, keynum, pszCurrentBinding ) == true )
+ return 0;
+
+#ifndef _X360
+ if ( ShouldScoreBoardHandleKeyInput( down, keynum, pszCurrentBinding ) )
+ return 0;
+#endif // !360
+
+ return BaseClass::HudElementKeyInput( down, keynum, pszCurrentBinding );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: See if spectator input occurred. Return 0 if the key is swallowed.
+//-----------------------------------------------------------------------------
+int ClientModeTFNormal::HandleSpectatorKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding )
+{
+#if defined( _X360 )
+ // On X360 when we have scoreboard up in spectator menu we cannot
+ // steal any input because gamertags must be selectable and gamercards
+ // must be accessible.
+ // We cannot rely on any keybindings in this case since user could have
+ // remapped everything.
+ if ( m_pScoreboard && m_pScoreboard->IsVisible() )
+ {
+ return 1;
+ }
+#endif
+
+ // @note Tom Bui: Coaching, so override all input
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pLocalPlayer && pLocalPlayer->m_bIsCoaching )
+ {
+ if ( down && pszCurrentBinding && Q_strcmp( pszCurrentBinding, "+jump" ) == 0 )
+ {
+ engine->ClientCmd( "spec_mode" );
+ return 0;
+ }
+ return 1;
+ }
+ else
+ {
+ return BaseClass::HandleSpectatorKeyInput( down, keynum, pszCurrentBinding );
+ }
+}
+
+bool ClientModeTFNormal::DoPostScreenSpaceEffects( const CViewSetup *pSetup )
+{
+ if ( !BaseClass::DoPostScreenSpaceEffects( pSetup ) )
+ return false;
+
+ if( IsInFreezeCam() )
+ return false;
+
+ g_GlowObjectManager.RenderGlowEffects( pSetup, 0 );
+
+ return true;
+}
+
+#if !defined(NO_STEAM)
+void ClientModeTFNormal::OnScreenshotRequested( ScreenshotRequested_t *pParam )
+{
+ // Steam has requested a screenshot, act as if the key currently bound to screenshots
+ // has been pressed (we want tagging and the killcam screenshot behavior if applicable)
+ HudElementKeyInput( 0, BUTTON_CODE_INVALID, "screenshot" );
+ engine->ClientCmd( "screenshot" );
+}
+#endif
+
+
+
+//-----------------------------------------------------------------------------------------
+ConVar cl_ask_favorite_min_session_duration( "cl_ask_favorite_min_session_duration", "600", 0, "If player stays on a server for longer than this time (in seconds) prompt to add server to favorites" );
+ConVar cl_ask_favorite_opt_out( "cl_ask_favorite_opt_out", "0", FCVAR_ARCHIVE, "If nonzero, don't auto-ask to favorite servers" );
+ConVar cl_ask_favorite_for_any_server( "cl_ask_favorite_for_any_server", "0", 0, "If nonzero, auto-ask for local/LAN servers (for debugging)" );
+
+
+ConVar cl_ask_blacklist_max_session_duration( "cl_ask_blacklist_max_session_duration", "60", 0, "If player stays on a server for less than this time (in seconds) prompt to add server to blacklist" );
+ConVar cl_ask_blacklist_opt_out( "cl_ask_blacklist_opt_out", "0", FCVAR_ARCHIVE, "If nonzero, don't auto-ask to blacklist servers" );
+ConVar cl_ask_blacklist_for_any_server( "cl_ask_blacklist_for_any_server", "0", 0, "If nonzero, auto-ask for local/LAN servers (for debugging)" );
+
+
+//----------------------------------------------------------------------------
+void OnAskFavoriteDialogButtonPressed( bool bConfirm, void *pContext )
+{
+ if ( bConfirm )
+ {
+ // add last server to our favorites
+ steamapicontext->SteamMatchmaking()->AddFavoriteGame( steamapicontext->SteamUtils()->GetAppID(),
+ GetClientModeTFNormal()->GetLastConnectedServerIP(),
+ GetClientModeTFNormal()->GetLastConnectedServerPort(),
+ GetClientModeTFNormal()->GetLastConnectedServerPort(),
+ k_unFavoriteFlagFavorite,
+ CRTime::RTime32TimeCur() );
+
+ // Send it to the GC
+ GCSDK::CGCMsg< MsgGCServerBrowser_Server_t > msg( k_EMsgGCServerBrowser_FavoriteServer );
+ msg.Body().m_unIP = GetClientModeTFNormal()->GetLastConnectedServerIP();
+ msg.Body().m_usPort = GetClientModeTFNormal()->GetLastConnectedServerPort();
+ msg.Body().m_ubSource = k_EGCMsgServerBrowser_FromAutoAskDialog;
+ GCClientSystem()->BSendMessage( msg );
+ }
+}
+
+
+//----------------------------------------------------------------------------
+void OnAskBlacklistDialogButtonPressed( bool bConfirm, void *pContext )
+{
+ if ( bConfirm )
+ {
+ // add last server to our blacklist
+ CBlacklistedServerManager blackList;
+ blackList.LoadServersFromFile( BLACKLIST_DEFAULT_SAVE_FILE, false );
+
+ blackList.AddServer( GetClientModeTFNormal()->GetLastConnectedServerName(),
+ GetClientModeTFNormal()->GetLastConnectedServerIP(),
+ GetClientModeTFNormal()->GetLastConnectedServerPort() );
+
+ blackList.SaveToFile( BLACKLIST_DEFAULT_SAVE_FILE );
+
+ // Send it to the GC
+ GCSDK::CGCMsg< MsgGCServerBrowser_Server_t > msg( k_EMsgGCServerBrowser_BlacklistServer );
+ msg.Body().m_unIP = GetClientModeTFNormal()->GetLastConnectedServerIP();
+ msg.Body().m_usPort = GetClientModeTFNormal()->GetLastConnectedServerPort();
+ msg.Body().m_ubSource = k_EGCMsgServerBrowser_FromAutoAskDialog;
+ GCClientSystem()->BSendMessage( msg );
+ }
+}
+
+
+//----------------------------------------------------------------------------
+// If conditions are right, prompt the user to either favorite or blacklist
+// the last server they had connected to.
+void ClientModeTFNormal::AskFavoriteOrBlacklist() const
+{
+ // based on the time we spent on our last server, ask to favorite or blacklist it
+ int sessionDuration = GetSteamWorksSGameStatsUploader().GetTimeSinceEpoch() - m_lastServerConnectTime;
+
+ if ( sessionDuration > 0 )
+ {
+ // favorite?
+ if ( !cl_ask_favorite_opt_out.GetBool() && sessionDuration > cl_ask_favorite_min_session_duration.GetFloat() )
+ {
+ // don't ask to favorite reserved addresses
+ if ( !cl_ask_favorite_for_any_server.GetBool() )
+ {
+ netadr_t netAdr( GetLastConnectedServerIP(), GetLastConnectedServerPort() );
+
+ if ( !netAdr.IsValid() || netAdr.IsReservedAdr() )
+ return;
+
+ // Don't offer this for Valve servers, either
+ if ( GTFGCClientSystem()->BIsIPRecentMatchServer( netAdr ) )
+ {
+ return;
+ }
+ }
+
+ // is this server already a favorite?
+ for( int i=0; i<steamapicontext->SteamMatchmaking()->GetFavoriteGameCount(); ++i )
+ {
+ AppId_t appID = steamapicontext->SteamUtils()->GetAppID();
+ uint32 IP;
+ uint16 connPort;
+ uint16 queryPort;
+ uint32 flags;
+ uint32 time32LastPlayedOnServer;
+
+ if ( steamapicontext->SteamMatchmaking()->GetFavoriteGame( i, &appID, &IP, &connPort, &queryPort, &flags, &time32LastPlayedOnServer ) )
+ {
+ if ( ( flags & k_unFavoriteFlagFavorite ) == false )
+ {
+ // not a favorite
+ continue;
+ }
+
+ if ( ( appID == 0 || appID == steamapicontext->SteamUtils()->GetAppID() ) &&
+ IP == GetLastConnectedServerIP() &&
+ connPort == GetLastConnectedServerPort() )
+ {
+ // already have this server in our favorites - don't ask again
+ return;
+ }
+ }
+ }
+
+ // new potential favorite - ask
+ ShowConfirmOptOutDialog( "#TF_Serverlist_Ask_Favorite_Title", "#TF_Serverlist_Ask_Favorite_Text",
+ "#TF_Serverlist_Ask_Yes", "#TF_Serverlist_Ask_No",
+ "#TF_ServerList_Ask_Favorite_Opt_Out", "cl_ask_favorite_opt_out",
+ OnAskFavoriteDialogButtonPressed, NULL );
+ }
+
+ // blacklist?
+ if ( !cl_ask_blacklist_opt_out.GetBool() && sessionDuration < cl_ask_blacklist_max_session_duration.GetFloat() )
+ {
+ // is this server already blacklisted?
+ CBlacklistedServerManager blackList;
+ blackList.LoadServersFromFile( BLACKLIST_DEFAULT_SAVE_FILE, false );
+
+ netadr_t lastServer( GetLastConnectedServerIP(), (unsigned short)GetLastConnectedServerPort() );
+ if ( ( !blackList.CanServerBeBlacklisted( lastServer.GetIPHostByteOrder(), lastServer.GetPort(), GetLastConnectedServerName() )
+ || GTFGCClientSystem()->BIsIPRecentMatchServer( lastServer ) )
+ && !cl_ask_blacklist_for_any_server.GetBool() )
+ {
+ // don't bother - this server is not blacklistable
+ return;
+ }
+
+ if ( blackList.IsServerBlacklisted( GetLastConnectedServerIP(), GetLastConnectedServerPort(), GetLastConnectedServerName() ) )
+ {
+ // already marked as bad
+ return;
+ }
+
+ // new potential blacklist - ask
+ ShowConfirmOptOutDialog( "#TF_Serverlist_Ask_Blacklist_Title", "#TF_Serverlist_Ask_Blacklist_Text",
+ "#TF_Serverlist_Ask_Yes", "#TF_Serverlist_Ask_No",
+ "#TF_ServerList_Ask_Blacklist_Opt_Out", "cl_ask_blacklist_opt_out",
+ OnAskBlacklistDialogButtonPressed, NULL );
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+void ClientModeTFNormal::Update()
+{
+ BaseClass::Update();
+
+ TFModalStack()->Update();
+
+ NotificationQueue_Update();
+
+ // CHudVote *pHudVote = GET_HUDELEMENT( CHudVote );
+ // CTFHudMannVsMachineStatus *pMannVsMachineStatus = GET_HUDELEMENT( CTFHudMannVsMachineStatus );
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+
+ // Update steam controller stuff if one is active
+ if ( ::input->IsSteamControllerActive() )
+ {
+ // Walk through all the panels and HUD elements, see what kind of action set each one requests.
+ bool bNeedMenu = false;
+ bool bNeedHUD = false;
+ bool bNeedSpectator = false;
+ m_pViewport->ForEachPanel( [&] (IViewPortPanel* pPanel) {
+ if ( pPanel && pPanel->IsVisible() )
+ {
+ auto actionset = pPanel->GetPreferredActionSet();
+ if ( actionset == GAME_ACTION_SET_MENUCONTROLS )
+ {
+ bNeedMenu = true;
+ }
+ else if ( actionset == GAME_ACTION_SET_IN_GAME_HUD )
+ {
+ bNeedHUD = true;
+ }
+ else if ( actionset == GAME_ACTION_SET_SPECTATOR )
+ {
+ bNeedSpectator = true;
+ }
+ }
+ } );
+
+ gHUD.ForEachHudElement( [&]( CHudElement* pElement ) {
+ if ( pElement && pElement->IsActive() )
+ {
+ auto actionset = pElement->GetPreferredActionSet();
+ if ( actionset == GAME_ACTION_SET_MENUCONTROLS )
+ {
+ bNeedMenu = true;
+ }
+ else if ( actionset == GAME_ACTION_SET_IN_GAME_HUD )
+ {
+ bNeedHUD = true;
+ }
+ else if ( actionset == GAME_ACTION_SET_SPECTATOR )
+ {
+ bNeedSpectator = true;
+ }
+ }
+ } );
+
+ // Set the preferred action set. Requesting menu trumps hud, which trumps spectator, which trumps fps.
+ if ( !engine->IsInGame() || !engine->IsConnected() || enginevgui->IsGameUIVisible() || bNeedMenu )
+ {
+ ::input->SetPreferredGameActionSet( GAME_ACTION_SET_MENUCONTROLS );
+ }
+ else if ( bNeedHUD )
+ {
+ ::input->SetPreferredGameActionSet( GAME_ACTION_SET_IN_GAME_HUD );
+ }
+ else if ( bNeedSpectator || (pLocalPlayer && pLocalPlayer->GetTeamNumber() == TEAM_SPECTATOR) )
+ {
+ ::input->SetPreferredGameActionSet( GAME_ACTION_SET_SPECTATOR );
+ }
+ else
+ {
+ ::input->SetPreferredGameActionSet( GAME_ACTION_SET_FPSCONTROLS );
+ }
+
+ // Adjust look sensitivity down if the player is current using a zoomed-in weapon (typically sniper rifle).
+ float look_sensitivity = 0.125f;
+ if ( pLocalPlayer && pLocalPlayer->m_Shared.InCond( TF_COND_ZOOMED ) )
+ {
+ look_sensitivity = 0.035f;
+ }
+
+ // Set the flag for special action handling if we're taunting
+ if ( pLocalPlayer && pLocalPlayer->m_Shared.InCond( TF_COND_TAUNTING ) )
+ {
+ ::input->SetGameActionSetFlags( GAME_ACTION_SET_FLAGS_TAUNTING );
+ }
+ else
+ {
+ ::input->SetGameActionSetFlags( GAME_ACTION_SET_FLAGS_NONE );
+ }
+
+ sc_look_sensitivity_scale.SetValue( look_sensitivity );
+ }
+
+ if ( !engine->IsInGame() )
+ {
+ // @note Tom Bui: we want this thing to always run, so we get animations at the main menu
+ g_pClientMode->GetViewportAnimationController()->UpdateAnimations( gpGlobals->curtime );
+ }
+
+ if ( !engine->IsConnected() )
+ {
+ if ( m_wasConnectedLastUpdate )
+ {
+ // just disconnected from a game
+ m_wasConnectedLastUpdate = false;
+
+ AskFavoriteOrBlacklist();
+
+ m_lastServerConnectTime = 0;
+ }
+ }
+ else
+ {
+ m_wasConnectedLastUpdate = true;
+ }
+}
+
+
+//----------------------------------------------------------------------------
+void ClientModeTFNormal::ComputeVguiResConditions( KeyValues *pkvConditions )
+{
+ BaseClass::ComputeVguiResConditions( pkvConditions );
+}
+
+
+//----------------------------------------------------------------------------
+bool ClientModeTFNormal::IsInfoPanelAllowed()
+{
+ if ( !BaseClass::IsInfoPanelAllowed() )
+ return false;
+
+ return !m_bRestrictInfoPanel || !m_bInfoPanelShown;
+}
+
+//----------------------------------------------------------------------------
+bool ClientModeTFNormal::IsHTMLInfoPanelAllowed()
+{
+ if ( !BaseClass::IsHTMLInfoPanelAllowed() )
+ return false;
+
+ return !m_bRestrictInfoPanel;
+}
+
+
+//----------------------------------------------------------------------------
+void ClientModeTFNormal::InfoPanelDisplayed()
+{
+ BaseClass::InfoPanelDisplayed();
+
+ m_bInfoPanelShown = true;
+}
+
+//----------------------------------------------------------------------------
+bool ClientModeTFNormal::IsEngyBuildVisible() const
+{
+ return m_pMenuEngyBuild && m_pMenuEngyBuild->IsVisible();
+}
+
+//----------------------------------------------------------------------------
+bool ClientModeTFNormal::IsEngyDestroyVisible() const
+{
+ return m_pMenuEngyDestroy && m_pMenuEngyDestroy->IsVisible();
+}
+
+//----------------------------------------------------------------------------
+bool ClientModeTFNormal::IsEngyEurekaTeleportVisible() const
+{
+ return m_pEurekaTeleportMenu && m_pEurekaTeleportMenu->IsVisible();
+}
+
+//----------------------------------------------------------------------------
+bool ClientModeTFNormal::IsSpyDisguiseVisible() const
+{
+ return m_pMenuSpyDisguise && m_pMenuSpyDisguise->IsVisible();
+}
+
+//----------------------------------------------------------------------------
+bool ClientModeTFNormal::IsUpgradePanelVisible() const
+{
+ return m_pMenuUpgradePanel && m_pMenuUpgradePanel->IsVisible();
+}
+
+//----------------------------------------------------------------------------
+bool ClientModeTFNormal::IsTauntSelectPanelVisible() const
+{
+ return m_pMenuTauntSelection && m_pMenuTauntSelection->IsVisible();
+}
+
+//----------------------------------------------------------------------------
+void ClientModeTFNormal::PrintTextToChat( const char *pText, KeyValues *pKeyValues )
+{
+ CBaseHudChat *pHUDChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat );
+ if ( pHUDChat )
+ {
+ wchar_t wszText[1024]=L"";
+ g_pVGuiLocalize->ConstructString_safe( wszText, pText, pKeyValues );
+ char szAnsi[1024];
+ g_pVGuiLocalize->ConvertUnicodeToANSI( wszText, szAnsi, sizeof(szAnsi) );
+ pHUDChat->Printf( CHAT_FILTER_NONE, "%s", szAnsi );
+ }
+}
+
+void ClientModeTFNormal::PrintTextToChatPlayer( int iPlayerIndex, const char *pText, KeyValues *pKeyValues )
+{
+ CBaseHudChat *pHUDChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat );
+ if ( pHUDChat )
+ {
+ wchar_t wszText[1024]=L"";
+ g_pVGuiLocalize->ConstructString_safe( wszText, pText, pKeyValues );
+ char szAnsi[1024];
+ g_pVGuiLocalize->ConvertUnicodeToANSI( wszText, szAnsi, sizeof(szAnsi) );
+ pHUDChat->ChatPrintf( iPlayerIndex, CHAT_FILTER_NONE, "%s", szAnsi );
+ }
+}
+
+void ClientModeTFNormal::OnDemoRecordStart( char const* pDemoBaseName )
+{
+ BaseClass::OnDemoRecordStart( pDemoBaseName );
+}
+
+void ClientModeTFNormal::OnDemoRecordStop()
+{
+ IGameEvent *event = gameeventmanager->CreateEvent( "ds_stop" );
+ if ( event )
+ {
+ gameeventmanager->FireEventClientSide( event );
+ }
+
+ BaseClass::OnDemoRecordStop();
+}
+
+void __MsgFunc_PlayerBonusPoints( bf_read &msg )
+{
+ int nPoints = (int) msg.ReadByte();
+ int iPlayerEntIndex = (int) msg.ReadByte();
+ int iSourceEntIndex = (int) msg.ReadByte();
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "player_bonuspoints" );
+ if ( event )
+ {
+ event->SetInt( "points", nPoints );
+ event->SetInt( "player_entindex", iPlayerEntIndex );
+ event->SetInt( "source_entindex", iSourceEntIndex );
+ gameeventmanager->FireEventClientSide( event );
+ }
+}
+
+void __MsgFunc_PlayerGodRayEffect( bf_read &msg )
+{
+ int iPlayerEntIndex = (int)msg.ReadByte();
+
+ CBasePlayer *player = UTIL_PlayerByIndex( iPlayerEntIndex );
+ if ( player )
+ {
+ player->ParticleProp()->Create( "god_rays", PATTACH_ABSORIGIN_FOLLOW );
+ }
+}
+
+void __MsgFunc_PlayerTeleportHomeEffect( bf_read &msg )
+{
+ int iPlayerEntIndex = (int)msg.ReadByte();
+
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex( iPlayerEntIndex );
+ if ( pPlayer )
+ {
+ pPlayer->ParticleProp()->Create( "drg_wrenchmotron_teleport", PATTACH_ABSORIGIN );
+ }
+}
+
+void __MsgFunc_RDTeamPointsChanged( bf_read &msg )
+{
+ int nPoints = (int)msg.ReadShort();
+ int nTeam = (int)msg.ReadByte();
+ int nMethod = (int)msg.ReadByte();
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "rd_team_points_changed" );
+ if ( event )
+ {
+ event->SetInt( "points", nPoints );
+ event->SetInt( "team", nTeam );
+ event->SetInt( "method", nMethod );
+ gameeventmanager->FireEventClientSide( event );
+ }
+}
+
+void __MsgFunc_PlayerLoadoutUpdated( bf_read &msg )
+{
+ int iPlayerEntIndex = (int)msg.ReadByte();
+ C_TFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayerEntIndex ) );
+ if ( pPlayer )
+ {
+ CTFPlayerInventory *pInv = pPlayer->Inventory();
+ if ( pInv )
+ {
+ pInv->ClearClassLoadoutChangeTracking();
+ }
+ }
+}
+
+void __MsgFunc_PlayerTauntSoundLoopStart( bf_read &msg )
+{
+ int iPlayerEntIndex = (int)msg.ReadByte();
+ C_TFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayerEntIndex ) );
+ if ( pPlayer )
+ {
+ char szTauntSoundLoopName[256];
+ msg.ReadString( szTauntSoundLoopName, sizeof(szTauntSoundLoopName) );
+ pPlayer->PlayTauntSoundLoop( szTauntSoundLoopName );
+ }
+}
+
+void __MsgFunc_PlayerTauntSoundLoopEnd( bf_read &msg )
+{
+ int iPlayerEntIndex = (int)msg.ReadByte();
+ C_TFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayerEntIndex ) );
+ if ( pPlayer )
+ {
+ pPlayer->StopTauntSoundLoop();
+ }
+}
+
+
+void __MsgFunc_ForcePlayerViewAngles( bf_read &msg )
+{
+ // Read flag byte. Should be 1 right now.
+ int iFlags = (int)msg.ReadByte();
+ NOTE_UNUSED( iFlags );
+ Assert( iFlags == 0x01 );
+
+ int iPlayerEntIndex = (int)msg.ReadByte();
+ C_TFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayerEntIndex ) );
+ if ( pPlayer )
+ {
+ QAngle viewangles;
+ msg.ReadBitAngles( viewangles );
+
+ // Set the magic angles here!
+ pPlayer->SetLocalAngles( viewangles );
+ pPlayer->SetAbsAngles( viewangles );
+ pPlayer->SetTauntYaw( viewangles[YAW] );
+ pPlayer->m_Shared.SetVehicleMoveAngles( viewangles );
+ }
+}
+
+ConVar tf_halloween_bonus_ducks_cooldown( "tf_halloween_bonus_ducks_cooldown", "20", FCVAR_ARCHIVE );
+void __MsgFunc_BonusDucks( bf_read &msg )
+{
+ static float sflNextBonusDucks = 0.f;
+
+ int iPlayerEntIndex = (int)msg.ReadByte();
+ int iIgnoreTimer = (int)msg.ReadByte();
+
+ if ( Plat_FloatTime() < sflNextBonusDucks && !iIgnoreTimer )
+ return;
+
+ sflNextBonusDucks = Plat_FloatTime() + tf_halloween_bonus_ducks_cooldown.GetFloat();
+
+
+ C_TFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayerEntIndex ) );
+ if ( pPlayer )
+ {
+ pPlayer->EmitSound( "sf14.Merasmus.DuckHunt.BonusDucks" );
+ }
+}
+
+void __MsgFunc_PlayerPickupWeapon( bf_read &msg )
+{
+ IGameEvent *event = gameeventmanager->CreateEvent( "localplayer_pickup_weapon" );
+ gameeventmanager->FireEventClientSide( event );
+}
+
+void __MsgFunc_AutoBalanceVolunteer( bf_read &msg )
+{
+ KeyValuesAD pKeyValues( "data" );
+ pKeyValues->SetInt( "points", tf_autobalance_xp_bonus.GetInt() );
+
+ CAutobalanceVolunteerNotification *pNotification = new CAutobalanceVolunteerNotification();
+ pNotification->SetText( GTFGCClientSystem()->BConnectedToMatchServer( true ) ? "#TF_AutoBalanceVolunteerXPBonus" : "#TF_AutoBalanceVolunteer" );
+ pNotification->SetLifetime( tf_autobalance_query_lifetime.GetInt() );
+ pNotification->SetKeyValues( pKeyValues );
+ NotificationQueue_Add( pNotification );
+
+ if ( cl_notifications_show_ingame.GetInt() == 0 )
+ {
+ // player has turned off the in-game notifications so let's throw a chat
+ // message to see if they will check their notifications in the main menu
+ C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
+ CBaseHudChat *pHUDChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat );
+ if ( pPlayer && pHUDChat )
+ {
+ char szLocalized[100];
+ g_pVGuiLocalize->ConvertUnicodeToANSI(g_pVGuiLocalize->Find( "#TF_AutoBalanceVolunteer_ChatText" ), szLocalized, sizeof( szLocalized ) );
+ pHUDChat->ChatPrintf( pPlayer->entindex(), CHAT_FILTER_NONE, "%s ", szLocalized );
+ }
+ }
+}
+
+void __MsgFunc_AutoBalanceVolunteer_Cancel( bf_read &msg )
+{
+ NotificationQueue_Remove( &CAutobalanceVolunteerNotification::IsNotificationType );
+}
+
+void __MsgFunc_QuestObjectiveCompleted( bf_read &msg )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+
+ itemid_t itemID = (itemid_t)msg.ReadLongLong();
+ uint16 nStandardPoints = msg.ReadWord();
+ uint16 nBonusPoints = msg.ReadWord();
+ uint32 nObjectiveDefIndex = msg.ReadWord();
+
+ QuestObjectiveManager()->UpdateFromServer( itemID, nStandardPoints, nBonusPoints );
+ QuestObjectiveManager()->EnsureTrackersForPlayer( steamapicontext->SteamUser()->GetSteamID() );
+
+ // Passing a -1 means this is a ninja update where we don't want the fanfare
+ if ( nObjectiveDefIndex != (uint32)-1 )
+ {
+ IGameEvent *pEvent = gameeventmanager->CreateEvent( "quest_objective_completed" );
+ if ( pEvent )
+ {
+ pEvent->SetInt( "quest_item_id_low", itemID & 0xFFFFFFFF );
+ pEvent->SetInt( "quest_item_id_hi", itemID >> 32 );
+ pEvent->SetInt( "quest_objective_id", nObjectiveDefIndex );
+ gameeventmanager->FireEventClientSide( pEvent );
+ }
+ }
+}
+
+float PlaySoundEntry( const char* pszSoundEntryName )
+{
+ const char *pszSoundName = UTIL_GetRandomSoundFromEntry( pszSoundEntryName );
+ if ( pszSoundName && pszSoundName[0] )
+ {
+ vgui::surface()->PlaySound( pszSoundName );
+ return enginesound->GetSoundDuration( pszSoundName );
+ }
+
+ return 0.f;
+}
+
+#ifdef _WIN32
+
+#include "winlite.h"
+#pragma warning (push)
+#pragma warning(disable:4201) // nameless struct/union
+#include <mmsystem.h>
+#pragma warning (pop)
+#endif
+//
+void PlayOutOfGameSound( const char *pszSound )
+{
+#ifdef _WIN32
+ PlaySound( pszSound, NULL, SND_FILENAME | SND_ASYNC );
+#endif
+}