summaryrefslogtreecommitdiff
path: root/game/client/cstrike/clientmode_csnormal.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/cstrike/clientmode_csnormal.cpp')
-rw-r--r--game/client/cstrike/clientmode_csnormal.cpp1108
1 files changed, 1108 insertions, 0 deletions
diff --git a/game/client/cstrike/clientmode_csnormal.cpp b/game/client/cstrike/clientmode_csnormal.cpp
new file mode 100644
index 0000000..87b5cc5
--- /dev/null
+++ b/game/client/cstrike/clientmode_csnormal.cpp
@@ -0,0 +1,1108 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//===========================================================================//
+#include "cbase.h"
+#include "hud.h"
+#include "clientmode_csnormal.h"
+#include "cdll_client_int.h"
+#include "iinput.h"
+#include "vgui/ISurface.h"
+#include "vgui/IPanel.h"
+#include <vgui_controls/AnimationController.h>
+#include "ivmodemanager.h"
+#include "buymenu.h"
+#include "filesystem.h"
+#include "vgui/IVGui.h"
+#include "hud_basechat.h"
+#include "view_shared.h"
+#include "view.h"
+#include "ivrenderview.h"
+#include "cstrikeclassmenu.h"
+#include "model_types.h"
+#include "iefx.h"
+#include "dlight.h"
+#include <imapoverview.h>
+#include "c_playerresource.h"
+#include "c_soundscape.h"
+#include <KeyValues.h>
+#include "text_message.h"
+#include "panelmetaclassmgr.h"
+#include "vguicenterprint.h"
+#include "physpropclientside.h"
+#include "c_weapon__stubs.h"
+#include <engine/IEngineSound.h>
+#include "c_cs_hostage.h"
+#include "buy_presets/buy_presets.h"
+#include "bitbuf.h"
+#include "usermessages.h"
+#include "prediction.h"
+#include "datacache/imdlcache.h"
+//=============================================================================
+// HPE_BEGIN:
+// [tj] Needed to retrieve achievement text
+// [menglish] Need access to message macros
+//=============================================================================
+
+#include "achievementmgr.h"
+#include "hud_macros.h"
+#include "c_plantedc4.h"
+#include "tier1/fmtstr.h"
+#include "history_resource.h"
+#include "cs_client_gamestats.h"
+
+// [tj] We need to forward declare this, since the definition is all inside the implementation file
+class CHudHintDisplay;
+
+//=============================================================================
+// HPE_END
+//=============================================================================
+
+
+void __MsgFunc_MatchEndConditions( bf_read &msg );
+
+class CHudChat;
+
+ConVar default_fov( "default_fov", "90", FCVAR_CHEAT );
+
+IClientMode *g_pClientMode = NULL;
+
+// This is a temporary entity used to render the player's model while drawing the class selection menu.
+CHandle<C_BaseAnimatingOverlay> g_ClassImagePlayer; // player
+CHandle<C_BaseAnimating> g_ClassImageWeapon; // weapon
+
+STUB_WEAPON_CLASS( cycler_weapon, WeaponCycler, C_BaseCombatWeapon );
+STUB_WEAPON_CLASS( weapon_cubemap, WeaponCubemap, C_BaseCombatWeapon );
+
+//-----------------------------------------------------------------------------
+// HACK: the detail sway convars are archive, and default to 0. Existing CS:S players thus have no detail
+// prop sway. We'll force them to DoD's default values for now. What we really need in the long run is
+// a system to apply changes to archived convars' defaults to existing players.
+extern ConVar cl_detail_max_sway;
+extern ConVar cl_detail_avoid_radius;
+extern ConVar cl_detail_avoid_force;
+extern ConVar cl_detail_avoid_recover_speed;
+
+//-----------------------------------------------------------------------------
+ConVar cl_autobuy(
+ "cl_autobuy",
+ "",
+ FCVAR_USERINFO,
+ "The order in which autobuy will attempt to purchase items" );
+
+//-----------------------------------------------------------------------------
+ConVar cl_rebuy(
+ "cl_rebuy",
+ "",
+ FCVAR_USERINFO,
+ "The order in which rebuy will attempt to repurchase items" );
+
+//-----------------------------------------------------------------------------
+void SetBuyData( const ConVar &buyVar, const char *filename )
+{
+ // if we already have autobuy data, don't bother re-parsing the text file
+ if ( *buyVar.GetString() )
+ return;
+
+ // First, look for a mapcycle file in the cfg directory, which is preferred
+ char szRecommendedName[ 256 ];
+ char szResolvedName[ 256 ];
+ V_sprintf_safe( szRecommendedName, "cfg/%s", filename );
+ V_strcpy_safe( szResolvedName, szRecommendedName );
+ if ( filesystem->FileExists( szResolvedName, "GAME" ) )
+ {
+ Msg( "Loading '%s'.\n", szResolvedName );
+ }
+ else
+ {
+ // Check the root
+ V_strcpy_safe( szResolvedName, filename );
+ if ( filesystem->FileExists( szResolvedName, "GAME" ) )
+ {
+ Msg( "Loading '%s' ('%s' was not found.)\n", szResolvedName, szRecommendedName );
+ }
+ else
+ {
+
+ // Try cfg/xxx_default.txt
+ V_strcpy_safe( szResolvedName, szRecommendedName );
+ char *dotTxt = V_stristr( szResolvedName, ".txt" );
+ Assert( dotTxt );
+ if ( dotTxt )
+ {
+ V_strcpy( dotTxt, "_default.txt" );
+ }
+ if ( !filesystem->FileExists( szResolvedName, "GAME" ) )
+ {
+ Warning( "Not loading buy data. Neither '%s' nor %s were found.\n", szResolvedName, szRecommendedName );
+ return;
+ }
+ Msg( "Loading '%s'\n", szResolvedName );
+ }
+ }
+
+ CUtlBuffer buf;
+ if ( !filesystem->ReadFile( szResolvedName, "GAME", buf ) )
+ {
+ // WAT
+ Warning( "Failed to load '%s'.\n", szResolvedName );
+ return;
+ }
+ buf.PutChar('\0');
+
+ char token[256];
+ char buystring[256];
+ V_sprintf_safe( buystring, "setinfo %s \"", buyVar.GetName() );
+
+ const char *pfile = engine->ParseFile( (const char *)buf.Base(), token, sizeof(token) );
+
+ bool first = true;
+
+ while (pfile != NULL)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ Q_strncat(buystring, " ", sizeof(buystring), COPY_ALL_CHARACTERS);
+ }
+
+ Q_strncat(buystring, token, sizeof(buystring), COPY_ALL_CHARACTERS);
+
+ pfile = engine->ParseFile( pfile, token, sizeof(token) );
+ }
+
+ Q_strncat(buystring, "\"", sizeof(buystring), COPY_ALL_CHARACTERS);
+
+ engine->ClientCmd(buystring);
+}
+
+void MsgFunc_KillCam(bf_read &msg)
+{
+ C_CSPlayer *pPlayer = ToCSPlayer( C_BasePlayer::GetLocalPlayer() );
+
+ if ( !pPlayer )
+ return;
+
+ int newMode = msg.ReadByte();
+
+ if ( newMode != g_nKillCamMode )
+ {
+#if !defined( NO_ENTITY_PREDICTION )
+ if ( g_nKillCamMode == OBS_MODE_NONE )
+ {
+ // kill cam is switch on, turn off prediction
+ g_bForceCLPredictOff = true;
+ }
+ else if ( newMode == OBS_MODE_NONE )
+ {
+ // kill cam is switched off, restore old prediction setting is we switch back to normal mode
+ g_bForceCLPredictOff = false;
+ }
+#endif
+ g_nKillCamMode = newMode;
+ }
+
+ g_nKillCamTarget1 = msg.ReadByte();
+ g_nKillCamTarget2 = msg.ReadByte();
+}
+
+// --------------------------------------------------------------------------------- //
+// CCSModeManager.
+// --------------------------------------------------------------------------------- //
+
+class CCSModeManager : public IVModeManager
+{
+public:
+ virtual void Init();
+ virtual void SwitchMode( bool commander, bool force ) {}
+ virtual void LevelInit( const char *newmap );
+ virtual void LevelShutdown( void );
+ virtual void ActivateMouse( bool isactive ) {}
+};
+
+static CCSModeManager g_ModeManager;
+IVModeManager *modemanager = ( IVModeManager * )&g_ModeManager;
+
+// --------------------------------------------------------------------------------- //
+// CCSModeManager implementation.
+// --------------------------------------------------------------------------------- //
+
+#define SCREEN_FILE "scripts/vgui_screens.txt"
+
+void CCSModeManager::Init()
+{
+ g_pClientMode = GetClientModeNormal();
+
+ PanelMetaClassMgr()->LoadMetaClassDefinitionFile( SCREEN_FILE );
+}
+
+void CCSModeManager::LevelInit( const char *newmap )
+{
+ g_pClientMode->LevelInit( newmap );
+
+ SetBuyData( cl_autobuy, "autobuy.txt" );
+ SetBuyData( cl_rebuy, "rebuy.txt" );
+
+#if !defined( NO_ENTITY_PREDICTION )
+ if ( g_nKillCamMode > OBS_MODE_NONE )
+ {
+ g_bForceCLPredictOff = false;
+ }
+#endif
+
+ g_nKillCamMode = OBS_MODE_NONE;
+ g_nKillCamTarget1 = 0;
+ g_nKillCamTarget2 = 0;
+
+ // HACK: the detail sway convars are archive, and default to 0. Existing CS:S players thus have no detail
+ // prop sway. We'll force them to DoD's default values for now.
+ if ( !cl_detail_max_sway.GetFloat() &&
+ !cl_detail_avoid_radius.GetFloat() &&
+ !cl_detail_avoid_force.GetFloat() &&
+ !cl_detail_avoid_recover_speed.GetFloat() )
+ {
+ cl_detail_max_sway.SetValue( "5" );
+ cl_detail_avoid_radius.SetValue( "64" );
+ cl_detail_avoid_force.SetValue( "0.4" );
+ cl_detail_avoid_recover_speed.SetValue( "0.25" );
+ }
+}
+
+void CCSModeManager::LevelShutdown( void )
+{
+ g_pClientMode->LevelShutdown();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+ClientModeCSNormal::ClientModeCSNormal()
+{
+ HOOK_MESSAGE( MatchEndConditions );
+}
+
+void ClientModeCSNormal::Init()
+{
+ BaseClass::Init();
+
+ ListenForGameEvent( "round_end" );
+ ListenForGameEvent( "round_start" );
+ ListenForGameEvent( "player_team" );
+ ListenForGameEvent( "player_death" );
+ ListenForGameEvent( "bomb_planted" );
+ ListenForGameEvent( "bomb_exploded" );
+ ListenForGameEvent( "bomb_defused" );
+ ListenForGameEvent( "hostage_killed" );
+ ListenForGameEvent( "hostage_hurt" );
+
+ usermessages->HookMessage( "KillCam", MsgFunc_KillCam );
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Add the shared HUD elements to the render groups responsible for hiding
+ // conflicting UI
+ //=============================================================================
+ CHudElement* hintBox = (CHudElement*)GET_HUDELEMENT( CHudHintDisplay );
+ if (hintBox)
+ {
+ hintBox->RegisterForRenderGroup("hide_for_scoreboard");
+ hintBox->RegisterForRenderGroup("hide_for_round_panel");
+ }
+
+
+ CHudElement* historyResource = (CHudElement*)GET_HUDELEMENT( CHudHistoryResource );
+ if (historyResource)
+ {
+ historyResource->RegisterForRenderGroup("hide_for_scoreboard");
+ }
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+}
+
+void ClientModeCSNormal::InitViewport()
+{
+ BaseClass::InitViewport();
+
+ m_pViewport = new CounterStrikeViewport();
+ m_pViewport->Start( gameuifuncs, gameeventmanager );
+}
+
+
+void ClientModeCSNormal::Update()
+{
+ BaseClass::Update();
+
+ // Override the hud's visibility if this is a logo (like E3 demo) map.
+ if ( CSGameRules() && CSGameRules()->IsLogoMap() )
+ m_pViewport->SetVisible( false );
+}
+
+
+/*
+void ClientModeCSNormal::UpdateSpectatorMode( void )
+{
+ C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
+
+ if ( !pPlayer )
+ return;
+
+ IMapOverview * overviewmap = m_pViewport->GetMapOverviewInterface();
+
+ if ( !overviewmap )
+ return;
+
+ overviewmap->SetTime( gpGlobals->curtime );
+
+ int obs_mode = pPlayer->GetObserverMode();
+
+ if ( obs_mode < OBS_MODE_IN_EYE )
+ return;
+
+ Vector worldpos = pPlayer->GetLocalOrigin();
+ QAngle angles; engine->GetViewAngles( angles );
+
+ C_BaseEntity *target = pPlayer->GetObserverTarget();
+
+ if ( target && (obs_mode == OBS_MODE_IN_EYE || obs_mode == OBS_MODE_CHASE) )
+ {
+ worldpos = target->GetAbsOrigin();
+
+ if ( obs_mode == OBS_MODE_IN_EYE )
+ {
+ angles = target->GetAbsAngles();
+ }
+ }
+
+ Vector2D mappos = overviewmap->WorldToMap( worldpos );
+
+ overviewmap->SetCenter( mappos );
+ overviewmap->SetAngle( angles.y );
+
+ for ( int i = 1; i<= MAX_PLAYERS; i++)
+ {
+ C_BaseEntity *ent = ClientEntityList().GetEnt( i );
+
+ if ( !ent || !ent->IsPlayer() )
+ continue;
+
+ C_BasePlayer *p = ToBasePlayer( ent );
+
+ // update position of active players in our PVS
+ Vector position = p->GetAbsOrigin();
+ QAngle angle = p->GetAbsAngles();
+
+ if ( p->IsDormant() )
+ {
+ // if player is not in PVS, use PlayerResources data
+ position = g_PR->GetPosition( i );
+ angles[1] = g_PR->GetViewAngle( i );
+ }
+
+ overviewmap->SetPlayerPositions( i-1, position, angles );
+ }
+} */
+
+//-----------------------------------------------------------------------------
+// Purpose: We've received a keypress from the engine. Return 1 if the engine is allowed to handle it.
+//-----------------------------------------------------------------------------
+int ClientModeCSNormal::KeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding )
+{
+ // don't process input in LogoMaps
+ if( CSGameRules() && CSGameRules()->IsLogoMap() )
+ return 1;
+
+ return BaseClass::KeyInput( down, keynum, pszCurrentBinding );
+}
+
+
+
+IClientMode *GetClientModeNormal()
+{
+ static ClientModeCSNormal g_ClientModeNormal;
+ return &g_ClientModeNormal;
+}
+
+
+ClientModeCSNormal* GetClientModeCSNormal()
+{
+ Assert( dynamic_cast< ClientModeCSNormal* >( GetClientModeNormal() ) );
+
+ return static_cast< ClientModeCSNormal* >( GetClientModeNormal() );
+}
+
+float ClientModeCSNormal::GetViewModelFOV( void )
+{
+ return 74.0f;
+}
+
+int ClientModeCSNormal::GetDeathMessageStartHeight( void )
+{
+ return m_pViewport->GetDeathMessageStartHeight();
+}
+
+void ClientModeCSNormal::FireGameEvent( IGameEvent *event )
+{
+ CBaseHudChat *pHudChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat );
+ C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
+ CLocalPlayerFilter filter;
+
+ if ( !pLocalPlayer || !pHudChat )
+ return;
+
+ const char *eventname = event->GetName();
+
+ if ( !eventname || !eventname[0] )
+ return;
+
+ if ( Q_strcmp( "round_start", eventname ) == 0 )
+ {
+ // recreate all client side physics props
+ C_PhysPropClientside::RecreateAll();
+
+ // remove hostage ragdolls
+ for ( int i=0; i<g_HostageRagdolls.Count(); ++i )
+ {
+ // double-check that the EHANDLE is still valid
+ if ( g_HostageRagdolls[i] )
+ {
+ g_HostageRagdolls[i]->Release();
+ }
+ }
+ g_HostageRagdolls.RemoveAll();
+
+ // Just tell engine to clear decals
+ engine->ClientCmd( "r_cleardecals\n" );
+
+ //stop any looping sounds
+ enginesound->StopAllSounds( true );
+
+ Soundscape_OnStopAllSounds(); // Tell the soundscape system.
+ }
+
+
+ else if ( Q_strcmp( "round_end", eventname ) == 0 )
+ {
+ int winningTeam = event->GetInt("winner");
+ int reason = event->GetInt("reason");
+
+ // play endround announcer sound
+ if ( winningTeam == TEAM_CT )
+ {
+ if ( reason == Bomb_Defused )
+ {
+ C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Event.BombDefused");
+ }
+ else
+ {
+ C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Event.CTWin");
+ }
+ }
+ else if ( winningTeam == TEAM_TERRORIST )
+ {
+ C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Event.TERWin");
+ }
+ else
+ {
+ C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Event.RoundDraw");
+ }
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [pfreese] Only show centerprint message for game commencing; the rest of
+ // these messages are handled by the end-of-round panel.
+ // [Forrest] Show all centerprint messages if the end-of-round panel is disabled.
+ //=============================================================================
+ static ConVarRef sv_nowinpanel( "sv_nowinpanel" );
+ static ConVarRef cl_nowinpanel( "cl_nowinpanel" );
+ if ( reason == Game_Commencing || sv_nowinpanel.GetBool() || cl_nowinpanel.GetBool() )
+ {
+ internalCenterPrint->Print( hudtextmessage->LookupString( event->GetString("message") ) );
+
+ // we are starting a new round; clear the current match stats
+ g_CSClientGameStats.ResetMatchStats();
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+ }
+
+ else if ( Q_strcmp( "player_team", eventname ) == 0 )
+ {
+ CBaseHudChat *pHudChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat );
+ C_BasePlayer *pPlayer = USERID2PLAYER( event->GetInt("userid") );
+
+ if ( !pPlayer )
+ return;
+
+ bool bDisconnected = event->GetBool("disconnect");
+
+ if ( bDisconnected )
+ return;
+
+ int iTeam = event->GetInt("team");
+
+ if ( pPlayer->IsLocalPlayer() )
+ {
+ // that's me
+ pPlayer->TeamChange( iTeam );
+ }
+
+ if ( iTeam == TEAM_SPECTATOR )
+ pHudChat->Printf( CHAT_FILTER_NONE, hudtextmessage->LookupString( "#Game_join_spectators" ), pPlayer->GetPlayerName() );
+ else if ( iTeam == TEAM_TERRORIST )
+ pHudChat->Printf( CHAT_FILTER_NONE, hudtextmessage->LookupString( "#Game_join_terrorist" ), pPlayer->GetPlayerName() );
+ else if ( iTeam == TEAM_CT )
+ pHudChat->Printf( CHAT_FILTER_NONE, hudtextmessage->LookupString( "#Game_join_ct" ), pPlayer->GetPlayerName() );
+ }
+
+ else if ( Q_strcmp( "bomb_planted", eventname ) == 0 )
+ {
+ //C_BasePlayer *pPlayer = USERID2PLAYER( event->GetInt("userid") );
+
+ // show centerprint message
+ internalCenterPrint->Print( "#Cstrike_TitlesTXT_Bomb_Planted" );
+
+ // play sound
+ C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Event.BombPlanted") ;
+ }
+
+ else if ( Q_strcmp( "bomb_defused", eventname ) == 0 )
+ {
+ // C_BasePlayer *pPlayer = USERID2PLAYER( event->GetInt("userid") );
+ }
+ //=============================================================================
+ // HPE_BEGIN:
+ // [menglish] Tell the client side bomb that the bomb has exploding here creating the explosion particle effect
+ //=============================================================================
+
+ else if ( Q_strcmp( "bomb_exploded", eventname ) == 0 )
+ {
+ if ( g_PlantedC4s.Count() > 0 )
+ {
+ // bomb is planted
+ C_PlantedC4 *pC4 = g_PlantedC4s[0];
+ pC4->Explode();
+ }
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ else if ( Q_strcmp( "hostage_killed", eventname ) == 0 )
+ {
+ // play sound for spectators and CTs
+ if ( pLocalPlayer->IsObserver() || (pLocalPlayer->GetTeamNumber() == TEAM_CT) )
+ {
+ C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Event.HostageKilled") ;
+ }
+
+ // Show warning to killer
+ if ( pLocalPlayer->GetUserID() == event->GetInt("userid") )
+ {
+ internalCenterPrint->Print( "#Cstrike_TitlesTXT_Killed_Hostage" );
+ }
+ }
+
+ else if ( Q_strcmp( "hostage_hurt", eventname ) == 0 )
+ {
+ // Let the loacl player know he harmed a hostage
+ if ( pLocalPlayer->GetUserID() == event->GetInt("userid") )
+ {
+ internalCenterPrint->Print( "#Cstrike_TitlesTXT_Injured_Hostage" );
+ }
+ }
+
+ else if ( Q_strcmp( "player_death", eventname ) == 0 )
+ {
+ C_BasePlayer *pPlayer = USERID2PLAYER( event->GetInt("userid") );
+
+ C_CSPlayer* csPlayer = ToCSPlayer(pPlayer);
+ if (csPlayer)
+ {
+ csPlayer->ClearSoundEvents();
+ }
+
+ if ( pPlayer == C_BasePlayer::GetLocalPlayer() )
+ {
+ // we just died, hide any buy panels
+ gViewPortInterface->ShowPanel( PANEL_BUY, false );
+ gViewPortInterface->ShowPanel( PANEL_BUY_CT, false );
+ gViewPortInterface->ShowPanel( PANEL_BUY_TER, false );
+ gViewPortInterface->ShowPanel( PANEL_BUY_EQUIP_CT, false );
+ gViewPortInterface->ShowPanel( PANEL_BUY_EQUIP_TER, false );
+ }
+ }
+
+ else if ( Q_strcmp( "player_changename", eventname ) == 0 )
+ {
+ return; // server sends a colorized text string for this
+ }
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] We handle this here instead of in the base class
+ // The reason is that we don't use string tables to localize.
+ // Instead, we use the steam localization mechanism.
+ //
+ // [dwenger] Remove dependency on stats system for name of achievement.
+ //=============================================================================
+
+ else if ( Q_strcmp( "achievement_earned", eventname ) == 0 )
+ {
+ CBaseHudChat *hudChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat );
+ int iPlayerIndex = event->GetInt( "player" );
+ C_BasePlayer *pPlayer = UTIL_PlayerByIndex( iPlayerIndex );
+ int iAchievement = event->GetInt( "achievement" );
+
+ if ( !hudChat || !pPlayer )
+ return;
+
+
+ CAchievementMgr *pAchievementMgr = dynamic_cast<CAchievementMgr *>( engine->GetAchievementMgr() );
+ if ( !pAchievementMgr )
+ return;
+
+ IAchievement *pAchievement = pAchievementMgr->GetAchievementByID( iAchievement );
+ if ( pAchievement )
+ {
+ if ( !pPlayer->IsDormant() && pPlayer->ShouldAnnounceAchievement() )
+ {
+ pPlayer->SetNextAchievementAnnounceTime( gpGlobals->curtime + ACHIEVEMENT_ANNOUNCEMENT_MIN_TIME );
+
+ //Do something for the player - Actually we should probably do this client-side when the achievement is first earned.
+ if (pPlayer->IsLocalPlayer())
+ {
+ }
+ pPlayer->OnAchievementAchieved( iAchievement );
+ }
+
+ if ( g_PR )
+ {
+ wchar_t wszPlayerName[MAX_PLAYER_NAME_LENGTH];
+ g_pVGuiLocalize->ConvertANSIToUnicode( g_PR->GetPlayerName( iPlayerIndex ), wszPlayerName, sizeof( wszPlayerName ) );
+
+ wchar_t achievementName[1024];
+ const wchar_t* constAchievementName = &achievementName[0];
+
+ constAchievementName = ACHIEVEMENT_LOCALIZED_NAME( pAchievement );
+
+ if (constAchievementName)
+ {
+ wchar_t wszLocalizedString[128];
+ g_pVGuiLocalize->ConstructString( wszLocalizedString, sizeof( wszLocalizedString ), g_pVGuiLocalize->Find( "#Achievement_Earned" ), 2, wszPlayerName, constAchievementName/*wszAchievementString*/ );
+
+ char szLocalized[128];
+ g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalizedString, szLocalized, sizeof( szLocalized ) );
+
+ hudChat->ChatPrintf( iPlayerIndex, CHAT_FILTER_ACHIEVEMENT, "%s", szLocalized );
+
+ /*
+ if (pPlayer->IsLocalPlayer())
+ {
+ char achievementDescription[1024];
+ const char* constAchievementDescription = &achievementDescription[0];
+
+ constAchievementDescription = pUserStats->GetAchievementDisplayAttribute( pAchievement->GetName(), "desc" );
+ hudChat->ChatPrintf( iPlayerIndex, CHAT_FILTER_ACHIEVEMENT, "(%s)", constAchievementDescription );
+ }
+ */
+ }
+ }
+ }
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+
+ else
+ {
+ BaseClass::FireGameEvent( event );
+ }
+}
+
+
+void RemoveClassImageEntity()
+{
+ C_BaseAnimating *pEnt = g_ClassImagePlayer.Get();
+ if ( pEnt )
+ {
+ pEnt->Remove();
+ g_ClassImagePlayer = NULL;
+ }
+
+ pEnt = g_ClassImageWeapon.Get();
+ if ( pEnt )
+ {
+ pEnt->Remove();
+ g_ClassImagePlayer = NULL;
+ }
+}
+
+
+bool ShouldRecreateClassImageEntity( C_BaseAnimating *pEnt, const char *pNewModelName )
+{
+ if ( !pNewModelName || !pNewModelName[0] )
+ return false;
+
+ if ( !pEnt )
+ return true;
+
+ const model_t *pModel = pEnt->GetModel();
+
+ if ( !pModel )
+ return true;
+
+ const char *pName = modelinfo->GetModelName( pModel );
+ if ( !pName )
+ return true;
+
+ // reload only if names are different
+ const char *pNameNoPath = V_UnqualifiedFileName( pName );
+ const char *pNewModelNameNoPath = V_UnqualifiedFileName( pNewModelName );
+ return( Q_stricmp( pNameNoPath, pNewModelNameNoPath ) != 0 );
+}
+
+
+void UpdateClassImageEntity(
+ const char *pModelName,
+ int x, int y, int width, int height )
+{
+ C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
+
+ if ( !pLocalPlayer )
+ return;
+
+ MDLCACHE_CRITICAL_SECTION();
+
+ const char *pWeaponName = "models/weapons/w_rif_ak47.mdl";
+ const char *pWeaponSequence = "Walk_Upper_AK";
+
+ int i;
+ for ( i=0; i<CTPlayerModels.Count(); ++i )
+ {
+ if ( Q_strcasecmp( pModelName, CTPlayerModels[i] ) == 0 )
+ {
+ // give CTs a M4
+ pWeaponName = "models/weapons/w_rif_m4a1.mdl";
+ pWeaponSequence = "Walk_Upper_M4";
+ break;
+ }
+ }
+
+ if ( pLocalPlayer->IsAlive() && pLocalPlayer->GetActiveWeapon() )
+ {
+ C_WeaponCSBase *weapon = dynamic_cast< C_WeaponCSBase * >(pLocalPlayer->GetActiveWeapon());
+ if ( weapon )
+ {
+ pWeaponName = weapon->GetWorldModel();
+ pWeaponSequence = VarArgs("Walk_Upper_%s", weapon->GetCSWpnData().m_szAnimExtension);
+ }
+ }
+
+ C_BaseAnimatingOverlay *pPlayerModel = g_ClassImagePlayer.Get();
+
+ // Does the entity even exist yet?
+ bool recreatePlayer = ShouldRecreateClassImageEntity( pPlayerModel, pModelName );
+ if ( recreatePlayer )
+ {
+ if ( pPlayerModel )
+ pPlayerModel->Remove();
+
+ pPlayerModel = new C_BaseAnimatingOverlay;
+ pPlayerModel->InitializeAsClientEntity( pModelName, RENDER_GROUP_OPAQUE_ENTITY );
+ pPlayerModel->AddEffects( EF_NODRAW ); // don't let the renderer draw the model normally
+
+ // let player walk ahead
+ pPlayerModel->SetSequence( pPlayerModel->LookupSequence( "walk_lower" ) );
+ pPlayerModel->SetPoseParameter( "move_yaw", 0.0f ); // move_yaw
+ pPlayerModel->SetPoseParameter( "body_pitch", 10.0f ); // body_pitch, look down a bit
+ pPlayerModel->SetPoseParameter( "body_yaw", 0.0f ); // body_yaw
+ pPlayerModel->SetPoseParameter( "move_y", 0.0f ); // move_y
+ pPlayerModel->SetPoseParameter( "move_x", 1.0f ); // move_x, walk forward
+ pPlayerModel->m_flAnimTime = gpGlobals->curtime;
+
+ g_ClassImagePlayer = pPlayerModel;
+ }
+
+ C_BaseAnimating *pWeaponModel = g_ClassImageWeapon.Get();
+
+ // Does the entity even exist yet?
+ if ( recreatePlayer || ShouldRecreateClassImageEntity( pWeaponModel, pWeaponName ) )
+ {
+ if ( pWeaponModel )
+ pWeaponModel->Remove();
+
+ pWeaponModel = new C_BaseAnimating;
+ pWeaponModel->InitializeAsClientEntity( pWeaponName, RENDER_GROUP_OPAQUE_ENTITY );
+ pWeaponModel->AddEffects( EF_NODRAW ); // don't let the renderer draw the model normally
+ pWeaponModel->FollowEntity( pPlayerModel ); // attach to player model
+ pWeaponModel->m_flAnimTime = gpGlobals->curtime;
+ g_ClassImageWeapon = pWeaponModel;
+ }
+
+ Vector origin = pLocalPlayer->EyePosition();
+ Vector lightOrigin = origin;
+
+ // find a spot inside the world for the dlight's origin, or it won't illuminate the model
+ Vector testPos( origin.x - 100, origin.y, origin.z + 100 );
+ trace_t tr;
+ UTIL_TraceLine( origin, testPos, MASK_OPAQUE, pLocalPlayer, COLLISION_GROUP_NONE, &tr );
+ if ( tr.fraction == 1.0f )
+ {
+ lightOrigin = tr.endpos;
+ }
+ else
+ {
+ // Now move the model away so we get the correct illumination
+ lightOrigin = tr.endpos + Vector( 1, 0, -1 ); // pull out from the solid
+ Vector start = lightOrigin;
+ Vector end = lightOrigin + Vector( 100, 0, -100 );
+ UTIL_TraceLine( start, end, MASK_OPAQUE, pLocalPlayer, COLLISION_GROUP_NONE, &tr );
+ origin = tr.endpos;
+ }
+
+ // move player model in front of our view
+ pPlayerModel->SetAbsOrigin( origin );
+ pPlayerModel->SetAbsAngles( QAngle( 0, 210, 0 ) );
+
+ // wacky hacky, set upper body animation
+ pPlayerModel->m_SequenceTransitioner.CheckForSequenceChange(
+ pPlayerModel->GetModelPtr(),
+ pPlayerModel->LookupSequence( "walk_lower" ),
+ false,
+ true
+ );
+ pPlayerModel->m_SequenceTransitioner.UpdateCurrent(
+ pPlayerModel->GetModelPtr(),
+ pPlayerModel->LookupSequence( "walk_lower" ),
+ pPlayerModel->GetCycle(),
+ pPlayerModel->GetPlaybackRate(),
+ gpGlobals->realtime
+ );
+
+ // Now, blend the lower and upper (aim) anims together
+ pPlayerModel->SetNumAnimOverlays( 2 );
+ int numOverlays = pPlayerModel->GetNumAnimOverlays();
+ for ( i=0; i < numOverlays; ++i )
+ {
+ C_AnimationLayer *layer = pPlayerModel->GetAnimOverlay( i );
+
+ layer->m_flCycle = pPlayerModel->GetCycle();
+ if ( i )
+ layer->m_nSequence = pPlayerModel->LookupSequence( pWeaponSequence );
+ else
+ layer->m_nSequence = pPlayerModel->LookupSequence( "walk_lower" );
+
+ layer->m_flPlaybackRate = 1.0;
+ layer->m_flWeight = 1.0f;
+ layer->SetOrder( i );
+ }
+
+ pPlayerModel->FrameAdvance( gpGlobals->frametime );
+
+ // Now draw it.
+ CViewSetup view;
+ view.x = x;
+ view.y = y;
+ view.width = width;
+ view.height = height;
+
+ view.m_bOrtho = false;
+ view.fov = 54;
+
+ view.origin = origin + Vector( -110, -5, -5 );
+
+ Vector vMins, vMaxs;
+ pPlayerModel->C_BaseAnimating::GetRenderBounds( vMins, vMaxs );
+ view.origin.z += ( vMins.z + vMaxs.z ) * 0.55f;
+
+ view.angles.Init();
+ view.zNear = VIEW_NEARZ;
+ view.zFar = 1000;
+
+ Frustum dummyFrustum;
+ render->Push3DView( view, 0, NULL, dummyFrustum );
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [mhansen] We don't want to light the model in the world. We want it to
+ // always be lit normal like even if you are standing in a dark (or green) area
+ // in the world.
+ //=============================================================================
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->SetLightingOrigin( vec3_origin );
+ pRenderContext->SetAmbientLight( 0.4, 0.4, 0.4 );
+
+ static Vector white[6] =
+ {
+ Vector( 0.4, 0.4, 0.4 ),
+ Vector( 0.4, 0.4, 0.4 ),
+ Vector( 0.4, 0.4, 0.4 ),
+ Vector( 0.4, 0.4, 0.4 ),
+ Vector( 0.4, 0.4, 0.4 ),
+ Vector( 0.4, 0.4, 0.4 ),
+ };
+
+ g_pStudioRender->SetAmbientLightColors( white );
+ g_pStudioRender->SetLocalLights( 0, NULL );
+
+ modelrender->SuppressEngineLighting( true );
+ float color[3] = { 1.0f, 1.0f, 1.0f };
+ render->SetColorModulation( color );
+ render->SetBlend( 1.0f );
+ pPlayerModel->DrawModel( STUDIO_RENDER );
+
+ if ( pWeaponModel )
+ {
+ pWeaponModel->DrawModel( STUDIO_RENDER );
+ }
+
+ modelrender->SuppressEngineLighting( false );
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ render->PopView( dummyFrustum );
+}
+
+
+bool WillPanelBeVisible( vgui::VPANEL hPanel )
+{
+ while ( hPanel )
+ {
+ if ( !vgui::ipanel()->IsVisible( hPanel ) )
+ return false;
+
+ hPanel = vgui::ipanel()->GetParent( hPanel );
+ }
+ return true;
+}
+
+
+
+void ClientModeCSNormal::PostRenderVGui()
+{
+ // If the team menu is up, then we will render the model of the character that is currently selected.
+ for ( int i=0; i < g_ClassImagePanels.Count(); i++ )
+ {
+ CCSClassImagePanel *pPanel = g_ClassImagePanels[i];
+ if ( WillPanelBeVisible( pPanel->GetVPanel() ) )
+ {
+ // Ok, we have a visible class image panel.
+ int x, y, w, h;
+ pPanel->GetBounds( x, y, w, h );
+ pPanel->LocalToScreen( x, y );
+
+ // Allow for the border.
+ x += 3;
+ y += 5;
+ w -= 2;
+ h -= 10;
+
+ UpdateClassImageEntity( g_ClassImagePanels[i]->m_ModelName, x, y, w, h );
+ return;
+ }
+ }
+}
+
+bool ClientModeCSNormal::ShouldDrawViewModel( void )
+{
+ C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer();
+
+ if( pPlayer && pPlayer->GetFOV() != CSGameRules()->DefaultFOV() )
+ {
+ CWeaponCSBase *pWpn = pPlayer->GetActiveCSWeapon();
+
+ if( pWpn && pWpn->HideViewModelWhenZoomed() )
+ {
+ return false;
+ }
+ }
+
+ return BaseClass::ShouldDrawViewModel();
+}
+
+
+bool ClientModeCSNormal::CanRecordDemo( char *errorMsg, int length ) const
+{
+ C_CSPlayer *player = C_CSPlayer::GetLocalCSPlayer();
+ if ( !player )
+ {
+ return true;
+ }
+
+ if ( !player->IsAlive() )
+ {
+ return true;
+ }
+
+ // don't start recording while flashed, as it would remove the flash
+ if ( player->m_flFlashBangTime > gpGlobals->curtime )
+ {
+ Q_strncpy( errorMsg, "Cannot record demos while blind.", length );
+ return false;
+ }
+
+ // don't start recording while smoke grenades are spewing smoke, as the existing smoke would be destroyed
+ C_BaseEntityIterator it;
+ C_BaseEntity *ent;
+ while ( (ent = it.Next()) != NULL )
+ {
+ if ( Q_strcmp( ent->GetClassname(), "class C_ParticleSmokeGrenade" ) == 0 )
+ {
+ Q_strncpy( errorMsg, "Cannot record demos while a smoke grenade is active.", length );
+ return false;
+ }
+ }
+
+ return true;
+}
+
+//=============================================================================
+// HPE_BEGIN:
+// [menglish] Save server information shown to the client in a persistent place
+//=============================================================================
+
+void ClientModeCSNormal::SetServerName(wchar_t* name)
+{
+ V_wcsncpy(m_pServerName, name, sizeof( m_pServerName ) );
+}
+
+void ClientModeCSNormal::SetMapName(wchar_t* name)
+{
+ V_wcsncpy(m_pMapName, name, sizeof( m_pMapName ) );
+}
+
+//=============================================================================
+// HPE_END
+//=============================================================================
+
+// Receive the PlayerIgnited user message and send out a clientside event for achievements to hook.
+void __MsgFunc_MatchEndConditions( bf_read &msg )
+{
+ int iFragLimit = (int) msg.ReadLong();
+ int iMaxRounds = (int) msg.ReadLong();
+ int iWinRounds = (int) msg.ReadLong();
+ int iTimeLimit = (int) msg.ReadLong();
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "match_end_conditions" );
+ if ( event )
+ {
+ event->SetInt( "frags", iFragLimit );
+ event->SetInt( "max_rounds", iMaxRounds );
+ event->SetInt( "win_rounds", iWinRounds );
+ event->SetInt( "time", iTimeLimit );
+ gameeventmanager->FireEventClientSide( event );
+ }
+}