diff options
Diffstat (limited to 'game/client/tf/clientmode_tf.cpp')
| -rw-r--r-- | game/client/tf/clientmode_tf.cpp | 2301 |
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 +} |