Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2301 lines
68 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // The copyright to the contents herein is the property of Valve, L.L.C.
  4. // The contents may be used and/or copied only with the written permission of
  5. // Valve, L.L.C., or in accordance with the terms and conditions stipulated in
  6. // the agreement/contract under which the contents have been supplied.
  7. //
  8. // $Header: $
  9. // $NoKeywords: $
  10. //
  11. //=============================================================================
  12. #include "cbase.h"
  13. #include "hud.h"
  14. #include "clientmode_tf.h"
  15. #include "cdll_client_int.h"
  16. #include "iinput.h"
  17. #include "iviewrender.h"
  18. #include "vgui/ISurface.h"
  19. #include "vgui/IPanel.h"
  20. #include "GameUI/IGameUI.h"
  21. #include <vgui_controls/AnimationController.h>
  22. #include "ivmodemanager.h"
  23. #include "buymenu.h"
  24. #include "filesystem.h"
  25. #include "vgui/IVGui.h"
  26. #include "hud_chat.h"
  27. #include "view_shared.h"
  28. #include "view.h"
  29. #include "ivrenderview.h"
  30. #include "model_types.h"
  31. #include "iefx.h"
  32. #include "dlight.h"
  33. #include <imapoverview.h>
  34. #include "c_playerresource.h"
  35. #include <KeyValues.h>
  36. #include "text_message.h"
  37. #include "panelmetaclassmgr.h"
  38. #include "c_tf_player.h"
  39. #include "ienginevgui.h"
  40. #include "in_buttons.h"
  41. #include "voice_status.h"
  42. #include "tf_gamerules.h"
  43. #include "tf_hud_menu_engy_build.h"
  44. #include "tf_hud_menu_engy_destroy.h"
  45. #include "tf_hud_menu_spy_disguise.h"
  46. #include "tf_statsummary.h"
  47. #include "tf_hud_freezepanel.h"
  48. #include "item_quickswitch.h"
  49. #include "hud_macros.h"
  50. #include "vgui/ILocalize.h"
  51. #include "glow_outline_effect.h"
  52. #include "vgui/IInput.h"
  53. #include "tf_hud_mainmenuoverride.h"
  54. #include "tf_controls.h"
  55. #include "econ_notifications.h"
  56. #include "rtime.h"
  57. #include "econ_item_description.h"
  58. #include "c_tf_playerresource.h"
  59. #include "c_team.h"
  60. #include "tf_hud_menu_eureka_teleport.h"
  61. #include "tf_hud_menu_taunt_selection.h"
  62. #include "tf_hud_inspectpanel.h"
  63. #include "engine/IEngineSound.h"
  64. #ifdef STAGING_ONLY
  65. #include "tf_hud_menu_spy_build.h"
  66. #endif // STAGING_ONLY
  67. #include "quest_objective_manager.h"
  68. #include "econ_item_system.h"
  69. #include "tf_mann_vs_machine_stats.h"
  70. #include "tf_hud_mann_vs_machine_status.h"
  71. #include "player_vs_environment/c_tf_upgrades.h"
  72. #include "steam/isteamfriends.h"
  73. #include "steamworks_gamestats.h"
  74. #include "confirm_dialog.h"
  75. #include "ServerBrowser/blacklisted_server_manager.h"
  76. #include "tf_quickplay_shared.h"
  77. #include "sourcevr/isourcevirtualreality.h"
  78. #include "client_virtualreality.h"
  79. #include "econ_gcmessages.h"
  80. #if defined( _X360 )
  81. #include "tf_clientscoreboard.h"
  82. #endif
  83. #include "gc_clientsystem.h"
  84. #include "tf_gcmessages.h"
  85. #include "tf_gc_client.h"
  86. #include "tf_wardata.h"
  87. #include "debugoverlay_shared.h"
  88. #include "hud_vote.h"
  89. #include "c_tf_notification.h"
  90. // memdbgon must be the last include file in a .cpp file!!!
  91. #include "tier0/memdbgon.h"
  92. void __MsgFunc_AutoBalanceVolunteer( bf_read &msg );
  93. void __MsgFunc_AutoBalanceVolunteer_Cancel( bf_read &msg );
  94. void __MsgFunc_PlayerIgnitedInv( bf_read &msg );
  95. void __MsgFunc_PlayerIgnited( bf_read &msg );
  96. void __MsgFunc_Damage( bf_read &msg );
  97. void __MsgFunc_HudArenaNotify( bf_read &msg );
  98. void __MsgFunc_UpdateAchievement( bf_read &msg );
  99. void __MsgFunc_DamageDodged( bf_read &msg );
  100. void __MsgFunc_PlayerJarated( bf_read &msg );
  101. void __MsgFunc_PlayerExtinguished( bf_read &msg );
  102. void __MsgFunc_BreakModel( bf_read &msg );
  103. void __MsgFunc_BreakModel_Pumpkin( bf_read &msg );
  104. void __MsgFunc_BreakModelRocketDud( bf_read &msg );
  105. void __MsgFunc_CheapBreakModel( bf_read &msg );
  106. void __MsgFunc_PlayerJaratedFade( bf_read &msg );
  107. void __MsgFunc_PlayerShieldBlocked( bf_read &msg );
  108. void __MsgFunc_PlayerBonusPoints( bf_read &msg );
  109. void __MsgFunc_SpawnFlyingBird( bf_read &msg );
  110. void __MsgFunc_PlayerGodRayEffect( bf_read &msg );
  111. void __MsgFunc_PlayerTeleportHomeEffect( bf_read &msg );
  112. void __MsgFunc_RDTeamPointsChanged( bf_read &msg );
  113. void __MsgFunc_PlayerLoadoutUpdated( bf_read &msg );
  114. void __MsgFunc_PlayerTauntSoundLoopStart( bf_read &msg );
  115. void __MsgFunc_PlayerTauntSoundLoopEnd( bf_read &msg );
  116. void __MsgFunc_ForcePlayerViewAngles( bf_read &msg );
  117. void __MsgFunc_BonusDucks( bf_read &msg );
  118. void __MsgFunc_PlayerPickupWeapon( bf_read &msg );
  119. void __MsgFunc_QuestObjectiveCompleted( bf_read &msg );
  120. #if !defined(NO_STEAM)
  121. extern ConVar cl_steamscreenshots;
  122. #endif
  123. extern ISoundEmitterSystemBase *soundemitterbase;
  124. static Color colorEyeballBossText( 134, 80, 172, 255 );
  125. static Color colorMerasmusText( 112, 176, 74, 255 );
  126. ConVar default_fov( "default_fov", "75", FCVAR_CHEAT );
  127. ConVar fov_desired( "fov_desired", "75", FCVAR_ARCHIVE | FCVAR_USERINFO, "Sets the base field-of-view.", true, 20.0, true, MAX_FOV );
  128. #define TF_HIGHFIVE_HINT_MAXDIST 512.0f
  129. #define TF_HIGHFIVE_HINT_MAXHINTS 3
  130. #define TF_HIGHFIVE_HINT_MINTIMEBETWEEN 10.0f
  131. 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 );
  132. ConVar tf_taunt_always_show_hint( "tf_taunt_always_show_hint", "1", FCVAR_CLIENTDLL );
  133. extern ConVar tf_allow_all_team_partner_taunt;
  134. extern ConVar tf_mvm_buybacks_method;
  135. extern ConVar tf_autobalance_query_lifetime;
  136. extern ConVar tf_autobalance_xp_bonus;
  137. extern ConVar cl_notifications_show_ingame;
  138. extern ConVar sc_look_sensitivity_scale;
  139. extern bool TournamentHudElementKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding );
  140. extern bool ArenaClassLayoutKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding );
  141. extern bool CoachingHandlesKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding );
  142. extern bool ItemTestHandlesKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding );
  143. extern bool ShouldScoreBoardHandleKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding );
  144. static bool TrainingHandlesKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding )
  145. {
  146. if ( TFGameRules() != NULL && TFGameRules()->IsInTraining() && TFGameRules()->IsWaitingForTrainingContinue() )
  147. {
  148. if ( down && keynum == KEY_SPACE )
  149. {
  150. engine->ClientCmd_Unrestricted( "training_continue" );
  151. return true;
  152. }
  153. }
  154. return false;
  155. }
  156. static bool HalloweenHandlesKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding )
  157. {
  158. C_TFPlayer *pPlayer = ToTFPlayer( C_BasePlayer::GetLocalPlayer() );
  159. if ( pPlayer )
  160. {
  161. // don't do anything while dancing
  162. if ( pPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_THRILLER ) )
  163. {
  164. return true;
  165. }
  166. // only allow +attack
  167. if ( pPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
  168. {
  169. if ( pszCurrentBinding )
  170. {
  171. if ( FStrEq( pszCurrentBinding, "+attack" ) )
  172. {
  173. engine->ServerCmd( "boo" );
  174. return true;
  175. }
  176. else if ( FStrEq( pszCurrentBinding, "+attack2" ) || FStrEq( pszCurrentBinding, "+attack3" ) )
  177. {
  178. return true;
  179. }
  180. }
  181. }
  182. }
  183. return false;
  184. }
  185. static bool TauntHandlesKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding )
  186. {
  187. static const char *pszStopTauntKeys[] =
  188. {
  189. "+jump",
  190. "+taunt",
  191. "weapon_taunt",
  192. };
  193. C_TFPlayer *pPlayer = ToTFPlayer( C_BasePlayer::GetLocalPlayer() );
  194. if ( pPlayer )
  195. {
  196. if ( pPlayer->m_Shared.InCond( TF_COND_TAUNTING ) )
  197. {
  198. for ( int i=0; i<ARRAYSIZE( pszStopTauntKeys ); ++i )
  199. {
  200. if ( down && pszCurrentBinding && FStrEq( pszCurrentBinding, pszStopTauntKeys[i] ) )
  201. {
  202. // Halloween Hackery
  203. if ( i == 0 && pPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  204. {
  205. continue;
  206. }
  207. engine->ClientCmd( "stop_taunt" );
  208. return true;
  209. }
  210. }
  211. }
  212. }
  213. return false;
  214. }
  215. // Sets convars to tag the current mapname and the player in your crosshairs.
  216. // The player tagged will be overridden for killcam shots to be the killer
  217. static void ScreenshotTaggingKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding )
  218. {
  219. if ( pszCurrentBinding && ( FStrEq( pszCurrentBinding, "screenshot" ) || FStrEq( pszCurrentBinding, "jpeg" ) ) )
  220. {
  221. // Tag the player in the crosshairs
  222. C_TFPlayer *pPlayer = ToTFPlayer( C_BasePlayer::GetLocalPlayer() );
  223. if ( pPlayer )
  224. {
  225. C_TFPlayer *pCrosshairs = ToTFPlayer( UTIL_PlayerByIndex( pPlayer->GetIDTarget() ) );
  226. if ( pCrosshairs )
  227. {
  228. CSteamID steamID;
  229. if ( pCrosshairs->GetSteamID( &steamID ) )
  230. {
  231. ConVarRef cl_screenshotusertag( "cl_screenshotusertag" );
  232. if ( cl_screenshotusertag.IsValid() )
  233. {
  234. cl_screenshotusertag.SetValue( (int)steamID.GetAccountID() );
  235. }
  236. }
  237. }
  238. }
  239. // Tag the current map
  240. ConVarRef cl_screenshotlocation( "cl_screenshotlocation" );
  241. if ( cl_screenshotlocation.IsValid() )
  242. {
  243. char szMapName[MAX_MAP_NAME];
  244. Q_FileBase( engine->GetLevelName(), szMapName, sizeof(szMapName) );
  245. Q_strlower( szMapName );
  246. cl_screenshotlocation.SetValue( GetMapDisplayName( szMapName ) );
  247. }
  248. }
  249. }
  250. static void EnableSteamScreenshots( bool bEnable )
  251. {
  252. #if !defined(NO_STEAM)
  253. if ( steamapicontext && steamapicontext->SteamScreenshots() )
  254. {
  255. ConVarRef cl_savescreenshotstosteam( "cl_savescreenshotstosteam" );
  256. if ( cl_savescreenshotstosteam.IsValid() )
  257. {
  258. cl_savescreenshotstosteam.SetValue( bEnable );
  259. steamapicontext->SteamScreenshots()->HookScreenshots( bEnable );
  260. }
  261. }
  262. #endif
  263. }
  264. #if !defined(NO_STEAM)
  265. void SteamScreenshotsCallBack( IConVar *var, const char *pOldString, float flOldValue )
  266. {
  267. EnableSteamScreenshots( cl_steamscreenshots.GetBool() );
  268. }
  269. ConVar cl_steamscreenshots( "cl_steamscreenshots", "1", FCVAR_ARCHIVE, "Enable/disable saving screenshots to Steam", SteamScreenshotsCallBack );
  270. #endif
  271. void HUDMinModeChangedCallBack( IConVar *var, const char *pOldString, float flOldValue )
  272. {
  273. engine->ExecuteClientCmd( "hud_reloadscheme" );
  274. }
  275. ConVar cl_hud_minmode( "cl_hud_minmode", "0", FCVAR_ARCHIVE, "Set to 1 to turn on the advanced minimalist HUD mode.", HUDMinModeChangedCallBack );
  276. IClientMode *g_pClientMode = NULL;
  277. // --------------------------------------------------------------------------------- //
  278. // CTFModeManager.
  279. // --------------------------------------------------------------------------------- //
  280. class CTFModeManager : public IVModeManager
  281. {
  282. public:
  283. virtual void Init();
  284. virtual void SwitchMode( bool commander, bool force ) {}
  285. virtual void LevelInit( const char *newmap );
  286. virtual void LevelShutdown( void );
  287. virtual void ActivateMouse( bool isactive ) {}
  288. };
  289. static CTFModeManager g_ModeManager;
  290. IVModeManager *modemanager = ( IVModeManager * )&g_ModeManager;
  291. // --------------------------------------------------------------------------------- //
  292. // CTFModeManager implementation.
  293. // --------------------------------------------------------------------------------- //
  294. #define SCREEN_FILE "scripts/vgui_screens.txt"
  295. void CTFModeManager::Init()
  296. {
  297. g_pClientMode = GetClientModeNormal();
  298. PanelMetaClassMgr()->LoadMetaClassDefinitionFile( SCREEN_FILE );
  299. // Load the objects.txt file.
  300. LoadObjectInfos( ::filesystem );
  301. GetClientVoiceMgr()->SetHeadLabelOffset( 40 );
  302. EnableSteamScreenshots( true );
  303. }
  304. void CTFModeManager::LevelInit( const char *newmap )
  305. {
  306. g_pClientMode->LevelInit( newmap );
  307. ConVarRef voice_steal( "voice_steal" );
  308. if ( voice_steal.IsValid() )
  309. {
  310. voice_steal.SetValue( 1 );
  311. }
  312. }
  313. void CTFModeManager::LevelShutdown( void )
  314. {
  315. g_pClientMode->LevelShutdown();
  316. extern void CL_Training_LevelShutdown();
  317. extern void CL_Coaching_LevelShutdown();
  318. extern void CL_Consumables_LevelShutdown();
  319. extern void CL_Halloween_LevelShutdown();
  320. CL_Training_LevelShutdown();
  321. CL_Coaching_LevelShutdown();
  322. CL_Consumables_LevelShutdown();
  323. CL_Halloween_LevelShutdown();
  324. }
  325. //-----------------------------------------------------------------------------
  326. // Purpose:
  327. //-----------------------------------------------------------------------------
  328. ClientModeTFNormal::ClientModeTFNormal()
  329. {
  330. m_pMenuEngyBuild = NULL;
  331. m_pMenuEngyDestroy = NULL;
  332. m_pMenuSpyDisguise = NULL;
  333. m_pEurekaTeleportMenu = NULL;
  334. m_pMenuTauntSelection = NULL;
  335. #ifdef STAGING_ONLY
  336. m_pMenuSpyBuild = NULL;
  337. #endif // STAGING_ONLY
  338. m_pGameUI = NULL;
  339. m_pFreezePanel = NULL;
  340. m_pQuickSwitch = NULL;
  341. m_pInspectPanel = NULL;
  342. m_wasConnectedLastUpdate = false;
  343. m_lastServerIP = 0;
  344. m_lastServerPort = 0;
  345. m_lastServerName = NULL;
  346. m_lastServerConnectTime = 0;
  347. m_pTeamGoalTournament = NULL;
  348. #if defined( _X360 )
  349. m_pScoreboard = NULL;
  350. #endif
  351. HOOK_MESSAGE( AutoBalanceVolunteer );
  352. HOOK_MESSAGE( AutoBalanceVolunteer_Cancel );
  353. // Hook global message handlers
  354. HOOK_MESSAGE( PlayerIgnited );
  355. HOOK_MESSAGE( PlayerIgnitedInv );
  356. HOOK_MESSAGE( Damage );
  357. HOOK_MESSAGE( HudArenaNotify );
  358. HOOK_MESSAGE( UpdateAchievement );
  359. HOOK_MESSAGE( DamageDodged );
  360. HOOK_MESSAGE( PlayerJarated );
  361. HOOK_MESSAGE( PlayerExtinguished );
  362. HOOK_MESSAGE( BreakModel );
  363. HOOK_MESSAGE( CheapBreakModel );
  364. HOOK_MESSAGE( BreakModel_Pumpkin );
  365. HOOK_MESSAGE( BreakModelRocketDud );
  366. HOOK_MESSAGE( PlayerJaratedFade );
  367. HOOK_MESSAGE( PlayerShieldBlocked );
  368. HOOK_MESSAGE( PlayerBonusPoints );
  369. HOOK_MESSAGE( SpawnFlyingBird );
  370. HOOK_MESSAGE( PlayerGodRayEffect );
  371. HOOK_MESSAGE( PlayerTeleportHomeEffect );
  372. HOOK_MESSAGE( RDTeamPointsChanged );
  373. HOOK_MESSAGE( PlayerLoadoutUpdated );
  374. HOOK_MESSAGE( PlayerTauntSoundLoopStart );
  375. HOOK_MESSAGE( PlayerTauntSoundLoopEnd );
  376. HOOK_MESSAGE( ForcePlayerViewAngles );
  377. HOOK_MESSAGE( BonusDucks );
  378. HOOK_MESSAGE( PlayerPickupWeapon );
  379. HOOK_MESSAGE( QuestObjectiveCompleted );
  380. #if !defined(NO_STEAM)
  381. m_CallbackScreenshotRequested.Register( this, &ClientModeTFNormal::OnScreenshotRequested );
  382. #endif
  383. }
  384. //-----------------------------------------------------------------------------
  385. // Purpose: If you don't know what a destructor is by now, you are probably going to get fired
  386. //-----------------------------------------------------------------------------
  387. ClientModeTFNormal::~ClientModeTFNormal()
  388. {
  389. }
  390. // See interface.h/.cpp for specifics: basically this ensures that we actually Sys_UnloadModule the dll and that we don't call Sys_LoadModule
  391. // over and over again.
  392. static CDllDemandLoader g_GameUI( "GameUI" );
  393. //-----------------------------------------------------------------------------
  394. // Purpose:
  395. //-----------------------------------------------------------------------------
  396. void ClientModeTFNormal::Init()
  397. {
  398. m_pMenuEngyBuild = ( CHudMenuEngyBuild * )GET_HUDELEMENT( CHudMenuEngyBuild );
  399. Assert( m_pMenuEngyBuild );
  400. m_pMenuEngyDestroy = ( CHudMenuEngyDestroy * )GET_HUDELEMENT( CHudMenuEngyDestroy );
  401. Assert( m_pMenuEngyDestroy );
  402. m_pMenuSpyDisguise = ( CHudMenuSpyDisguise * )GET_HUDELEMENT( CHudMenuSpyDisguise );
  403. Assert( m_pMenuSpyDisguise );
  404. m_pFreezePanel = ( CTFFreezePanel * )GET_HUDELEMENT( CTFFreezePanel );
  405. Assert( m_pFreezePanel );
  406. m_pQuickSwitch = ( CItemQuickSwitchPanel * )GET_HUDELEMENT( CItemQuickSwitchPanel );
  407. Assert( m_pQuickSwitch );
  408. m_pMenuTauntSelection = ( CHudMenuTauntSelection * )GET_HUDELEMENT( CHudMenuTauntSelection );
  409. Assert( m_pMenuTauntSelection );
  410. m_pMenuUpgradePanel = ( CHudUpgradePanel* )GET_HUDELEMENT( CHudUpgradePanel );
  411. #ifdef STAGING_ONLY
  412. m_pMenuSpyBuild = ( CHudMenuSpyBuild * )GET_HUDELEMENT( CHudMenuSpyBuild );
  413. Assert( m_pMenuSpyBuild );
  414. #endif // STAGING_ONLY
  415. m_pMenuSpell = ( CHudSpellMenu * )GET_HUDELEMENT( CHudSpellMenu);
  416. Assert( m_pMenuSpell );
  417. m_pEurekaTeleportMenu = ( CHudEurekaEffectTeleportMenu * )GET_HUDELEMENT( CHudEurekaEffectTeleportMenu );
  418. Assert( m_pEurekaTeleportMenu );
  419. m_pTeamGoalTournament = (CHudTeamGoalTournament *)GET_HUDELEMENT( CHudTeamGoalTournament );
  420. Assert( m_pTeamGoalTournament );
  421. m_pInspectPanel = (CHudInspectPanel *)GET_HUDELEMENT( CHudInspectPanel );
  422. Assert( m_pInspectPanel );
  423. m_wasConnectedLastUpdate = false;
  424. m_lastServerIP = 0;
  425. m_lastServerPort = 0;
  426. m_lastServerName = NULL;
  427. m_lastServerConnectTime = 0;
  428. m_flNextAllowedHighFiveHintTime = 0.0f;
  429. m_bInfoPanelShown = false;
  430. m_bRestrictInfoPanel = false;
  431. CreateInterfaceFn gameUIFactory = g_GameUI.GetFactory();
  432. if ( gameUIFactory )
  433. {
  434. m_pGameUI = (IGameUI *) gameUIFactory(GAMEUI_INTERFACE_VERSION, NULL );
  435. if ( NULL != m_pGameUI )
  436. {
  437. // insert stats summary panel as the loading background dialog
  438. CTFStatsSummaryPanel *pPanel = GStatsSummaryPanel();
  439. pPanel->InvalidateLayout( false, true );
  440. pPanel->SetVisible( false );
  441. pPanel->MakePopup( false );
  442. m_pGameUI->SetLoadingBackgroundDialog( pPanel->GetVPanel() );
  443. IViewPortPanel *pMMOverride = ( gViewPortInterface->FindPanelByName( PANEL_MAINMENUOVERRIDE ) );
  444. if ( pMMOverride )
  445. {
  446. ((CHudMainMenuOverride*)pMMOverride)->AttachToGameUI();
  447. }
  448. }
  449. }
  450. #if defined( _X360 )
  451. m_pScoreboard = (CTFClientScoreBoardDialog *)( gViewPortInterface->FindPanelByName( PANEL_SCOREBOARD ) );
  452. Assert( m_pScoreboard );
  453. #endif
  454. ListenForGameEvent( "localplayer_changeclass" );
  455. #ifdef TF_RAID_MODE
  456. ListenForGameEvent( "raid_spawn_mob" );
  457. ListenForGameEvent( "raid_spawn_squad" );
  458. #endif // TF_RAID_MODE
  459. ListenForGameEvent( "player_upgraded" );
  460. ListenForGameEvent( "player_buyback" );
  461. ListenForGameEvent( "player_death" );
  462. ListenForGameEvent( "player_used_powerup_bottle" );
  463. MannVsMachineStats_Init();
  464. ListenForGameEvent( "pve_win_panel" );
  465. ListenForGameEvent( "arena_win_panel" );
  466. ListenForGameEvent( "teamplay_win_panel" );
  467. ListenForGameEvent( "server_spawn" );
  468. ListenForGameEvent( "pumpkin_lord_summoned" );
  469. ListenForGameEvent( "pumpkin_lord_killed" );
  470. ListenForGameEvent( "eyeball_boss_summoned" );
  471. ListenForGameEvent( "eyeball_boss_stunned" );
  472. ListenForGameEvent( "eyeball_boss_killed" );
  473. ListenForGameEvent( "eyeball_boss_killer" );
  474. ListenForGameEvent( "eyeball_boss_escape_imminent" );
  475. ListenForGameEvent( "eyeball_boss_escaped" );
  476. ListenForGameEvent( "merasmus_summoned" );
  477. ListenForGameEvent( "merasmus_killed" );
  478. ListenForGameEvent( "merasmus_escape_warning" );
  479. ListenForGameEvent( "merasmus_escaped" );
  480. ListenForGameEvent( "player_highfive_start" );
  481. ListenForGameEvent( "player_highfive_cancel" );
  482. ListenForGameEvent( "player_highfive_success" );
  483. ListenForGameEvent( "client_beginconnect" );
  484. ListenForGameEvent( "player_teleported" );
  485. ListenForGameEvent( "scorestats_accumulated_reset" );
  486. ListenForGameEvent( "scorestats_accumulated_update" );
  487. extern void Training_Init();
  488. Training_Init();
  489. BaseClass::Init();
  490. }
  491. //-----------------------------------------------------------------------------
  492. // Purpose:
  493. //-----------------------------------------------------------------------------
  494. void ClientModeTFNormal::Shutdown()
  495. {
  496. DestroyStatsSummaryPanel();
  497. }
  498. void ClientModeTFNormal::InitViewport()
  499. {
  500. m_pViewport = new TFViewport();
  501. m_pViewport->Start( gameuifuncs, gameeventmanager );
  502. }
  503. void ClientModeTFNormal::LevelInit( const char *newmap )
  504. {
  505. BaseClass::LevelInit( newmap );
  506. m_bInfoPanelShown = false;
  507. }
  508. IClientMode *GetClientModeNormal()
  509. {
  510. static ClientModeTFNormal g_ClientModeNormal;
  511. return &g_ClientModeNormal;
  512. }
  513. ClientModeTFNormal* GetClientModeTFNormal()
  514. {
  515. Assert( dynamic_cast< ClientModeTFNormal* >( GetClientModeNormal() ) );
  516. return static_cast< ClientModeTFNormal* >( GetClientModeNormal() );
  517. }
  518. extern ConVar v_viewmodel_fov;
  519. ConVar v_viewmodel_fov_demo( "viewmodel_fov_demo", "54", FCVAR_ARCHIVE );
  520. float ClientModeTFNormal::GetViewModelFOV( void )
  521. {
  522. // If we're playing back a demo, we clamp the viewmodel fov
  523. if ( engine->IsPlayingDemo() )
  524. return v_viewmodel_fov_demo.GetFloat();
  525. return v_viewmodel_fov.GetFloat();
  526. }
  527. extern ConVar r_drawviewmodel;
  528. //-----------------------------------------------------------------------------
  529. // Purpose:
  530. //-----------------------------------------------------------------------------
  531. bool ClientModeTFNormal::ShouldDrawViewModel()
  532. {
  533. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
  534. if ( pPlayer )
  535. {
  536. if ( pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) )
  537. return false;
  538. }
  539. if ( !r_drawviewmodel.GetBool() )
  540. return false;
  541. return true;
  542. }
  543. ConVar tf_hud_no_crosshair_on_scope_zoom( "tf_hud_no_crosshair_on_scope_zoom", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
  544. bool ClientModeTFNormal::ShouldDrawCrosshair()
  545. {
  546. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
  547. if ( !pPlayer )
  548. return false;
  549. if ( pPlayer->GetPlayerClass() &&
  550. pPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_SNIPER &&
  551. pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) &&
  552. tf_hud_no_crosshair_on_scope_zoom.GetBool() )
  553. {
  554. return false;
  555. }
  556. return ClientModeShared::ShouldDrawCrosshair();
  557. }
  558. //-----------------------------------------------------------------------------
  559. // Purpose: Returns true if VR mode should black out everything outside the HUD.
  560. // This is used for things like sniper scopes and full screen UI
  561. //-----------------------------------------------------------------------------
  562. bool ClientModeTFNormal::ShouldBlackoutAroundHUD()
  563. {
  564. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
  565. if ( !pPlayer )
  566. return true;
  567. return ClientModeShared::ShouldBlackoutAroundHUD();
  568. }
  569. //-----------------------------------------------------------------------------
  570. // Purpose: Allows the client mode to override mouse control stuff in sourcevr
  571. //-----------------------------------------------------------------------------
  572. HeadtrackMovementMode_t ClientModeTFNormal::ShouldOverrideHeadtrackControl()
  573. {
  574. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
  575. if ( !pPlayer )
  576. return HMM_NOOVERRIDE;
  577. // TODO: check if these actually all work right.
  578. switch ( pPlayer->GetObserverMode() )
  579. {
  580. case OBS_MODE_DEATHCAM : // Turned into OBS_MODE_CHASE in VR
  581. case OBS_MODE_ROAMING :
  582. case OBS_MODE_FIXED : // Checked - works.
  583. case OBS_MODE_CHASE : // Checked - works.
  584. case OBS_MODE_POI : // PASSTIME NOT CHECKED
  585. case OBS_MODE_FREEZECAM : // Turned into OBS_MODE_CHASE in VR
  586. return HMM_SHOOTMOVEMOUSE_LOOKFACE;
  587. case OBS_MODE_IN_EYE : // Checked - works.
  588. return HMM_NOOVERRIDE;
  589. }
  590. return ClientModeShared::ShouldOverrideHeadtrackControl();
  591. }
  592. int ClientModeTFNormal::GetDeathMessageStartHeight( void )
  593. {
  594. return m_pViewport->GetDeathMessageStartHeight();
  595. }
  596. void ClientModeTFNormal::FireGameEvent( IGameEvent *event )
  597. {
  598. const char *eventname = event->GetName();
  599. if ( !eventname || !eventname[0] )
  600. return;
  601. CBaseHudChat *pHUDChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat );
  602. if ( FStrEq( "player_changename", eventname ) )
  603. {
  604. return; // server sends a colorized text string for this
  605. }
  606. else if ( FStrEq( "localplayer_changeclass", eventname ) )
  607. {
  608. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  609. if ( pLocalPlayer && pLocalPlayer->GetPlayerClass() )
  610. {
  611. int iClass = pLocalPlayer->GetPlayerClass()->GetClassIndex();
  612. // have the player to exec a <class>.cfg file for the class they have selected
  613. char szCmd[128];
  614. Q_snprintf( szCmd, sizeof( szCmd ), "exec %s.cfg", GetPlayerClassData( iClass )->m_szClassName );
  615. engine->ExecuteClientCmd( szCmd );
  616. }
  617. }
  618. #ifdef TF_RAID_MODE
  619. else if ( FStrEq( "raid_spawn_mob", eventname ) )
  620. {
  621. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  622. if ( pLocalPlayer )
  623. {
  624. pLocalPlayer->EmitSound( "Raid.MobSpawn" );
  625. }
  626. }
  627. else if ( FStrEq( "raid_spawn_squad", eventname ) )
  628. {
  629. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  630. if ( pLocalPlayer )
  631. {
  632. pLocalPlayer->EmitSound( "Raid.SquadSpawn" );
  633. }
  634. }
  635. #endif // TF_RAID_MODE
  636. else if ( FStrEq( "player_connect_client", eventname ) || FStrEq( "player_disconnect", eventname ) )
  637. {
  638. // ignore these
  639. if ( TFGameRules() && TFGameRules()->IsPVEModeActive() && event->GetInt( "bot" ) != 0 )
  640. return;
  641. }
  642. else if ( FStrEq( "server_cvar", eventname ) )
  643. {
  644. if ( TFGameRules() && TFGameRules()->IsPVEModeActive() && !Q_strcmp( event->GetString("cvarname"), "tf_bot_count" ) )
  645. return;
  646. }
  647. else if ( FStrEq( "player_buyback", eventname ) )
  648. {
  649. int idxPlayer = event->GetInt( "player" );
  650. KeyValuesAD pKeyValues( "data" );
  651. if ( g_TF_PR )
  652. {
  653. const char *pszString = tf_mvm_buybacks_method.GetBool() ? "#TF_PVE_Player_BuyBack_Fixed" : "#TF_PVE_Player_BuyBack";
  654. pKeyValues->SetString( "player", g_TF_PR->GetPlayerName( idxPlayer ) );
  655. pKeyValues->SetInt( "credits", event->GetInt( "cost", 0 ) );
  656. PrintTextToChatPlayer( idxPlayer, pszString, pKeyValues );
  657. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  658. if ( pLocalPlayer )
  659. {
  660. pLocalPlayer->EmitSound( "MVM.PlayerBoughtIn" );
  661. }
  662. }
  663. }
  664. else if ( FStrEq( "player_death", eventname ) )
  665. {
  666. // Make sure they're not doing a dead ringer fake death
  667. if ( ( event->GetInt( "death_flags" ) & TF_DEATH_FEIGN_DEATH ) == 0 )
  668. {
  669. if ( TFGameRules() && ( TFGameRules()->State_Get() == GR_STATE_RND_RUNNING ) && ( TFGameRules()->IsMannVsMachineMode() || TFGameRules()->IsCompetitiveMode() ) )
  670. {
  671. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  672. if ( pLocalPlayer )
  673. {
  674. int nVictimIndex = event->GetInt( "victim_entindex" );
  675. int nVictimTeam = g_TF_PR->GetTeam( nVictimIndex );
  676. // See if there are any other players still alive
  677. bool bSomeAlive = false;
  678. for ( int playerIndex = 1; playerIndex <= MAX_PLAYERS; playerIndex++ )
  679. {
  680. if ( !g_TF_PR->IsConnected( playerIndex ) )
  681. continue;
  682. if ( nVictimIndex == playerIndex )
  683. continue;
  684. if ( nVictimTeam != g_TF_PR->GetTeam( playerIndex ) )
  685. continue;
  686. if ( g_TF_PR->IsAlive( playerIndex ) )
  687. {
  688. // Found one
  689. bSomeAlive = true;
  690. break;
  691. }
  692. }
  693. if ( !bSomeAlive )
  694. {
  695. if ( TFGameRules()->IsMannVsMachineMode() )
  696. {
  697. if ( nVictimTeam == TF_TEAM_PVE_DEFENDERS )
  698. {
  699. // Inform the team that everyone is dead
  700. pLocalPlayer->EmitSound( "Announcer.MVM_All_Dead" );
  701. }
  702. }
  703. else
  704. {
  705. const char *pszSound = NULL;
  706. if ( ( RandomInt( 1, 100 ) <= 20 ) || ( pLocalPlayer->GetTeamNumber() < FIRST_GAME_TEAM ) )
  707. {
  708. pszSound = ( nVictimTeam == TF_TEAM_RED ) ? "Announcer.TeamWipeRed" : "Announcer.TeamWipeBlu";
  709. }
  710. else if ( pLocalPlayer->GetTeamNumber() == nVictimTeam )
  711. {
  712. pszSound = "Announcer.YourTeamWiped";
  713. }
  714. else
  715. {
  716. pszSound = "Announcer.TheirTeamWiped";
  717. }
  718. if ( pszSound )
  719. {
  720. pLocalPlayer->EmitSound( pszSound );
  721. }
  722. }
  723. }
  724. }
  725. }
  726. }
  727. }
  728. else if ( FStrEq( "player_used_powerup_bottle", eventname ) )
  729. {
  730. int idxPlayer = event->GetInt( "player" );
  731. KeyValuesAD pKeyValues( "data" );
  732. if ( g_TF_PR )
  733. {
  734. pKeyValues->SetString( "player", g_TF_PR->GetPlayerName( idxPlayer ) );
  735. const char *pText = NULL;
  736. switch ( event->GetInt( "type" ) )
  737. {
  738. case POWERUP_BOTTLE_CRITBOOST: pText = "#TF_PVE_Player_UsedCritsBottle"; break;
  739. case POWERUP_BOTTLE_UBERCHARGE: pText = "#TF_PVE_Player_UsedUberBottle"; break;
  740. case POWERUP_BOTTLE_RECALL: pText = "#TF_PVE_Player_UsedRecallBottle"; break;
  741. case POWERUP_BOTTLE_REFILL_AMMO: pText = "#TF_PVE_Player_UsedRefillAmmoBottle"; break;
  742. case POWERUP_BOTTLE_BUILDINGS_INSTANT_UPGRADE: pText = "#TF_PVE_Player_UsedBuildingUpgrade"; break;
  743. case POWERUP_BOTTLE_RADIUS_STEALTH: pText = "#TF_PVE_Player_UsedRadiusStealth"; break;
  744. #ifdef STAGING_ONLY
  745. case POWERUP_BOTTLE_SEE_CASH_THROUGH_WALL: pText = "#TF_PVE_Player_SeeCashThroughWall"; break;
  746. #endif
  747. }
  748. if ( pText != NULL )
  749. {
  750. PrintTextToChatPlayer( idxPlayer, pText, pKeyValues );
  751. }
  752. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  753. if ( pLocalPlayer )
  754. {
  755. pLocalPlayer->EmitSound( "MVM.PlayerUsedPowerup" );
  756. }
  757. }
  758. }
  759. else if (
  760. FStrEq( "pve_win_panel", eventname ) ||
  761. FStrEq( "arena_win_panel", eventname ) ||
  762. FStrEq( "teamplay_win_panel", eventname ) )
  763. {
  764. #if defined( REPLAY_ENABLED )
  765. DisplayReplayReminder();
  766. #endif
  767. }
  768. else if ( FStrEq( "server_spawn", eventname ) )
  769. {
  770. uint32 newServerIP = 0;
  771. int newServerPort = event->GetInt( "port" );
  772. const char *pzAddress = event->GetString( "address" );
  773. if ( pzAddress )
  774. {
  775. CUtlStringList IPs;
  776. V_SplitString( pzAddress, ".", IPs );
  777. if ( IPs.Count() == 4 )
  778. {
  779. byte ip[4];
  780. for ( int i=0; i<IPs.Count() && i<4; ++i )
  781. {
  782. ip[i] = (byte) Q_atoi( IPs[i] );
  783. }
  784. newServerIP = (ip[0]<<24) + (ip[1]<<16) + (ip[2]<<8) + ip[3];
  785. }
  786. }
  787. if ( m_lastServerConnectTime == 0 || newServerIP != m_lastServerIP || newServerPort != m_lastServerPort )
  788. {
  789. // we are connecting, or have connected to a different server
  790. m_lastServerIP = newServerIP;
  791. m_lastServerPort = newServerPort;
  792. const char *hostname = event->GetString( "hostname" );
  793. if ( hostname )
  794. {
  795. if ( m_lastServerName )
  796. {
  797. delete [] m_lastServerName;
  798. m_lastServerName = NULL;
  799. }
  800. int hostnameLength = V_strlen( hostname )+1;
  801. m_lastServerName = new char[ hostnameLength ];
  802. V_strncpy( m_lastServerName, hostname, hostnameLength );
  803. }
  804. m_lastServerConnectTime = GetSteamWorksSGameStatsUploader().GetTimeSinceEpoch();
  805. }
  806. m_flNextAllowedHighFiveHintTime = 0.0f;
  807. // Play Sound and flash window if joining a game from a lobby
  808. CTFGSLobby *pLobby = GTFGCClientSystem()->GetLobby();
  809. if ( pLobby )
  810. {
  811. engine->FlashWindow();
  812. // If minimized, Blink and play noise
  813. if ( engine->IsActiveApp() )
  814. {
  815. vgui::surface()->PlaySound( "ui/vote_started.wav" );
  816. }
  817. else
  818. {
  819. char fullpath[ 512 ];
  820. g_pFullFileSystem->RelativePathToFullPath( "sound/ui/vote_started.wav", "GAME", fullpath, sizeof( fullpath ) );
  821. PlayOutOfGameSound( fullpath );
  822. }
  823. }
  824. }
  825. else if ( FStrEq( "pumpkin_lord_summoned", eventname ) )
  826. {
  827. if ( !TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
  828. {
  829. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  830. if ( pLocalPlayer )
  831. {
  832. pLocalPlayer->EmitSound( "Halloween.HeadlessBossSpawn" );
  833. }
  834. CEconNotification *pNotification = new CEconNotification();
  835. pNotification->SetText( "#TF_Halloween_Boss_Appeared" );
  836. pNotification->SetLifetime( 5.0f );
  837. pNotification->SetSoundFilename( "vo/null.mp3" );
  838. NotificationQueue_Add( pNotification );
  839. }
  840. }
  841. else if ( FStrEq( "pumpkin_lord_killed", eventname ) )
  842. {
  843. if ( !TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
  844. {
  845. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  846. if ( pLocalPlayer )
  847. {
  848. pLocalPlayer->EmitSound( "Halloween.HeadlessBossDeath" );
  849. }
  850. CEconNotification *pNotification = new CEconNotification();
  851. pNotification->SetText( "#TF_Halloween_Boss_Killed" );
  852. pNotification->SetLifetime( 5.0f );
  853. pNotification->SetSoundFilename( "vo/null.mp3" );
  854. NotificationQueue_Add( pNotification );
  855. }
  856. }
  857. else if ( FStrEq( "eyeball_boss_summoned", eventname ) )
  858. {
  859. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  860. if ( pLocalPlayer )
  861. {
  862. pLocalPlayer->EmitSound( "Halloween.MonoculusBossSpawn" );
  863. }
  864. KeyValuesAD keyValues( "eyeball_boss" );
  865. keyValues->SetColor( "custom_color", colorEyeballBossText );
  866. CEconNotification *pNotification = new CEconNotification();
  867. const int iLevel = event->GetInt( "level" );
  868. if ( iLevel > 1 )
  869. {
  870. wchar_t *pszBaseString = g_pVGuiLocalize->Find( "TF_Halloween_Eyeball_Boss_LevelUp_Appeared" );
  871. if ( pszBaseString )
  872. {
  873. KeyValuesAD pKeyValues( "data" );
  874. pKeyValues->SetInt( "level", iLevel );
  875. wchar_t wTemp[256];
  876. g_pVGuiLocalize->ConstructString_safe( wTemp, pszBaseString, pKeyValues );
  877. static char szAnsi[1024];
  878. g_pVGuiLocalize->ConvertUnicodeToANSI( wTemp, szAnsi, sizeof(szAnsi) );
  879. pNotification->SetText( szAnsi );
  880. }
  881. }
  882. else
  883. {
  884. pNotification->SetText( "#TF_Halloween_Eyeball_Boss_Appeared" );
  885. }
  886. pNotification->SetLifetime( 5.0f );
  887. pNotification->SetSoundFilename( "vo/null.mp3" );
  888. pNotification->SetKeyValues( keyValues );
  889. NotificationQueue_Add( pNotification );
  890. }
  891. else if ( FStrEq( "eyeball_boss_stunned", eventname ) )
  892. {
  893. int iPlayerIndex = event->GetInt( "player_entindex" );
  894. if ( pHUDChat )
  895. {
  896. KeyValuesAD pKeyValues( "data" );
  897. if ( g_TF_PR )
  898. {
  899. pKeyValues->SetString( "player", g_TF_PR->GetPlayerName( iPlayerIndex ) );
  900. pHUDChat->SetCustomColor( colorEyeballBossText );
  901. const int iLevel = event->GetInt( "level" );
  902. if ( iLevel > 1 )
  903. {
  904. pKeyValues->SetInt( "level", iLevel );
  905. PrintTextToChatPlayer( iPlayerIndex, "#TF_Halloween_Eyeball_Boss_LevelUp_Stun", pKeyValues );
  906. }
  907. else
  908. {
  909. PrintTextToChatPlayer( iPlayerIndex, "#TF_Halloween_Eyeball_Boss_Stun", pKeyValues );
  910. }
  911. }
  912. }
  913. }
  914. else if ( FStrEq( "eyeball_boss_killed", eventname ) )
  915. {
  916. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  917. if ( pLocalPlayer )
  918. {
  919. pLocalPlayer->EmitSound( "Halloween.MonoculusBossDeath" );
  920. }
  921. KeyValuesAD keyValues( "eyeball_boss" );
  922. keyValues->SetColor( "custom_color", colorEyeballBossText );
  923. CEconNotification *pNotification = new CEconNotification();
  924. const int iLevel = event->GetInt( "level" );
  925. if ( iLevel > 1 )
  926. {
  927. wchar_t *pszBaseString = g_pVGuiLocalize->Find( "TF_Halloween_Eyeball_Boss_LevelUp_Killed" );
  928. if ( pszBaseString )
  929. {
  930. KeyValuesAD pKeyValues( "data" );
  931. pKeyValues->SetInt( "level", iLevel );
  932. wchar_t wTemp[256];
  933. g_pVGuiLocalize->ConstructString_safe( wTemp, pszBaseString, pKeyValues );
  934. static char szAnsi[1024];
  935. g_pVGuiLocalize->ConvertUnicodeToANSI( wTemp, szAnsi, sizeof(szAnsi) );
  936. pNotification->SetText( szAnsi );
  937. }
  938. }
  939. else
  940. {
  941. pNotification->SetText( "#TF_Halloween_Eyeball_Boss_Killed" );
  942. }
  943. pNotification->SetLifetime( 5.0f );
  944. pNotification->SetSoundFilename( "vo/null.mp3" );
  945. pNotification->SetKeyValues( keyValues );
  946. NotificationQueue_Add( pNotification );
  947. }
  948. else if ( FStrEq( "eyeball_boss_killer", eventname ) )
  949. {
  950. int iPlayerIndex = event->GetInt( "player_entindex" );
  951. if ( pHUDChat )
  952. {
  953. KeyValuesAD pKeyValues( "data" );
  954. if ( g_TF_PR )
  955. {
  956. pKeyValues->SetString( "player", g_TF_PR->GetPlayerName( iPlayerIndex ) );
  957. pHUDChat->SetCustomColor( colorEyeballBossText );
  958. const int iLevel = event->GetInt( "level" );
  959. if ( iLevel > 1 )
  960. {
  961. pKeyValues->SetInt( "level", iLevel );
  962. PrintTextToChatPlayer( iPlayerIndex, "#TF_Halloween_Eyeball_Boss_LevelUp_Killers", pKeyValues );
  963. }
  964. else
  965. {
  966. PrintTextToChatPlayer( iPlayerIndex, "#TF_Halloween_Eyeball_Boss_Killers", pKeyValues );
  967. }
  968. }
  969. }
  970. }
  971. else if ( FStrEq( "eyeball_boss_escape_imminent", eventname ) )
  972. {
  973. int nSecondsRemaining = event->GetInt( "time_remaining" );
  974. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  975. if ( pLocalPlayer )
  976. {
  977. if ( nSecondsRemaining <= 10 )
  978. pLocalPlayer->EmitSound( "Halloween.EyeballBossEscapeImminent" );
  979. else
  980. pLocalPlayer->EmitSound( "Halloween.EyeballBossEscapeSoon" );
  981. }
  982. if ( pHUDChat )
  983. {
  984. char szEyeballBossEscaping[128];
  985. pHUDChat->SetCustomColor( colorEyeballBossText );
  986. const int iLevel = event->GetInt( "level" );
  987. if ( iLevel > 1 )
  988. {
  989. KeyValuesAD pKeyValues( "data" );
  990. pKeyValues->SetInt( "level", iLevel );
  991. V_sprintf_safe( szEyeballBossEscaping, "#TF_Halloween_Eyeball_Boss_LevelUp_Escaping_In_%i", nSecondsRemaining );
  992. PrintTextToChat( szEyeballBossEscaping, pKeyValues );
  993. }
  994. else
  995. {
  996. V_sprintf_safe( szEyeballBossEscaping, "#TF_Halloween_Eyeball_Boss_Escaping_In_%i", nSecondsRemaining );
  997. PrintTextToChat( szEyeballBossEscaping );
  998. }
  999. }
  1000. }
  1001. else if ( FStrEq( "eyeball_boss_escaped", eventname ) )
  1002. {
  1003. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  1004. if ( pLocalPlayer )
  1005. {
  1006. pLocalPlayer->EmitSound( "Halloween.EyeballBossEscaped" );
  1007. }
  1008. KeyValuesAD keyValues( "eyeball_boss" );
  1009. keyValues->SetColor( "custom_color", colorEyeballBossText );
  1010. CEconNotification *pNotification = new CEconNotification();
  1011. const int iLevel = event->GetInt( "level" );
  1012. if ( iLevel > 1 )
  1013. {
  1014. wchar_t *pszBaseString = g_pVGuiLocalize->Find( "TF_Halloween_Eyeball_Boss_LevelUp_Escaped" );
  1015. if ( pszBaseString )
  1016. {
  1017. KeyValuesAD pKeyValues( "data" );
  1018. pKeyValues->SetInt( "level", iLevel );
  1019. wchar_t wTemp[256];
  1020. g_pVGuiLocalize->ConstructString_safe( wTemp, pszBaseString, pKeyValues );
  1021. static char szAnsi[1024];
  1022. g_pVGuiLocalize->ConvertUnicodeToANSI( wTemp, szAnsi, sizeof(szAnsi) );
  1023. pNotification->SetText( szAnsi );
  1024. }
  1025. }
  1026. else
  1027. {
  1028. pNotification->SetText( "#TF_Halloween_Eyeball_Boss_Escaped" );
  1029. }
  1030. pNotification->SetLifetime( 5.0f );
  1031. pNotification->SetSoundFilename( "vo/null.mp3" );
  1032. pNotification->SetKeyValues( keyValues );
  1033. NotificationQueue_Add( pNotification );
  1034. }
  1035. else if ( FStrEq( "merasmus_summoned", eventname ) )
  1036. {
  1037. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  1038. if ( pLocalPlayer )
  1039. {
  1040. pLocalPlayer->EmitSound( "Halloween.MerasmusBossSpawn" );
  1041. }
  1042. KeyValuesAD keyValues( "merasmus" );
  1043. keyValues->SetColor( "custom_color", colorMerasmusText );
  1044. CEconNotification *pNotification = new CEconNotification();
  1045. const int iLevel = event->GetInt( "level" );
  1046. if ( iLevel > 1 )
  1047. {
  1048. wchar_t *pszBaseString = g_pVGuiLocalize->Find( "TF_Halloween_Merasmus_LevelUp_Appeared" );
  1049. if ( pszBaseString )
  1050. {
  1051. KeyValuesAD pKeyValues( "data" );
  1052. pKeyValues->SetInt( "level", iLevel );
  1053. wchar_t wTemp[256];
  1054. g_pVGuiLocalize->ConstructString_safe( wTemp, pszBaseString, pKeyValues );
  1055. static char szAnsi[1024];
  1056. g_pVGuiLocalize->ConvertUnicodeToANSI( wTemp, szAnsi, sizeof(szAnsi) );
  1057. pNotification->SetText( szAnsi );
  1058. }
  1059. }
  1060. else
  1061. {
  1062. pNotification->SetText( "#TF_Halloween_Merasmus_Appeared" );
  1063. }
  1064. pNotification->SetLifetime( 5.0f );
  1065. pNotification->SetSoundFilename( "vo/null.mp3" );
  1066. pNotification->SetKeyValues( keyValues );
  1067. NotificationQueue_Add( pNotification );
  1068. }
  1069. else if ( FStrEq( "merasmus_killed", eventname ) )
  1070. {
  1071. KeyValuesAD keyValues( "merasmus" );
  1072. keyValues->SetColor( "custom_color", colorMerasmusText );
  1073. CEconNotification *pNotification = new CEconNotification();
  1074. const int iLevel = event->GetInt( "level" );
  1075. if ( iLevel > 1 )
  1076. {
  1077. wchar_t *pszBaseString = g_pVGuiLocalize->Find( "TF_Halloween_Merasmus_LevelUp_Killed" );
  1078. if ( pszBaseString )
  1079. {
  1080. KeyValuesAD pKeyValues( "data" );
  1081. pKeyValues->SetInt( "level", iLevel );
  1082. wchar_t wTemp[256];
  1083. g_pVGuiLocalize->ConstructString_safe( wTemp, pszBaseString, pKeyValues );
  1084. static char szAnsi[1024];
  1085. g_pVGuiLocalize->ConvertUnicodeToANSI( wTemp, szAnsi, sizeof(szAnsi) );
  1086. pNotification->SetText( szAnsi );
  1087. }
  1088. }
  1089. else
  1090. {
  1091. pNotification->SetText( "#TF_Halloween_Merasmus_Killed" );
  1092. }
  1093. pNotification->SetLifetime( 5.0f );
  1094. pNotification->SetSoundFilename( "vo/null.mp3" );
  1095. pNotification->SetKeyValues( keyValues );
  1096. NotificationQueue_Add( pNotification );
  1097. }
  1098. else if ( FStrEq( "merasmus_escape_warning", eventname ) )
  1099. {
  1100. int nSecondsRemaining = event->GetInt( "time_remaining" );
  1101. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  1102. if ( pLocalPlayer )
  1103. {
  1104. if ( nSecondsRemaining <= 10 )
  1105. pLocalPlayer->EmitSound( "Halloween.EyeballBossEscapeImminent" );
  1106. else
  1107. pLocalPlayer->EmitSound( "Halloween.EyeballBossEscapeSoon" );
  1108. }
  1109. if ( pHUDChat )
  1110. {
  1111. char szEyeballBossEscaping[128];
  1112. pHUDChat->SetCustomColor( colorMerasmusText );
  1113. const int iLevel = event->GetInt( "level" );
  1114. if ( iLevel > 1 )
  1115. {
  1116. KeyValuesAD pKeyValues( "data" );
  1117. pKeyValues->SetInt( "level", iLevel );
  1118. V_sprintf_safe( szEyeballBossEscaping, "#TF_Halloween_Merasmus_LevelUp_Escaping_In_%i", nSecondsRemaining );
  1119. PrintTextToChat( szEyeballBossEscaping, pKeyValues );
  1120. }
  1121. else
  1122. {
  1123. V_sprintf_safe( szEyeballBossEscaping, "#TF_Halloween_Merasmus_Escaping_In_%i", nSecondsRemaining );
  1124. PrintTextToChat( szEyeballBossEscaping );
  1125. }
  1126. }
  1127. }
  1128. else if ( FStrEq( "merasmus_escaped", eventname ) )
  1129. {
  1130. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  1131. if ( pLocalPlayer )
  1132. {
  1133. pLocalPlayer->EmitSound( "Halloween.EyeballBossEscaped" );
  1134. }
  1135. KeyValuesAD keyValues( "merasmus" );
  1136. keyValues->SetColor( "custom_color", colorMerasmusText );
  1137. CEconNotification *pNotification = new CEconNotification();
  1138. const int iLevel = event->GetInt( "level" );
  1139. if ( iLevel > 1 )
  1140. {
  1141. wchar_t *pszBaseString = g_pVGuiLocalize->Find( "TF_Halloween_Merasmus_LevelUp_Escaped" );
  1142. if ( pszBaseString )
  1143. {
  1144. KeyValuesAD pKeyValues( "data" );
  1145. pKeyValues->SetInt( "level", iLevel );
  1146. wchar_t wTemp[256];
  1147. g_pVGuiLocalize->ConstructString_safe( wTemp, pszBaseString, pKeyValues );
  1148. static char szAnsi[1024];
  1149. g_pVGuiLocalize->ConvertUnicodeToANSI( wTemp, szAnsi, sizeof(szAnsi) );
  1150. pNotification->SetText( szAnsi );
  1151. }
  1152. }
  1153. else
  1154. {
  1155. pNotification->SetText( "#TF_Halloween_Merasmus_Escaped" );
  1156. }
  1157. pNotification->SetLifetime( 5.0f );
  1158. pNotification->SetSoundFilename( "vo/null.mp3" );
  1159. pNotification->SetKeyValues( keyValues );
  1160. NotificationQueue_Add( pNotification );
  1161. }
  1162. else if ( FStrEq( "player_highfive_start", eventname ) )
  1163. {
  1164. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  1165. // 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.
  1166. if ( pLocalPlayer && pLocalPlayer->IsAlive() &&
  1167. ( tf_taunt_always_show_hint.GetBool() ||
  1168. ( tf_highfive_hintcount.GetInt() < TF_HIGHFIVE_HINT_MAXHINTS && gpGlobals->curtime > m_flNextAllowedHighFiveHintTime ) ) )
  1169. {
  1170. int entindex = event->GetInt( "entindex" );
  1171. C_BasePlayer *pHighFiveInitiator = UTIL_PlayerByIndex( entindex );
  1172. if ( pHighFiveInitiator && pHighFiveInitiator != pLocalPlayer && ( pHighFiveInitiator->GetTeamNumber() == pLocalPlayer->GetTeamNumber() || tf_allow_all_team_partner_taunt.GetBool() ) )
  1173. {
  1174. // check that this player isn't too far away
  1175. Vector vecStart = pLocalPlayer->EyePosition();
  1176. Vector vecEnd = pHighFiveInitiator->EyePosition();
  1177. float flLength = (vecEnd - vecStart).Length();
  1178. if ( flLength < TF_HIGHFIVE_HINT_MAXDIST )
  1179. {
  1180. // check that we have line of sight to this player
  1181. trace_t tr;
  1182. UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, pLocalPlayer, COLLISION_GROUP_NONE, &tr );
  1183. if ( !tr.startsolid && tr.m_pEnt != NULL && tr.m_pEnt == pHighFiveInitiator )
  1184. {
  1185. IGameEvent *pEvent = gameeventmanager->CreateEvent( "show_annotation" );
  1186. if ( pEvent )
  1187. {
  1188. Vector location = pHighFiveInitiator->GetAbsOrigin();
  1189. pEvent->SetString( "text", "#TF_HighFive_Hint" );
  1190. pEvent->SetInt( "id", TF_HIGHFIVE_HINT_MASK | entindex );
  1191. pEvent->SetFloat( "worldPosX", location.x );
  1192. pEvent->SetFloat( "worldPosY", location.y );
  1193. pEvent->SetFloat( "worldPosZ", location.z + 48.0f );
  1194. pEvent->SetFloat( "lifetime", 10.0f );
  1195. pEvent->SetInt( "follow_entindex", entindex );
  1196. gameeventmanager->FireEventClientSide( pEvent );
  1197. tf_highfive_hintcount.SetValue( tf_highfive_hintcount.GetInt() + 1 );
  1198. m_flNextAllowedHighFiveHintTime = gpGlobals->curtime + TF_HIGHFIVE_HINT_MINTIMEBETWEEN;
  1199. }
  1200. }
  1201. }
  1202. }
  1203. }
  1204. }
  1205. else if ( FStrEq( "player_highfive_cancel", eventname ) )
  1206. {
  1207. int entindex = event->GetInt( "entindex" );
  1208. IGameEvent *pEvent = gameeventmanager->CreateEvent( "hide_annotation" );
  1209. if ( pEvent )
  1210. {
  1211. pEvent->SetInt( "id", TF_HIGHFIVE_HINT_MASK | entindex );
  1212. gameeventmanager->FireEventClientSide( pEvent );
  1213. }
  1214. }
  1215. else if ( FStrEq( "player_highfive_success", eventname ) )
  1216. {
  1217. int entindex = event->GetInt( "initiator_entindex" );
  1218. IGameEvent *pEvent = gameeventmanager->CreateEvent( "hide_annotation" );
  1219. if ( pEvent )
  1220. {
  1221. pEvent->SetInt( "id", TF_HIGHFIVE_HINT_MASK | entindex );
  1222. gameeventmanager->FireEventClientSide( pEvent );
  1223. }
  1224. }
  1225. else if ( FStrEq( "client_beginconnect", eventname ) )
  1226. {
  1227. const char *pchSource = event->GetString( "source" );
  1228. m_bRestrictInfoPanel = pchSource && ( FStrEq( "matchmaking", pchSource ) || !Q_strncmp( pchSource, "quickplay_", 10 ) );
  1229. m_bInfoPanelShown = false;
  1230. }
  1231. else if ( FStrEq( "player_teleported", eventname ) )
  1232. {
  1233. int iUserID = event->GetInt( "userid" );
  1234. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  1235. if( pLocalPlayer && pLocalPlayer->GetUserID() == iUserID && UseVR() )
  1236. {
  1237. g_ClientVirtualReality.AlignTorsoAndViewToWeapon();
  1238. }
  1239. }
  1240. else if ( FStrEq( "scorestats_accumulated_reset", eventname ) )
  1241. {
  1242. if ( g_TF_PR && TFGameRules() && !TFGameRules()->IsMannVsMachineMode() )
  1243. {
  1244. g_TF_PR->ResetPlayerScoreStats();
  1245. }
  1246. }
  1247. else if ( FStrEq( "scorestats_accumulated_update", eventname ) )
  1248. {
  1249. if ( g_TF_PR && TFGameRules() && !TFGameRules()->IsMannVsMachineMode() )
  1250. {
  1251. g_TF_PR->UpdatePlayerScoreStats();
  1252. }
  1253. }
  1254. BaseClass::FireGameEvent( event );
  1255. }
  1256. void ClientModeTFNormal::PostRenderVGui()
  1257. {
  1258. }
  1259. //-----------------------------------------------------------------------------
  1260. // Purpose:
  1261. //-----------------------------------------------------------------------------
  1262. bool ClientModeTFNormal::CreateMove( float flInputSampleTime, CUserCmd *cmd )
  1263. {
  1264. return BaseClass::CreateMove( flInputSampleTime, cmd );
  1265. }
  1266. //-----------------------------------------------------------------------------
  1267. // Purpose: See if hud elements want key input. Return 0 if the key is swallowed
  1268. //-----------------------------------------------------------------------------
  1269. int ClientModeTFNormal::HudElementKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding )
  1270. {
  1271. // Let scoreboard handle input first because on X360 we need gamertags and
  1272. // gamercards accessible at all times when gamertag is visible.
  1273. #if defined( _X360 )
  1274. if ( m_pScoreboard )
  1275. {
  1276. if ( !m_pScoreboard->HudElementKeyInput( down, keynum, pszCurrentBinding ) )
  1277. {
  1278. return 0;
  1279. }
  1280. }
  1281. #endif
  1282. // Applies basic tags if we're going to take a screenshot
  1283. ScreenshotTaggingKeyInput( down, keynum, pszCurrentBinding );
  1284. if ( TrainingHandlesKeyInput( down, keynum, pszCurrentBinding ) )
  1285. {
  1286. return 0;
  1287. }
  1288. if ( CoachingHandlesKeyInput( down, keynum, pszCurrentBinding ) )
  1289. {
  1290. return 0;
  1291. }
  1292. if ( ItemTestHandlesKeyInput( down, keynum, pszCurrentBinding ) )
  1293. {
  1294. return 0;
  1295. }
  1296. if ( HalloweenHandlesKeyInput( down, keynum, pszCurrentBinding ) )
  1297. {
  1298. return 0;
  1299. }
  1300. if ( TauntHandlesKeyInput( down, keynum, pszCurrentBinding ) )
  1301. {
  1302. return 0;
  1303. }
  1304. // check for hud menus
  1305. if ( m_pMenuEngyBuild )
  1306. {
  1307. if ( !m_pMenuEngyBuild->HudElementKeyInput( down, keynum, pszCurrentBinding ) )
  1308. {
  1309. return 0;
  1310. }
  1311. }
  1312. if ( m_pMenuEngyDestroy )
  1313. {
  1314. if ( !m_pMenuEngyDestroy->HudElementKeyInput( down, keynum, pszCurrentBinding ) )
  1315. {
  1316. return 0;
  1317. }
  1318. }
  1319. if ( m_pMenuSpyDisguise )
  1320. {
  1321. if ( !m_pMenuSpyDisguise->HudElementKeyInput( down, keynum, pszCurrentBinding ) )
  1322. {
  1323. return 0;
  1324. }
  1325. }
  1326. if ( m_pFreezePanel )
  1327. {
  1328. m_pFreezePanel->HudElementKeyInput( down, keynum, pszCurrentBinding );
  1329. }
  1330. if ( m_pQuickSwitch )
  1331. {
  1332. if ( !m_pQuickSwitch->HudElementKeyInput( down, keynum, pszCurrentBinding ) )
  1333. {
  1334. return 0;
  1335. }
  1336. }
  1337. if ( m_pMenuTauntSelection )
  1338. {
  1339. if ( !m_pMenuTauntSelection->HudElementKeyInput( down, keynum, pszCurrentBinding ) )
  1340. {
  1341. return 0;
  1342. }
  1343. }
  1344. #ifdef STAGING_ONLY
  1345. if ( m_pMenuSpyBuild )
  1346. {
  1347. if ( !m_pMenuSpyBuild->HudElementKeyInput( down, keynum, pszCurrentBinding ) )
  1348. {
  1349. return 0;
  1350. }
  1351. }
  1352. #endif // STAGING_ONLY
  1353. if ( m_pEurekaTeleportMenu )
  1354. {
  1355. if ( !m_pEurekaTeleportMenu->HudElementKeyInput( down, keynum, pszCurrentBinding ) )
  1356. {
  1357. return 0;
  1358. }
  1359. }
  1360. if ( m_pInspectPanel )
  1361. {
  1362. if ( !m_pInspectPanel->HudElementKeyInput( down, keynum, pszCurrentBinding ) )
  1363. {
  1364. return 0;
  1365. }
  1366. }
  1367. if ( m_pTeamGoalTournament )
  1368. {
  1369. if ( !m_pTeamGoalTournament->HudElementKeyInput( down, keynum, pszCurrentBinding ) )
  1370. {
  1371. return 0;
  1372. }
  1373. }
  1374. if ( TournamentHudElementKeyInput( down, keynum, pszCurrentBinding ) == true )
  1375. return 0;
  1376. if ( ArenaClassLayoutKeyInput( down, keynum, pszCurrentBinding ) == true )
  1377. return 0;
  1378. #ifndef _X360
  1379. if ( ShouldScoreBoardHandleKeyInput( down, keynum, pszCurrentBinding ) )
  1380. return 0;
  1381. #endif // !360
  1382. return BaseClass::HudElementKeyInput( down, keynum, pszCurrentBinding );
  1383. }
  1384. //-----------------------------------------------------------------------------
  1385. // Purpose: See if spectator input occurred. Return 0 if the key is swallowed.
  1386. //-----------------------------------------------------------------------------
  1387. int ClientModeTFNormal::HandleSpectatorKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding )
  1388. {
  1389. #if defined( _X360 )
  1390. // On X360 when we have scoreboard up in spectator menu we cannot
  1391. // steal any input because gamertags must be selectable and gamercards
  1392. // must be accessible.
  1393. // We cannot rely on any keybindings in this case since user could have
  1394. // remapped everything.
  1395. if ( m_pScoreboard && m_pScoreboard->IsVisible() )
  1396. {
  1397. return 1;
  1398. }
  1399. #endif
  1400. // @note Tom Bui: Coaching, so override all input
  1401. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  1402. if ( pLocalPlayer && pLocalPlayer->m_bIsCoaching )
  1403. {
  1404. if ( down && pszCurrentBinding && Q_strcmp( pszCurrentBinding, "+jump" ) == 0 )
  1405. {
  1406. engine->ClientCmd( "spec_mode" );
  1407. return 0;
  1408. }
  1409. return 1;
  1410. }
  1411. else
  1412. {
  1413. return BaseClass::HandleSpectatorKeyInput( down, keynum, pszCurrentBinding );
  1414. }
  1415. }
  1416. bool ClientModeTFNormal::DoPostScreenSpaceEffects( const CViewSetup *pSetup )
  1417. {
  1418. if ( !BaseClass::DoPostScreenSpaceEffects( pSetup ) )
  1419. return false;
  1420. if( IsInFreezeCam() )
  1421. return false;
  1422. g_GlowObjectManager.RenderGlowEffects( pSetup, 0 );
  1423. return true;
  1424. }
  1425. #if !defined(NO_STEAM)
  1426. void ClientModeTFNormal::OnScreenshotRequested( ScreenshotRequested_t *pParam )
  1427. {
  1428. // Steam has requested a screenshot, act as if the key currently bound to screenshots
  1429. // has been pressed (we want tagging and the killcam screenshot behavior if applicable)
  1430. HudElementKeyInput( 0, BUTTON_CODE_INVALID, "screenshot" );
  1431. engine->ClientCmd( "screenshot" );
  1432. }
  1433. #endif
  1434. //-----------------------------------------------------------------------------------------
  1435. 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" );
  1436. ConVar cl_ask_favorite_opt_out( "cl_ask_favorite_opt_out", "0", FCVAR_ARCHIVE, "If nonzero, don't auto-ask to favorite servers" );
  1437. 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)" );
  1438. 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" );
  1439. ConVar cl_ask_blacklist_opt_out( "cl_ask_blacklist_opt_out", "0", FCVAR_ARCHIVE, "If nonzero, don't auto-ask to blacklist servers" );
  1440. 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)" );
  1441. //----------------------------------------------------------------------------
  1442. void OnAskFavoriteDialogButtonPressed( bool bConfirm, void *pContext )
  1443. {
  1444. if ( bConfirm )
  1445. {
  1446. // add last server to our favorites
  1447. steamapicontext->SteamMatchmaking()->AddFavoriteGame( steamapicontext->SteamUtils()->GetAppID(),
  1448. GetClientModeTFNormal()->GetLastConnectedServerIP(),
  1449. GetClientModeTFNormal()->GetLastConnectedServerPort(),
  1450. GetClientModeTFNormal()->GetLastConnectedServerPort(),
  1451. k_unFavoriteFlagFavorite,
  1452. CRTime::RTime32TimeCur() );
  1453. // Send it to the GC
  1454. GCSDK::CGCMsg< MsgGCServerBrowser_Server_t > msg( k_EMsgGCServerBrowser_FavoriteServer );
  1455. msg.Body().m_unIP = GetClientModeTFNormal()->GetLastConnectedServerIP();
  1456. msg.Body().m_usPort = GetClientModeTFNormal()->GetLastConnectedServerPort();
  1457. msg.Body().m_ubSource = k_EGCMsgServerBrowser_FromAutoAskDialog;
  1458. GCClientSystem()->BSendMessage( msg );
  1459. }
  1460. }
  1461. //----------------------------------------------------------------------------
  1462. void OnAskBlacklistDialogButtonPressed( bool bConfirm, void *pContext )
  1463. {
  1464. if ( bConfirm )
  1465. {
  1466. // add last server to our blacklist
  1467. CBlacklistedServerManager blackList;
  1468. blackList.LoadServersFromFile( BLACKLIST_DEFAULT_SAVE_FILE, false );
  1469. blackList.AddServer( GetClientModeTFNormal()->GetLastConnectedServerName(),
  1470. GetClientModeTFNormal()->GetLastConnectedServerIP(),
  1471. GetClientModeTFNormal()->GetLastConnectedServerPort() );
  1472. blackList.SaveToFile( BLACKLIST_DEFAULT_SAVE_FILE );
  1473. // Send it to the GC
  1474. GCSDK::CGCMsg< MsgGCServerBrowser_Server_t > msg( k_EMsgGCServerBrowser_BlacklistServer );
  1475. msg.Body().m_unIP = GetClientModeTFNormal()->GetLastConnectedServerIP();
  1476. msg.Body().m_usPort = GetClientModeTFNormal()->GetLastConnectedServerPort();
  1477. msg.Body().m_ubSource = k_EGCMsgServerBrowser_FromAutoAskDialog;
  1478. GCClientSystem()->BSendMessage( msg );
  1479. }
  1480. }
  1481. //----------------------------------------------------------------------------
  1482. // If conditions are right, prompt the user to either favorite or blacklist
  1483. // the last server they had connected to.
  1484. void ClientModeTFNormal::AskFavoriteOrBlacklist() const
  1485. {
  1486. // based on the time we spent on our last server, ask to favorite or blacklist it
  1487. int sessionDuration = GetSteamWorksSGameStatsUploader().GetTimeSinceEpoch() - m_lastServerConnectTime;
  1488. if ( sessionDuration > 0 )
  1489. {
  1490. // favorite?
  1491. if ( !cl_ask_favorite_opt_out.GetBool() && sessionDuration > cl_ask_favorite_min_session_duration.GetFloat() )
  1492. {
  1493. // don't ask to favorite reserved addresses
  1494. if ( !cl_ask_favorite_for_any_server.GetBool() )
  1495. {
  1496. netadr_t netAdr( GetLastConnectedServerIP(), GetLastConnectedServerPort() );
  1497. if ( !netAdr.IsValid() || netAdr.IsReservedAdr() )
  1498. return;
  1499. // Don't offer this for Valve servers, either
  1500. if ( GTFGCClientSystem()->BIsIPRecentMatchServer( netAdr ) )
  1501. {
  1502. return;
  1503. }
  1504. }
  1505. // is this server already a favorite?
  1506. for( int i=0; i<steamapicontext->SteamMatchmaking()->GetFavoriteGameCount(); ++i )
  1507. {
  1508. AppId_t appID = steamapicontext->SteamUtils()->GetAppID();
  1509. uint32 IP;
  1510. uint16 connPort;
  1511. uint16 queryPort;
  1512. uint32 flags;
  1513. uint32 time32LastPlayedOnServer;
  1514. if ( steamapicontext->SteamMatchmaking()->GetFavoriteGame( i, &appID, &IP, &connPort, &queryPort, &flags, &time32LastPlayedOnServer ) )
  1515. {
  1516. if ( ( flags & k_unFavoriteFlagFavorite ) == false )
  1517. {
  1518. // not a favorite
  1519. continue;
  1520. }
  1521. if ( ( appID == 0 || appID == steamapicontext->SteamUtils()->GetAppID() ) &&
  1522. IP == GetLastConnectedServerIP() &&
  1523. connPort == GetLastConnectedServerPort() )
  1524. {
  1525. // already have this server in our favorites - don't ask again
  1526. return;
  1527. }
  1528. }
  1529. }
  1530. // new potential favorite - ask
  1531. ShowConfirmOptOutDialog( "#TF_Serverlist_Ask_Favorite_Title", "#TF_Serverlist_Ask_Favorite_Text",
  1532. "#TF_Serverlist_Ask_Yes", "#TF_Serverlist_Ask_No",
  1533. "#TF_ServerList_Ask_Favorite_Opt_Out", "cl_ask_favorite_opt_out",
  1534. OnAskFavoriteDialogButtonPressed, NULL );
  1535. }
  1536. // blacklist?
  1537. if ( !cl_ask_blacklist_opt_out.GetBool() && sessionDuration < cl_ask_blacklist_max_session_duration.GetFloat() )
  1538. {
  1539. // is this server already blacklisted?
  1540. CBlacklistedServerManager blackList;
  1541. blackList.LoadServersFromFile( BLACKLIST_DEFAULT_SAVE_FILE, false );
  1542. netadr_t lastServer( GetLastConnectedServerIP(), (unsigned short)GetLastConnectedServerPort() );
  1543. if ( ( !blackList.CanServerBeBlacklisted( lastServer.GetIPHostByteOrder(), lastServer.GetPort(), GetLastConnectedServerName() )
  1544. || GTFGCClientSystem()->BIsIPRecentMatchServer( lastServer ) )
  1545. && !cl_ask_blacklist_for_any_server.GetBool() )
  1546. {
  1547. // don't bother - this server is not blacklistable
  1548. return;
  1549. }
  1550. if ( blackList.IsServerBlacklisted( GetLastConnectedServerIP(), GetLastConnectedServerPort(), GetLastConnectedServerName() ) )
  1551. {
  1552. // already marked as bad
  1553. return;
  1554. }
  1555. // new potential blacklist - ask
  1556. ShowConfirmOptOutDialog( "#TF_Serverlist_Ask_Blacklist_Title", "#TF_Serverlist_Ask_Blacklist_Text",
  1557. "#TF_Serverlist_Ask_Yes", "#TF_Serverlist_Ask_No",
  1558. "#TF_ServerList_Ask_Blacklist_Opt_Out", "cl_ask_blacklist_opt_out",
  1559. OnAskBlacklistDialogButtonPressed, NULL );
  1560. }
  1561. }
  1562. }
  1563. //----------------------------------------------------------------------------
  1564. void ClientModeTFNormal::Update()
  1565. {
  1566. BaseClass::Update();
  1567. TFModalStack()->Update();
  1568. NotificationQueue_Update();
  1569. // CHudVote *pHudVote = GET_HUDELEMENT( CHudVote );
  1570. // CTFHudMannVsMachineStatus *pMannVsMachineStatus = GET_HUDELEMENT( CTFHudMannVsMachineStatus );
  1571. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  1572. // Update steam controller stuff if one is active
  1573. if ( ::input->IsSteamControllerActive() )
  1574. {
  1575. // Walk through all the panels and HUD elements, see what kind of action set each one requests.
  1576. bool bNeedMenu = false;
  1577. bool bNeedHUD = false;
  1578. bool bNeedSpectator = false;
  1579. m_pViewport->ForEachPanel( [&] (IViewPortPanel* pPanel) {
  1580. if ( pPanel && pPanel->IsVisible() )
  1581. {
  1582. auto actionset = pPanel->GetPreferredActionSet();
  1583. if ( actionset == GAME_ACTION_SET_MENUCONTROLS )
  1584. {
  1585. bNeedMenu = true;
  1586. }
  1587. else if ( actionset == GAME_ACTION_SET_IN_GAME_HUD )
  1588. {
  1589. bNeedHUD = true;
  1590. }
  1591. else if ( actionset == GAME_ACTION_SET_SPECTATOR )
  1592. {
  1593. bNeedSpectator = true;
  1594. }
  1595. }
  1596. } );
  1597. gHUD.ForEachHudElement( [&]( CHudElement* pElement ) {
  1598. if ( pElement && pElement->IsActive() )
  1599. {
  1600. auto actionset = pElement->GetPreferredActionSet();
  1601. if ( actionset == GAME_ACTION_SET_MENUCONTROLS )
  1602. {
  1603. bNeedMenu = true;
  1604. }
  1605. else if ( actionset == GAME_ACTION_SET_IN_GAME_HUD )
  1606. {
  1607. bNeedHUD = true;
  1608. }
  1609. else if ( actionset == GAME_ACTION_SET_SPECTATOR )
  1610. {
  1611. bNeedSpectator = true;
  1612. }
  1613. }
  1614. } );
  1615. // Set the preferred action set. Requesting menu trumps hud, which trumps spectator, which trumps fps.
  1616. if ( !engine->IsInGame() || !engine->IsConnected() || enginevgui->IsGameUIVisible() || bNeedMenu )
  1617. {
  1618. ::input->SetPreferredGameActionSet( GAME_ACTION_SET_MENUCONTROLS );
  1619. }
  1620. else if ( bNeedHUD )
  1621. {
  1622. ::input->SetPreferredGameActionSet( GAME_ACTION_SET_IN_GAME_HUD );
  1623. }
  1624. else if ( bNeedSpectator || (pLocalPlayer && pLocalPlayer->GetTeamNumber() == TEAM_SPECTATOR) )
  1625. {
  1626. ::input->SetPreferredGameActionSet( GAME_ACTION_SET_SPECTATOR );
  1627. }
  1628. else
  1629. {
  1630. ::input->SetPreferredGameActionSet( GAME_ACTION_SET_FPSCONTROLS );
  1631. }
  1632. // Adjust look sensitivity down if the player is current using a zoomed-in weapon (typically sniper rifle).
  1633. float look_sensitivity = 0.125f;
  1634. if ( pLocalPlayer && pLocalPlayer->m_Shared.InCond( TF_COND_ZOOMED ) )
  1635. {
  1636. look_sensitivity = 0.035f;
  1637. }
  1638. // Set the flag for special action handling if we're taunting
  1639. if ( pLocalPlayer && pLocalPlayer->m_Shared.InCond( TF_COND_TAUNTING ) )
  1640. {
  1641. ::input->SetGameActionSetFlags( GAME_ACTION_SET_FLAGS_TAUNTING );
  1642. }
  1643. else
  1644. {
  1645. ::input->SetGameActionSetFlags( GAME_ACTION_SET_FLAGS_NONE );
  1646. }
  1647. sc_look_sensitivity_scale.SetValue( look_sensitivity );
  1648. }
  1649. if ( !engine->IsInGame() )
  1650. {
  1651. // @note Tom Bui: we want this thing to always run, so we get animations at the main menu
  1652. g_pClientMode->GetViewportAnimationController()->UpdateAnimations( gpGlobals->curtime );
  1653. }
  1654. if ( !engine->IsConnected() )
  1655. {
  1656. if ( m_wasConnectedLastUpdate )
  1657. {
  1658. // just disconnected from a game
  1659. m_wasConnectedLastUpdate = false;
  1660. AskFavoriteOrBlacklist();
  1661. m_lastServerConnectTime = 0;
  1662. }
  1663. }
  1664. else
  1665. {
  1666. m_wasConnectedLastUpdate = true;
  1667. }
  1668. }
  1669. //----------------------------------------------------------------------------
  1670. void ClientModeTFNormal::ComputeVguiResConditions( KeyValues *pkvConditions )
  1671. {
  1672. BaseClass::ComputeVguiResConditions( pkvConditions );
  1673. }
  1674. //----------------------------------------------------------------------------
  1675. bool ClientModeTFNormal::IsInfoPanelAllowed()
  1676. {
  1677. if ( !BaseClass::IsInfoPanelAllowed() )
  1678. return false;
  1679. return !m_bRestrictInfoPanel || !m_bInfoPanelShown;
  1680. }
  1681. //----------------------------------------------------------------------------
  1682. bool ClientModeTFNormal::IsHTMLInfoPanelAllowed()
  1683. {
  1684. if ( !BaseClass::IsHTMLInfoPanelAllowed() )
  1685. return false;
  1686. return !m_bRestrictInfoPanel;
  1687. }
  1688. //----------------------------------------------------------------------------
  1689. void ClientModeTFNormal::InfoPanelDisplayed()
  1690. {
  1691. BaseClass::InfoPanelDisplayed();
  1692. m_bInfoPanelShown = true;
  1693. }
  1694. //----------------------------------------------------------------------------
  1695. bool ClientModeTFNormal::IsEngyBuildVisible() const
  1696. {
  1697. return m_pMenuEngyBuild && m_pMenuEngyBuild->IsVisible();
  1698. }
  1699. //----------------------------------------------------------------------------
  1700. bool ClientModeTFNormal::IsEngyDestroyVisible() const
  1701. {
  1702. return m_pMenuEngyDestroy && m_pMenuEngyDestroy->IsVisible();
  1703. }
  1704. //----------------------------------------------------------------------------
  1705. bool ClientModeTFNormal::IsEngyEurekaTeleportVisible() const
  1706. {
  1707. return m_pEurekaTeleportMenu && m_pEurekaTeleportMenu->IsVisible();
  1708. }
  1709. //----------------------------------------------------------------------------
  1710. bool ClientModeTFNormal::IsSpyDisguiseVisible() const
  1711. {
  1712. return m_pMenuSpyDisguise && m_pMenuSpyDisguise->IsVisible();
  1713. }
  1714. //----------------------------------------------------------------------------
  1715. bool ClientModeTFNormal::IsUpgradePanelVisible() const
  1716. {
  1717. return m_pMenuUpgradePanel && m_pMenuUpgradePanel->IsVisible();
  1718. }
  1719. //----------------------------------------------------------------------------
  1720. bool ClientModeTFNormal::IsTauntSelectPanelVisible() const
  1721. {
  1722. return m_pMenuTauntSelection && m_pMenuTauntSelection->IsVisible();
  1723. }
  1724. //----------------------------------------------------------------------------
  1725. void ClientModeTFNormal::PrintTextToChat( const char *pText, KeyValues *pKeyValues )
  1726. {
  1727. CBaseHudChat *pHUDChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat );
  1728. if ( pHUDChat )
  1729. {
  1730. wchar_t wszText[1024]=L"";
  1731. g_pVGuiLocalize->ConstructString_safe( wszText, pText, pKeyValues );
  1732. char szAnsi[1024];
  1733. g_pVGuiLocalize->ConvertUnicodeToANSI( wszText, szAnsi, sizeof(szAnsi) );
  1734. pHUDChat->Printf( CHAT_FILTER_NONE, "%s", szAnsi );
  1735. }
  1736. }
  1737. void ClientModeTFNormal::PrintTextToChatPlayer( int iPlayerIndex, const char *pText, KeyValues *pKeyValues )
  1738. {
  1739. CBaseHudChat *pHUDChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat );
  1740. if ( pHUDChat )
  1741. {
  1742. wchar_t wszText[1024]=L"";
  1743. g_pVGuiLocalize->ConstructString_safe( wszText, pText, pKeyValues );
  1744. char szAnsi[1024];
  1745. g_pVGuiLocalize->ConvertUnicodeToANSI( wszText, szAnsi, sizeof(szAnsi) );
  1746. pHUDChat->ChatPrintf( iPlayerIndex, CHAT_FILTER_NONE, "%s", szAnsi );
  1747. }
  1748. }
  1749. void ClientModeTFNormal::OnDemoRecordStart( char const* pDemoBaseName )
  1750. {
  1751. BaseClass::OnDemoRecordStart( pDemoBaseName );
  1752. }
  1753. void ClientModeTFNormal::OnDemoRecordStop()
  1754. {
  1755. IGameEvent *event = gameeventmanager->CreateEvent( "ds_stop" );
  1756. if ( event )
  1757. {
  1758. gameeventmanager->FireEventClientSide( event );
  1759. }
  1760. BaseClass::OnDemoRecordStop();
  1761. }
  1762. void __MsgFunc_PlayerBonusPoints( bf_read &msg )
  1763. {
  1764. int nPoints = (int) msg.ReadByte();
  1765. int iPlayerEntIndex = (int) msg.ReadByte();
  1766. int iSourceEntIndex = (int) msg.ReadByte();
  1767. IGameEvent *event = gameeventmanager->CreateEvent( "player_bonuspoints" );
  1768. if ( event )
  1769. {
  1770. event->SetInt( "points", nPoints );
  1771. event->SetInt( "player_entindex", iPlayerEntIndex );
  1772. event->SetInt( "source_entindex", iSourceEntIndex );
  1773. gameeventmanager->FireEventClientSide( event );
  1774. }
  1775. }
  1776. void __MsgFunc_PlayerGodRayEffect( bf_read &msg )
  1777. {
  1778. int iPlayerEntIndex = (int)msg.ReadByte();
  1779. CBasePlayer *player = UTIL_PlayerByIndex( iPlayerEntIndex );
  1780. if ( player )
  1781. {
  1782. player->ParticleProp()->Create( "god_rays", PATTACH_ABSORIGIN_FOLLOW );
  1783. }
  1784. }
  1785. void __MsgFunc_PlayerTeleportHomeEffect( bf_read &msg )
  1786. {
  1787. int iPlayerEntIndex = (int)msg.ReadByte();
  1788. CBasePlayer *pPlayer = UTIL_PlayerByIndex( iPlayerEntIndex );
  1789. if ( pPlayer )
  1790. {
  1791. pPlayer->ParticleProp()->Create( "drg_wrenchmotron_teleport", PATTACH_ABSORIGIN );
  1792. }
  1793. }
  1794. void __MsgFunc_RDTeamPointsChanged( bf_read &msg )
  1795. {
  1796. int nPoints = (int)msg.ReadShort();
  1797. int nTeam = (int)msg.ReadByte();
  1798. int nMethod = (int)msg.ReadByte();
  1799. IGameEvent *event = gameeventmanager->CreateEvent( "rd_team_points_changed" );
  1800. if ( event )
  1801. {
  1802. event->SetInt( "points", nPoints );
  1803. event->SetInt( "team", nTeam );
  1804. event->SetInt( "method", nMethod );
  1805. gameeventmanager->FireEventClientSide( event );
  1806. }
  1807. }
  1808. void __MsgFunc_PlayerLoadoutUpdated( bf_read &msg )
  1809. {
  1810. int iPlayerEntIndex = (int)msg.ReadByte();
  1811. C_TFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayerEntIndex ) );
  1812. if ( pPlayer )
  1813. {
  1814. CTFPlayerInventory *pInv = pPlayer->Inventory();
  1815. if ( pInv )
  1816. {
  1817. pInv->ClearClassLoadoutChangeTracking();
  1818. }
  1819. }
  1820. }
  1821. void __MsgFunc_PlayerTauntSoundLoopStart( bf_read &msg )
  1822. {
  1823. int iPlayerEntIndex = (int)msg.ReadByte();
  1824. C_TFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayerEntIndex ) );
  1825. if ( pPlayer )
  1826. {
  1827. char szTauntSoundLoopName[256];
  1828. msg.ReadString( szTauntSoundLoopName, sizeof(szTauntSoundLoopName) );
  1829. pPlayer->PlayTauntSoundLoop( szTauntSoundLoopName );
  1830. }
  1831. }
  1832. void __MsgFunc_PlayerTauntSoundLoopEnd( bf_read &msg )
  1833. {
  1834. int iPlayerEntIndex = (int)msg.ReadByte();
  1835. C_TFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayerEntIndex ) );
  1836. if ( pPlayer )
  1837. {
  1838. pPlayer->StopTauntSoundLoop();
  1839. }
  1840. }
  1841. void __MsgFunc_ForcePlayerViewAngles( bf_read &msg )
  1842. {
  1843. // Read flag byte. Should be 1 right now.
  1844. int iFlags = (int)msg.ReadByte();
  1845. NOTE_UNUSED( iFlags );
  1846. Assert( iFlags == 0x01 );
  1847. int iPlayerEntIndex = (int)msg.ReadByte();
  1848. C_TFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayerEntIndex ) );
  1849. if ( pPlayer )
  1850. {
  1851. QAngle viewangles;
  1852. msg.ReadBitAngles( viewangles );
  1853. // Set the magic angles here!
  1854. pPlayer->SetLocalAngles( viewangles );
  1855. pPlayer->SetAbsAngles( viewangles );
  1856. pPlayer->SetTauntYaw( viewangles[YAW] );
  1857. pPlayer->m_Shared.SetVehicleMoveAngles( viewangles );
  1858. }
  1859. }
  1860. ConVar tf_halloween_bonus_ducks_cooldown( "tf_halloween_bonus_ducks_cooldown", "20", FCVAR_ARCHIVE );
  1861. void __MsgFunc_BonusDucks( bf_read &msg )
  1862. {
  1863. static float sflNextBonusDucks = 0.f;
  1864. int iPlayerEntIndex = (int)msg.ReadByte();
  1865. int iIgnoreTimer = (int)msg.ReadByte();
  1866. if ( Plat_FloatTime() < sflNextBonusDucks && !iIgnoreTimer )
  1867. return;
  1868. sflNextBonusDucks = Plat_FloatTime() + tf_halloween_bonus_ducks_cooldown.GetFloat();
  1869. C_TFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayerEntIndex ) );
  1870. if ( pPlayer )
  1871. {
  1872. pPlayer->EmitSound( "sf14.Merasmus.DuckHunt.BonusDucks" );
  1873. }
  1874. }
  1875. void __MsgFunc_PlayerPickupWeapon( bf_read &msg )
  1876. {
  1877. IGameEvent *event = gameeventmanager->CreateEvent( "localplayer_pickup_weapon" );
  1878. gameeventmanager->FireEventClientSide( event );
  1879. }
  1880. void __MsgFunc_AutoBalanceVolunteer( bf_read &msg )
  1881. {
  1882. KeyValuesAD pKeyValues( "data" );
  1883. pKeyValues->SetInt( "points", tf_autobalance_xp_bonus.GetInt() );
  1884. CAutobalanceVolunteerNotification *pNotification = new CAutobalanceVolunteerNotification();
  1885. pNotification->SetText( GTFGCClientSystem()->BConnectedToMatchServer( true ) ? "#TF_AutoBalanceVolunteerXPBonus" : "#TF_AutoBalanceVolunteer" );
  1886. pNotification->SetLifetime( tf_autobalance_query_lifetime.GetInt() );
  1887. pNotification->SetKeyValues( pKeyValues );
  1888. NotificationQueue_Add( pNotification );
  1889. if ( cl_notifications_show_ingame.GetInt() == 0 )
  1890. {
  1891. // player has turned off the in-game notifications so let's throw a chat
  1892. // message to see if they will check their notifications in the main menu
  1893. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
  1894. CBaseHudChat *pHUDChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat );
  1895. if ( pPlayer && pHUDChat )
  1896. {
  1897. char szLocalized[100];
  1898. g_pVGuiLocalize->ConvertUnicodeToANSI(g_pVGuiLocalize->Find( "#TF_AutoBalanceVolunteer_ChatText" ), szLocalized, sizeof( szLocalized ) );
  1899. pHUDChat->ChatPrintf( pPlayer->entindex(), CHAT_FILTER_NONE, "%s ", szLocalized );
  1900. }
  1901. }
  1902. }
  1903. void __MsgFunc_AutoBalanceVolunteer_Cancel( bf_read &msg )
  1904. {
  1905. NotificationQueue_Remove( &CAutobalanceVolunteerNotification::IsNotificationType );
  1906. }
  1907. void __MsgFunc_QuestObjectiveCompleted( bf_read &msg )
  1908. {
  1909. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  1910. itemid_t itemID = (itemid_t)msg.ReadLongLong();
  1911. uint16 nStandardPoints = msg.ReadWord();
  1912. uint16 nBonusPoints = msg.ReadWord();
  1913. uint32 nObjectiveDefIndex = msg.ReadWord();
  1914. QuestObjectiveManager()->UpdateFromServer( itemID, nStandardPoints, nBonusPoints );
  1915. QuestObjectiveManager()->EnsureTrackersForPlayer( steamapicontext->SteamUser()->GetSteamID() );
  1916. // Passing a -1 means this is a ninja update where we don't want the fanfare
  1917. if ( nObjectiveDefIndex != (uint32)-1 )
  1918. {
  1919. IGameEvent *pEvent = gameeventmanager->CreateEvent( "quest_objective_completed" );
  1920. if ( pEvent )
  1921. {
  1922. pEvent->SetInt( "quest_item_id_low", itemID & 0xFFFFFFFF );
  1923. pEvent->SetInt( "quest_item_id_hi", itemID >> 32 );
  1924. pEvent->SetInt( "quest_objective_id", nObjectiveDefIndex );
  1925. gameeventmanager->FireEventClientSide( pEvent );
  1926. }
  1927. }
  1928. }
  1929. float PlaySoundEntry( const char* pszSoundEntryName )
  1930. {
  1931. const char *pszSoundName = UTIL_GetRandomSoundFromEntry( pszSoundEntryName );
  1932. if ( pszSoundName && pszSoundName[0] )
  1933. {
  1934. vgui::surface()->PlaySound( pszSoundName );
  1935. return enginesound->GetSoundDuration( pszSoundName );
  1936. }
  1937. return 0.f;
  1938. }
  1939. #ifdef _WIN32
  1940. #include "winlite.h"
  1941. #pragma warning (push)
  1942. #pragma warning(disable:4201) // nameless struct/union
  1943. #include <mmsystem.h>
  1944. #pragma warning (pop)
  1945. #endif
  1946. //
  1947. void PlayOutOfGameSound( const char *pszSound )
  1948. {
  1949. #ifdef _WIN32
  1950. PlaySound( pszSound, NULL, SND_FILENAME | SND_ASYNC );
  1951. #endif
  1952. }