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.

1108 lines
32 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. #include "cbase.h"
  9. #include "hud.h"
  10. #include "clientmode_csnormal.h"
  11. #include "cdll_client_int.h"
  12. #include "iinput.h"
  13. #include "vgui/ISurface.h"
  14. #include "vgui/IPanel.h"
  15. #include <vgui_controls/AnimationController.h>
  16. #include "ivmodemanager.h"
  17. #include "buymenu.h"
  18. #include "filesystem.h"
  19. #include "vgui/IVGui.h"
  20. #include "hud_basechat.h"
  21. #include "view_shared.h"
  22. #include "view.h"
  23. #include "ivrenderview.h"
  24. #include "cstrikeclassmenu.h"
  25. #include "model_types.h"
  26. #include "iefx.h"
  27. #include "dlight.h"
  28. #include <imapoverview.h>
  29. #include "c_playerresource.h"
  30. #include "c_soundscape.h"
  31. #include <KeyValues.h>
  32. #include "text_message.h"
  33. #include "panelmetaclassmgr.h"
  34. #include "vguicenterprint.h"
  35. #include "physpropclientside.h"
  36. #include "c_weapon__stubs.h"
  37. #include <engine/IEngineSound.h>
  38. #include "c_cs_hostage.h"
  39. #include "buy_presets/buy_presets.h"
  40. #include "bitbuf.h"
  41. #include "usermessages.h"
  42. #include "prediction.h"
  43. #include "datacache/imdlcache.h"
  44. //=============================================================================
  45. // HPE_BEGIN:
  46. // [tj] Needed to retrieve achievement text
  47. // [menglish] Need access to message macros
  48. //=============================================================================
  49. #include "achievementmgr.h"
  50. #include "hud_macros.h"
  51. #include "c_plantedc4.h"
  52. #include "tier1/fmtstr.h"
  53. #include "history_resource.h"
  54. #include "cs_client_gamestats.h"
  55. // [tj] We need to forward declare this, since the definition is all inside the implementation file
  56. class CHudHintDisplay;
  57. //=============================================================================
  58. // HPE_END
  59. //=============================================================================
  60. void __MsgFunc_MatchEndConditions( bf_read &msg );
  61. class CHudChat;
  62. ConVar default_fov( "default_fov", "90", FCVAR_CHEAT );
  63. IClientMode *g_pClientMode = NULL;
  64. // This is a temporary entity used to render the player's model while drawing the class selection menu.
  65. CHandle<C_BaseAnimatingOverlay> g_ClassImagePlayer; // player
  66. CHandle<C_BaseAnimating> g_ClassImageWeapon; // weapon
  67. STUB_WEAPON_CLASS( cycler_weapon, WeaponCycler, C_BaseCombatWeapon );
  68. STUB_WEAPON_CLASS( weapon_cubemap, WeaponCubemap, C_BaseCombatWeapon );
  69. //-----------------------------------------------------------------------------
  70. // HACK: the detail sway convars are archive, and default to 0. Existing CS:S players thus have no detail
  71. // prop sway. We'll force them to DoD's default values for now. What we really need in the long run is
  72. // a system to apply changes to archived convars' defaults to existing players.
  73. extern ConVar cl_detail_max_sway;
  74. extern ConVar cl_detail_avoid_radius;
  75. extern ConVar cl_detail_avoid_force;
  76. extern ConVar cl_detail_avoid_recover_speed;
  77. //-----------------------------------------------------------------------------
  78. ConVar cl_autobuy(
  79. "cl_autobuy",
  80. "",
  81. FCVAR_USERINFO,
  82. "The order in which autobuy will attempt to purchase items" );
  83. //-----------------------------------------------------------------------------
  84. ConVar cl_rebuy(
  85. "cl_rebuy",
  86. "",
  87. FCVAR_USERINFO,
  88. "The order in which rebuy will attempt to repurchase items" );
  89. //-----------------------------------------------------------------------------
  90. void SetBuyData( const ConVar &buyVar, const char *filename )
  91. {
  92. // if we already have autobuy data, don't bother re-parsing the text file
  93. if ( *buyVar.GetString() )
  94. return;
  95. // First, look for a mapcycle file in the cfg directory, which is preferred
  96. char szRecommendedName[ 256 ];
  97. char szResolvedName[ 256 ];
  98. V_sprintf_safe( szRecommendedName, "cfg/%s", filename );
  99. V_strcpy_safe( szResolvedName, szRecommendedName );
  100. if ( filesystem->FileExists( szResolvedName, "GAME" ) )
  101. {
  102. Msg( "Loading '%s'.\n", szResolvedName );
  103. }
  104. else
  105. {
  106. // Check the root
  107. V_strcpy_safe( szResolvedName, filename );
  108. if ( filesystem->FileExists( szResolvedName, "GAME" ) )
  109. {
  110. Msg( "Loading '%s' ('%s' was not found.)\n", szResolvedName, szRecommendedName );
  111. }
  112. else
  113. {
  114. // Try cfg/xxx_default.txt
  115. V_strcpy_safe( szResolvedName, szRecommendedName );
  116. char *dotTxt = V_stristr( szResolvedName, ".txt" );
  117. Assert( dotTxt );
  118. if ( dotTxt )
  119. {
  120. V_strcpy( dotTxt, "_default.txt" );
  121. }
  122. if ( !filesystem->FileExists( szResolvedName, "GAME" ) )
  123. {
  124. Warning( "Not loading buy data. Neither '%s' nor %s were found.\n", szResolvedName, szRecommendedName );
  125. return;
  126. }
  127. Msg( "Loading '%s'\n", szResolvedName );
  128. }
  129. }
  130. CUtlBuffer buf;
  131. if ( !filesystem->ReadFile( szResolvedName, "GAME", buf ) )
  132. {
  133. // WAT
  134. Warning( "Failed to load '%s'.\n", szResolvedName );
  135. return;
  136. }
  137. buf.PutChar('\0');
  138. char token[256];
  139. char buystring[256];
  140. V_sprintf_safe( buystring, "setinfo %s \"", buyVar.GetName() );
  141. const char *pfile = engine->ParseFile( (const char *)buf.Base(), token, sizeof(token) );
  142. bool first = true;
  143. while (pfile != NULL)
  144. {
  145. if (first)
  146. {
  147. first = false;
  148. }
  149. else
  150. {
  151. Q_strncat(buystring, " ", sizeof(buystring), COPY_ALL_CHARACTERS);
  152. }
  153. Q_strncat(buystring, token, sizeof(buystring), COPY_ALL_CHARACTERS);
  154. pfile = engine->ParseFile( pfile, token, sizeof(token) );
  155. }
  156. Q_strncat(buystring, "\"", sizeof(buystring), COPY_ALL_CHARACTERS);
  157. engine->ClientCmd(buystring);
  158. }
  159. void MsgFunc_KillCam(bf_read &msg)
  160. {
  161. C_CSPlayer *pPlayer = ToCSPlayer( C_BasePlayer::GetLocalPlayer() );
  162. if ( !pPlayer )
  163. return;
  164. int newMode = msg.ReadByte();
  165. if ( newMode != g_nKillCamMode )
  166. {
  167. #if !defined( NO_ENTITY_PREDICTION )
  168. if ( g_nKillCamMode == OBS_MODE_NONE )
  169. {
  170. // kill cam is switch on, turn off prediction
  171. g_bForceCLPredictOff = true;
  172. }
  173. else if ( newMode == OBS_MODE_NONE )
  174. {
  175. // kill cam is switched off, restore old prediction setting is we switch back to normal mode
  176. g_bForceCLPredictOff = false;
  177. }
  178. #endif
  179. g_nKillCamMode = newMode;
  180. }
  181. g_nKillCamTarget1 = msg.ReadByte();
  182. g_nKillCamTarget2 = msg.ReadByte();
  183. }
  184. // --------------------------------------------------------------------------------- //
  185. // CCSModeManager.
  186. // --------------------------------------------------------------------------------- //
  187. class CCSModeManager : public IVModeManager
  188. {
  189. public:
  190. virtual void Init();
  191. virtual void SwitchMode( bool commander, bool force ) {}
  192. virtual void LevelInit( const char *newmap );
  193. virtual void LevelShutdown( void );
  194. virtual void ActivateMouse( bool isactive ) {}
  195. };
  196. static CCSModeManager g_ModeManager;
  197. IVModeManager *modemanager = ( IVModeManager * )&g_ModeManager;
  198. // --------------------------------------------------------------------------------- //
  199. // CCSModeManager implementation.
  200. // --------------------------------------------------------------------------------- //
  201. #define SCREEN_FILE "scripts/vgui_screens.txt"
  202. void CCSModeManager::Init()
  203. {
  204. g_pClientMode = GetClientModeNormal();
  205. PanelMetaClassMgr()->LoadMetaClassDefinitionFile( SCREEN_FILE );
  206. }
  207. void CCSModeManager::LevelInit( const char *newmap )
  208. {
  209. g_pClientMode->LevelInit( newmap );
  210. SetBuyData( cl_autobuy, "autobuy.txt" );
  211. SetBuyData( cl_rebuy, "rebuy.txt" );
  212. #if !defined( NO_ENTITY_PREDICTION )
  213. if ( g_nKillCamMode > OBS_MODE_NONE )
  214. {
  215. g_bForceCLPredictOff = false;
  216. }
  217. #endif
  218. g_nKillCamMode = OBS_MODE_NONE;
  219. g_nKillCamTarget1 = 0;
  220. g_nKillCamTarget2 = 0;
  221. // HACK: the detail sway convars are archive, and default to 0. Existing CS:S players thus have no detail
  222. // prop sway. We'll force them to DoD's default values for now.
  223. if ( !cl_detail_max_sway.GetFloat() &&
  224. !cl_detail_avoid_radius.GetFloat() &&
  225. !cl_detail_avoid_force.GetFloat() &&
  226. !cl_detail_avoid_recover_speed.GetFloat() )
  227. {
  228. cl_detail_max_sway.SetValue( "5" );
  229. cl_detail_avoid_radius.SetValue( "64" );
  230. cl_detail_avoid_force.SetValue( "0.4" );
  231. cl_detail_avoid_recover_speed.SetValue( "0.25" );
  232. }
  233. }
  234. void CCSModeManager::LevelShutdown( void )
  235. {
  236. g_pClientMode->LevelShutdown();
  237. }
  238. //-----------------------------------------------------------------------------
  239. // Purpose:
  240. //-----------------------------------------------------------------------------
  241. ClientModeCSNormal::ClientModeCSNormal()
  242. {
  243. HOOK_MESSAGE( MatchEndConditions );
  244. }
  245. void ClientModeCSNormal::Init()
  246. {
  247. BaseClass::Init();
  248. ListenForGameEvent( "round_end" );
  249. ListenForGameEvent( "round_start" );
  250. ListenForGameEvent( "player_team" );
  251. ListenForGameEvent( "player_death" );
  252. ListenForGameEvent( "bomb_planted" );
  253. ListenForGameEvent( "bomb_exploded" );
  254. ListenForGameEvent( "bomb_defused" );
  255. ListenForGameEvent( "hostage_killed" );
  256. ListenForGameEvent( "hostage_hurt" );
  257. usermessages->HookMessage( "KillCam", MsgFunc_KillCam );
  258. //=============================================================================
  259. // HPE_BEGIN:
  260. // [tj] Add the shared HUD elements to the render groups responsible for hiding
  261. // conflicting UI
  262. //=============================================================================
  263. CHudElement* hintBox = (CHudElement*)GET_HUDELEMENT( CHudHintDisplay );
  264. if (hintBox)
  265. {
  266. hintBox->RegisterForRenderGroup("hide_for_scoreboard");
  267. hintBox->RegisterForRenderGroup("hide_for_round_panel");
  268. }
  269. CHudElement* historyResource = (CHudElement*)GET_HUDELEMENT( CHudHistoryResource );
  270. if (historyResource)
  271. {
  272. historyResource->RegisterForRenderGroup("hide_for_scoreboard");
  273. }
  274. //=============================================================================
  275. // HPE_END
  276. //=============================================================================
  277. }
  278. void ClientModeCSNormal::InitViewport()
  279. {
  280. BaseClass::InitViewport();
  281. m_pViewport = new CounterStrikeViewport();
  282. m_pViewport->Start( gameuifuncs, gameeventmanager );
  283. }
  284. void ClientModeCSNormal::Update()
  285. {
  286. BaseClass::Update();
  287. // Override the hud's visibility if this is a logo (like E3 demo) map.
  288. if ( CSGameRules() && CSGameRules()->IsLogoMap() )
  289. m_pViewport->SetVisible( false );
  290. }
  291. /*
  292. void ClientModeCSNormal::UpdateSpectatorMode( void )
  293. {
  294. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  295. if ( !pPlayer )
  296. return;
  297. IMapOverview * overviewmap = m_pViewport->GetMapOverviewInterface();
  298. if ( !overviewmap )
  299. return;
  300. overviewmap->SetTime( gpGlobals->curtime );
  301. int obs_mode = pPlayer->GetObserverMode();
  302. if ( obs_mode < OBS_MODE_IN_EYE )
  303. return;
  304. Vector worldpos = pPlayer->GetLocalOrigin();
  305. QAngle angles; engine->GetViewAngles( angles );
  306. C_BaseEntity *target = pPlayer->GetObserverTarget();
  307. if ( target && (obs_mode == OBS_MODE_IN_EYE || obs_mode == OBS_MODE_CHASE) )
  308. {
  309. worldpos = target->GetAbsOrigin();
  310. if ( obs_mode == OBS_MODE_IN_EYE )
  311. {
  312. angles = target->GetAbsAngles();
  313. }
  314. }
  315. Vector2D mappos = overviewmap->WorldToMap( worldpos );
  316. overviewmap->SetCenter( mappos );
  317. overviewmap->SetAngle( angles.y );
  318. for ( int i = 1; i<= MAX_PLAYERS; i++)
  319. {
  320. C_BaseEntity *ent = ClientEntityList().GetEnt( i );
  321. if ( !ent || !ent->IsPlayer() )
  322. continue;
  323. C_BasePlayer *p = ToBasePlayer( ent );
  324. // update position of active players in our PVS
  325. Vector position = p->GetAbsOrigin();
  326. QAngle angle = p->GetAbsAngles();
  327. if ( p->IsDormant() )
  328. {
  329. // if player is not in PVS, use PlayerResources data
  330. position = g_PR->GetPosition( i );
  331. angles[1] = g_PR->GetViewAngle( i );
  332. }
  333. overviewmap->SetPlayerPositions( i-1, position, angles );
  334. }
  335. } */
  336. //-----------------------------------------------------------------------------
  337. // Purpose: We've received a keypress from the engine. Return 1 if the engine is allowed to handle it.
  338. //-----------------------------------------------------------------------------
  339. int ClientModeCSNormal::KeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding )
  340. {
  341. // don't process input in LogoMaps
  342. if( CSGameRules() && CSGameRules()->IsLogoMap() )
  343. return 1;
  344. return BaseClass::KeyInput( down, keynum, pszCurrentBinding );
  345. }
  346. IClientMode *GetClientModeNormal()
  347. {
  348. static ClientModeCSNormal g_ClientModeNormal;
  349. return &g_ClientModeNormal;
  350. }
  351. ClientModeCSNormal* GetClientModeCSNormal()
  352. {
  353. Assert( dynamic_cast< ClientModeCSNormal* >( GetClientModeNormal() ) );
  354. return static_cast< ClientModeCSNormal* >( GetClientModeNormal() );
  355. }
  356. float ClientModeCSNormal::GetViewModelFOV( void )
  357. {
  358. return 74.0f;
  359. }
  360. int ClientModeCSNormal::GetDeathMessageStartHeight( void )
  361. {
  362. return m_pViewport->GetDeathMessageStartHeight();
  363. }
  364. void ClientModeCSNormal::FireGameEvent( IGameEvent *event )
  365. {
  366. CBaseHudChat *pHudChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat );
  367. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  368. CLocalPlayerFilter filter;
  369. if ( !pLocalPlayer || !pHudChat )
  370. return;
  371. const char *eventname = event->GetName();
  372. if ( !eventname || !eventname[0] )
  373. return;
  374. if ( Q_strcmp( "round_start", eventname ) == 0 )
  375. {
  376. // recreate all client side physics props
  377. C_PhysPropClientside::RecreateAll();
  378. // remove hostage ragdolls
  379. for ( int i=0; i<g_HostageRagdolls.Count(); ++i )
  380. {
  381. // double-check that the EHANDLE is still valid
  382. if ( g_HostageRagdolls[i] )
  383. {
  384. g_HostageRagdolls[i]->Release();
  385. }
  386. }
  387. g_HostageRagdolls.RemoveAll();
  388. // Just tell engine to clear decals
  389. engine->ClientCmd( "r_cleardecals\n" );
  390. //stop any looping sounds
  391. enginesound->StopAllSounds( true );
  392. Soundscape_OnStopAllSounds(); // Tell the soundscape system.
  393. }
  394. else if ( Q_strcmp( "round_end", eventname ) == 0 )
  395. {
  396. int winningTeam = event->GetInt("winner");
  397. int reason = event->GetInt("reason");
  398. // play endround announcer sound
  399. if ( winningTeam == TEAM_CT )
  400. {
  401. if ( reason == Bomb_Defused )
  402. {
  403. C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Event.BombDefused");
  404. }
  405. else
  406. {
  407. C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Event.CTWin");
  408. }
  409. }
  410. else if ( winningTeam == TEAM_TERRORIST )
  411. {
  412. C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Event.TERWin");
  413. }
  414. else
  415. {
  416. C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Event.RoundDraw");
  417. }
  418. //=============================================================================
  419. // HPE_BEGIN:
  420. // [pfreese] Only show centerprint message for game commencing; the rest of
  421. // these messages are handled by the end-of-round panel.
  422. // [Forrest] Show all centerprint messages if the end-of-round panel is disabled.
  423. //=============================================================================
  424. static ConVarRef sv_nowinpanel( "sv_nowinpanel" );
  425. static ConVarRef cl_nowinpanel( "cl_nowinpanel" );
  426. if ( reason == Game_Commencing || sv_nowinpanel.GetBool() || cl_nowinpanel.GetBool() )
  427. {
  428. internalCenterPrint->Print( hudtextmessage->LookupString( event->GetString("message") ) );
  429. // we are starting a new round; clear the current match stats
  430. g_CSClientGameStats.ResetMatchStats();
  431. }
  432. //=============================================================================
  433. // HPE_END
  434. //=============================================================================
  435. }
  436. else if ( Q_strcmp( "player_team", eventname ) == 0 )
  437. {
  438. CBaseHudChat *pHudChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat );
  439. C_BasePlayer *pPlayer = USERID2PLAYER( event->GetInt("userid") );
  440. if ( !pPlayer )
  441. return;
  442. bool bDisconnected = event->GetBool("disconnect");
  443. if ( bDisconnected )
  444. return;
  445. int iTeam = event->GetInt("team");
  446. if ( pPlayer->IsLocalPlayer() )
  447. {
  448. // that's me
  449. pPlayer->TeamChange( iTeam );
  450. }
  451. if ( iTeam == TEAM_SPECTATOR )
  452. pHudChat->Printf( CHAT_FILTER_NONE, hudtextmessage->LookupString( "#Game_join_spectators" ), pPlayer->GetPlayerName() );
  453. else if ( iTeam == TEAM_TERRORIST )
  454. pHudChat->Printf( CHAT_FILTER_NONE, hudtextmessage->LookupString( "#Game_join_terrorist" ), pPlayer->GetPlayerName() );
  455. else if ( iTeam == TEAM_CT )
  456. pHudChat->Printf( CHAT_FILTER_NONE, hudtextmessage->LookupString( "#Game_join_ct" ), pPlayer->GetPlayerName() );
  457. }
  458. else if ( Q_strcmp( "bomb_planted", eventname ) == 0 )
  459. {
  460. //C_BasePlayer *pPlayer = USERID2PLAYER( event->GetInt("userid") );
  461. // show centerprint message
  462. internalCenterPrint->Print( "#Cstrike_TitlesTXT_Bomb_Planted" );
  463. // play sound
  464. C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Event.BombPlanted") ;
  465. }
  466. else if ( Q_strcmp( "bomb_defused", eventname ) == 0 )
  467. {
  468. // C_BasePlayer *pPlayer = USERID2PLAYER( event->GetInt("userid") );
  469. }
  470. //=============================================================================
  471. // HPE_BEGIN:
  472. // [menglish] Tell the client side bomb that the bomb has exploding here creating the explosion particle effect
  473. //=============================================================================
  474. else if ( Q_strcmp( "bomb_exploded", eventname ) == 0 )
  475. {
  476. if ( g_PlantedC4s.Count() > 0 )
  477. {
  478. // bomb is planted
  479. C_PlantedC4 *pC4 = g_PlantedC4s[0];
  480. pC4->Explode();
  481. }
  482. }
  483. //=============================================================================
  484. // HPE_END
  485. //=============================================================================
  486. else if ( Q_strcmp( "hostage_killed", eventname ) == 0 )
  487. {
  488. // play sound for spectators and CTs
  489. if ( pLocalPlayer->IsObserver() || (pLocalPlayer->GetTeamNumber() == TEAM_CT) )
  490. {
  491. C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Event.HostageKilled") ;
  492. }
  493. // Show warning to killer
  494. if ( pLocalPlayer->GetUserID() == event->GetInt("userid") )
  495. {
  496. internalCenterPrint->Print( "#Cstrike_TitlesTXT_Killed_Hostage" );
  497. }
  498. }
  499. else if ( Q_strcmp( "hostage_hurt", eventname ) == 0 )
  500. {
  501. // Let the loacl player know he harmed a hostage
  502. if ( pLocalPlayer->GetUserID() == event->GetInt("userid") )
  503. {
  504. internalCenterPrint->Print( "#Cstrike_TitlesTXT_Injured_Hostage" );
  505. }
  506. }
  507. else if ( Q_strcmp( "player_death", eventname ) == 0 )
  508. {
  509. C_BasePlayer *pPlayer = USERID2PLAYER( event->GetInt("userid") );
  510. C_CSPlayer* csPlayer = ToCSPlayer(pPlayer);
  511. if (csPlayer)
  512. {
  513. csPlayer->ClearSoundEvents();
  514. }
  515. if ( pPlayer == C_BasePlayer::GetLocalPlayer() )
  516. {
  517. // we just died, hide any buy panels
  518. gViewPortInterface->ShowPanel( PANEL_BUY, false );
  519. gViewPortInterface->ShowPanel( PANEL_BUY_CT, false );
  520. gViewPortInterface->ShowPanel( PANEL_BUY_TER, false );
  521. gViewPortInterface->ShowPanel( PANEL_BUY_EQUIP_CT, false );
  522. gViewPortInterface->ShowPanel( PANEL_BUY_EQUIP_TER, false );
  523. }
  524. }
  525. else if ( Q_strcmp( "player_changename", eventname ) == 0 )
  526. {
  527. return; // server sends a colorized text string for this
  528. }
  529. //=============================================================================
  530. // HPE_BEGIN:
  531. // [tj] We handle this here instead of in the base class
  532. // The reason is that we don't use string tables to localize.
  533. // Instead, we use the steam localization mechanism.
  534. //
  535. // [dwenger] Remove dependency on stats system for name of achievement.
  536. //=============================================================================
  537. else if ( Q_strcmp( "achievement_earned", eventname ) == 0 )
  538. {
  539. CBaseHudChat *hudChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat );
  540. int iPlayerIndex = event->GetInt( "player" );
  541. C_BasePlayer *pPlayer = UTIL_PlayerByIndex( iPlayerIndex );
  542. int iAchievement = event->GetInt( "achievement" );
  543. if ( !hudChat || !pPlayer )
  544. return;
  545. CAchievementMgr *pAchievementMgr = dynamic_cast<CAchievementMgr *>( engine->GetAchievementMgr() );
  546. if ( !pAchievementMgr )
  547. return;
  548. IAchievement *pAchievement = pAchievementMgr->GetAchievementByID( iAchievement );
  549. if ( pAchievement )
  550. {
  551. if ( !pPlayer->IsDormant() && pPlayer->ShouldAnnounceAchievement() )
  552. {
  553. pPlayer->SetNextAchievementAnnounceTime( gpGlobals->curtime + ACHIEVEMENT_ANNOUNCEMENT_MIN_TIME );
  554. //Do something for the player - Actually we should probably do this client-side when the achievement is first earned.
  555. if (pPlayer->IsLocalPlayer())
  556. {
  557. }
  558. pPlayer->OnAchievementAchieved( iAchievement );
  559. }
  560. if ( g_PR )
  561. {
  562. wchar_t wszPlayerName[MAX_PLAYER_NAME_LENGTH];
  563. g_pVGuiLocalize->ConvertANSIToUnicode( g_PR->GetPlayerName( iPlayerIndex ), wszPlayerName, sizeof( wszPlayerName ) );
  564. wchar_t achievementName[1024];
  565. const wchar_t* constAchievementName = &achievementName[0];
  566. constAchievementName = ACHIEVEMENT_LOCALIZED_NAME( pAchievement );
  567. if (constAchievementName)
  568. {
  569. wchar_t wszLocalizedString[128];
  570. g_pVGuiLocalize->ConstructString( wszLocalizedString, sizeof( wszLocalizedString ), g_pVGuiLocalize->Find( "#Achievement_Earned" ), 2, wszPlayerName, constAchievementName/*wszAchievementString*/ );
  571. char szLocalized[128];
  572. g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalizedString, szLocalized, sizeof( szLocalized ) );
  573. hudChat->ChatPrintf( iPlayerIndex, CHAT_FILTER_ACHIEVEMENT, "%s", szLocalized );
  574. /*
  575. if (pPlayer->IsLocalPlayer())
  576. {
  577. char achievementDescription[1024];
  578. const char* constAchievementDescription = &achievementDescription[0];
  579. constAchievementDescription = pUserStats->GetAchievementDisplayAttribute( pAchievement->GetName(), "desc" );
  580. hudChat->ChatPrintf( iPlayerIndex, CHAT_FILTER_ACHIEVEMENT, "(%s)", constAchievementDescription );
  581. }
  582. */
  583. }
  584. }
  585. }
  586. }
  587. //=============================================================================
  588. // HPE_END
  589. //=============================================================================
  590. else
  591. {
  592. BaseClass::FireGameEvent( event );
  593. }
  594. }
  595. void RemoveClassImageEntity()
  596. {
  597. C_BaseAnimating *pEnt = g_ClassImagePlayer.Get();
  598. if ( pEnt )
  599. {
  600. pEnt->Remove();
  601. g_ClassImagePlayer = NULL;
  602. }
  603. pEnt = g_ClassImageWeapon.Get();
  604. if ( pEnt )
  605. {
  606. pEnt->Remove();
  607. g_ClassImagePlayer = NULL;
  608. }
  609. }
  610. bool ShouldRecreateClassImageEntity( C_BaseAnimating *pEnt, const char *pNewModelName )
  611. {
  612. if ( !pNewModelName || !pNewModelName[0] )
  613. return false;
  614. if ( !pEnt )
  615. return true;
  616. const model_t *pModel = pEnt->GetModel();
  617. if ( !pModel )
  618. return true;
  619. const char *pName = modelinfo->GetModelName( pModel );
  620. if ( !pName )
  621. return true;
  622. // reload only if names are different
  623. const char *pNameNoPath = V_UnqualifiedFileName( pName );
  624. const char *pNewModelNameNoPath = V_UnqualifiedFileName( pNewModelName );
  625. return( Q_stricmp( pNameNoPath, pNewModelNameNoPath ) != 0 );
  626. }
  627. void UpdateClassImageEntity(
  628. const char *pModelName,
  629. int x, int y, int width, int height )
  630. {
  631. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  632. if ( !pLocalPlayer )
  633. return;
  634. MDLCACHE_CRITICAL_SECTION();
  635. const char *pWeaponName = "models/weapons/w_rif_ak47.mdl";
  636. const char *pWeaponSequence = "Walk_Upper_AK";
  637. int i;
  638. for ( i=0; i<CTPlayerModels.Count(); ++i )
  639. {
  640. if ( Q_strcasecmp( pModelName, CTPlayerModels[i] ) == 0 )
  641. {
  642. // give CTs a M4
  643. pWeaponName = "models/weapons/w_rif_m4a1.mdl";
  644. pWeaponSequence = "Walk_Upper_M4";
  645. break;
  646. }
  647. }
  648. if ( pLocalPlayer->IsAlive() && pLocalPlayer->GetActiveWeapon() )
  649. {
  650. C_WeaponCSBase *weapon = dynamic_cast< C_WeaponCSBase * >(pLocalPlayer->GetActiveWeapon());
  651. if ( weapon )
  652. {
  653. pWeaponName = weapon->GetWorldModel();
  654. pWeaponSequence = VarArgs("Walk_Upper_%s", weapon->GetCSWpnData().m_szAnimExtension);
  655. }
  656. }
  657. C_BaseAnimatingOverlay *pPlayerModel = g_ClassImagePlayer.Get();
  658. // Does the entity even exist yet?
  659. bool recreatePlayer = ShouldRecreateClassImageEntity( pPlayerModel, pModelName );
  660. if ( recreatePlayer )
  661. {
  662. if ( pPlayerModel )
  663. pPlayerModel->Remove();
  664. pPlayerModel = new C_BaseAnimatingOverlay;
  665. pPlayerModel->InitializeAsClientEntity( pModelName, RENDER_GROUP_OPAQUE_ENTITY );
  666. pPlayerModel->AddEffects( EF_NODRAW ); // don't let the renderer draw the model normally
  667. // let player walk ahead
  668. pPlayerModel->SetSequence( pPlayerModel->LookupSequence( "walk_lower" ) );
  669. pPlayerModel->SetPoseParameter( "move_yaw", 0.0f ); // move_yaw
  670. pPlayerModel->SetPoseParameter( "body_pitch", 10.0f ); // body_pitch, look down a bit
  671. pPlayerModel->SetPoseParameter( "body_yaw", 0.0f ); // body_yaw
  672. pPlayerModel->SetPoseParameter( "move_y", 0.0f ); // move_y
  673. pPlayerModel->SetPoseParameter( "move_x", 1.0f ); // move_x, walk forward
  674. pPlayerModel->m_flAnimTime = gpGlobals->curtime;
  675. g_ClassImagePlayer = pPlayerModel;
  676. }
  677. C_BaseAnimating *pWeaponModel = g_ClassImageWeapon.Get();
  678. // Does the entity even exist yet?
  679. if ( recreatePlayer || ShouldRecreateClassImageEntity( pWeaponModel, pWeaponName ) )
  680. {
  681. if ( pWeaponModel )
  682. pWeaponModel->Remove();
  683. pWeaponModel = new C_BaseAnimating;
  684. pWeaponModel->InitializeAsClientEntity( pWeaponName, RENDER_GROUP_OPAQUE_ENTITY );
  685. pWeaponModel->AddEffects( EF_NODRAW ); // don't let the renderer draw the model normally
  686. pWeaponModel->FollowEntity( pPlayerModel ); // attach to player model
  687. pWeaponModel->m_flAnimTime = gpGlobals->curtime;
  688. g_ClassImageWeapon = pWeaponModel;
  689. }
  690. Vector origin = pLocalPlayer->EyePosition();
  691. Vector lightOrigin = origin;
  692. // find a spot inside the world for the dlight's origin, or it won't illuminate the model
  693. Vector testPos( origin.x - 100, origin.y, origin.z + 100 );
  694. trace_t tr;
  695. UTIL_TraceLine( origin, testPos, MASK_OPAQUE, pLocalPlayer, COLLISION_GROUP_NONE, &tr );
  696. if ( tr.fraction == 1.0f )
  697. {
  698. lightOrigin = tr.endpos;
  699. }
  700. else
  701. {
  702. // Now move the model away so we get the correct illumination
  703. lightOrigin = tr.endpos + Vector( 1, 0, -1 ); // pull out from the solid
  704. Vector start = lightOrigin;
  705. Vector end = lightOrigin + Vector( 100, 0, -100 );
  706. UTIL_TraceLine( start, end, MASK_OPAQUE, pLocalPlayer, COLLISION_GROUP_NONE, &tr );
  707. origin = tr.endpos;
  708. }
  709. // move player model in front of our view
  710. pPlayerModel->SetAbsOrigin( origin );
  711. pPlayerModel->SetAbsAngles( QAngle( 0, 210, 0 ) );
  712. // wacky hacky, set upper body animation
  713. pPlayerModel->m_SequenceTransitioner.CheckForSequenceChange(
  714. pPlayerModel->GetModelPtr(),
  715. pPlayerModel->LookupSequence( "walk_lower" ),
  716. false,
  717. true
  718. );
  719. pPlayerModel->m_SequenceTransitioner.UpdateCurrent(
  720. pPlayerModel->GetModelPtr(),
  721. pPlayerModel->LookupSequence( "walk_lower" ),
  722. pPlayerModel->GetCycle(),
  723. pPlayerModel->GetPlaybackRate(),
  724. gpGlobals->realtime
  725. );
  726. // Now, blend the lower and upper (aim) anims together
  727. pPlayerModel->SetNumAnimOverlays( 2 );
  728. int numOverlays = pPlayerModel->GetNumAnimOverlays();
  729. for ( i=0; i < numOverlays; ++i )
  730. {
  731. C_AnimationLayer *layer = pPlayerModel->GetAnimOverlay( i );
  732. layer->m_flCycle = pPlayerModel->GetCycle();
  733. if ( i )
  734. layer->m_nSequence = pPlayerModel->LookupSequence( pWeaponSequence );
  735. else
  736. layer->m_nSequence = pPlayerModel->LookupSequence( "walk_lower" );
  737. layer->m_flPlaybackRate = 1.0;
  738. layer->m_flWeight = 1.0f;
  739. layer->SetOrder( i );
  740. }
  741. pPlayerModel->FrameAdvance( gpGlobals->frametime );
  742. // Now draw it.
  743. CViewSetup view;
  744. view.x = x;
  745. view.y = y;
  746. view.width = width;
  747. view.height = height;
  748. view.m_bOrtho = false;
  749. view.fov = 54;
  750. view.origin = origin + Vector( -110, -5, -5 );
  751. Vector vMins, vMaxs;
  752. pPlayerModel->C_BaseAnimating::GetRenderBounds( vMins, vMaxs );
  753. view.origin.z += ( vMins.z + vMaxs.z ) * 0.55f;
  754. view.angles.Init();
  755. view.zNear = VIEW_NEARZ;
  756. view.zFar = 1000;
  757. Frustum dummyFrustum;
  758. render->Push3DView( view, 0, NULL, dummyFrustum );
  759. //=============================================================================
  760. // HPE_BEGIN:
  761. // [mhansen] We don't want to light the model in the world. We want it to
  762. // always be lit normal like even if you are standing in a dark (or green) area
  763. // in the world.
  764. //=============================================================================
  765. CMatRenderContextPtr pRenderContext( materials );
  766. pRenderContext->SetLightingOrigin( vec3_origin );
  767. pRenderContext->SetAmbientLight( 0.4, 0.4, 0.4 );
  768. static Vector white[6] =
  769. {
  770. Vector( 0.4, 0.4, 0.4 ),
  771. Vector( 0.4, 0.4, 0.4 ),
  772. Vector( 0.4, 0.4, 0.4 ),
  773. Vector( 0.4, 0.4, 0.4 ),
  774. Vector( 0.4, 0.4, 0.4 ),
  775. Vector( 0.4, 0.4, 0.4 ),
  776. };
  777. g_pStudioRender->SetAmbientLightColors( white );
  778. g_pStudioRender->SetLocalLights( 0, NULL );
  779. modelrender->SuppressEngineLighting( true );
  780. float color[3] = { 1.0f, 1.0f, 1.0f };
  781. render->SetColorModulation( color );
  782. render->SetBlend( 1.0f );
  783. pPlayerModel->DrawModel( STUDIO_RENDER );
  784. if ( pWeaponModel )
  785. {
  786. pWeaponModel->DrawModel( STUDIO_RENDER );
  787. }
  788. modelrender->SuppressEngineLighting( false );
  789. //=============================================================================
  790. // HPE_END
  791. //=============================================================================
  792. render->PopView( dummyFrustum );
  793. }
  794. bool WillPanelBeVisible( vgui::VPANEL hPanel )
  795. {
  796. while ( hPanel )
  797. {
  798. if ( !vgui::ipanel()->IsVisible( hPanel ) )
  799. return false;
  800. hPanel = vgui::ipanel()->GetParent( hPanel );
  801. }
  802. return true;
  803. }
  804. void ClientModeCSNormal::PostRenderVGui()
  805. {
  806. // If the team menu is up, then we will render the model of the character that is currently selected.
  807. for ( int i=0; i < g_ClassImagePanels.Count(); i++ )
  808. {
  809. CCSClassImagePanel *pPanel = g_ClassImagePanels[i];
  810. if ( WillPanelBeVisible( pPanel->GetVPanel() ) )
  811. {
  812. // Ok, we have a visible class image panel.
  813. int x, y, w, h;
  814. pPanel->GetBounds( x, y, w, h );
  815. pPanel->LocalToScreen( x, y );
  816. // Allow for the border.
  817. x += 3;
  818. y += 5;
  819. w -= 2;
  820. h -= 10;
  821. UpdateClassImageEntity( g_ClassImagePanels[i]->m_ModelName, x, y, w, h );
  822. return;
  823. }
  824. }
  825. }
  826. bool ClientModeCSNormal::ShouldDrawViewModel( void )
  827. {
  828. C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer();
  829. if( pPlayer && pPlayer->GetFOV() != CSGameRules()->DefaultFOV() )
  830. {
  831. CWeaponCSBase *pWpn = pPlayer->GetActiveCSWeapon();
  832. if( pWpn && pWpn->HideViewModelWhenZoomed() )
  833. {
  834. return false;
  835. }
  836. }
  837. return BaseClass::ShouldDrawViewModel();
  838. }
  839. bool ClientModeCSNormal::CanRecordDemo( char *errorMsg, int length ) const
  840. {
  841. C_CSPlayer *player = C_CSPlayer::GetLocalCSPlayer();
  842. if ( !player )
  843. {
  844. return true;
  845. }
  846. if ( !player->IsAlive() )
  847. {
  848. return true;
  849. }
  850. // don't start recording while flashed, as it would remove the flash
  851. if ( player->m_flFlashBangTime > gpGlobals->curtime )
  852. {
  853. Q_strncpy( errorMsg, "Cannot record demos while blind.", length );
  854. return false;
  855. }
  856. // don't start recording while smoke grenades are spewing smoke, as the existing smoke would be destroyed
  857. C_BaseEntityIterator it;
  858. C_BaseEntity *ent;
  859. while ( (ent = it.Next()) != NULL )
  860. {
  861. if ( Q_strcmp( ent->GetClassname(), "class C_ParticleSmokeGrenade" ) == 0 )
  862. {
  863. Q_strncpy( errorMsg, "Cannot record demos while a smoke grenade is active.", length );
  864. return false;
  865. }
  866. }
  867. return true;
  868. }
  869. //=============================================================================
  870. // HPE_BEGIN:
  871. // [menglish] Save server information shown to the client in a persistent place
  872. //=============================================================================
  873. void ClientModeCSNormal::SetServerName(wchar_t* name)
  874. {
  875. V_wcsncpy(m_pServerName, name, sizeof( m_pServerName ) );
  876. }
  877. void ClientModeCSNormal::SetMapName(wchar_t* name)
  878. {
  879. V_wcsncpy(m_pMapName, name, sizeof( m_pMapName ) );
  880. }
  881. //=============================================================================
  882. // HPE_END
  883. //=============================================================================
  884. // Receive the PlayerIgnited user message and send out a clientside event for achievements to hook.
  885. void __MsgFunc_MatchEndConditions( bf_read &msg )
  886. {
  887. int iFragLimit = (int) msg.ReadLong();
  888. int iMaxRounds = (int) msg.ReadLong();
  889. int iWinRounds = (int) msg.ReadLong();
  890. int iTimeLimit = (int) msg.ReadLong();
  891. IGameEvent *event = gameeventmanager->CreateEvent( "match_end_conditions" );
  892. if ( event )
  893. {
  894. event->SetInt( "frags", iFragLimit );
  895. event->SetInt( "max_rounds", iMaxRounds );
  896. event->SetInt( "win_rounds", iWinRounds );
  897. event->SetInt( "time", iTimeLimit );
  898. gameeventmanager->FireEventClientSide( event );
  899. }
  900. }