diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/cstrike/clientmode_csnormal.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/client/cstrike/clientmode_csnormal.cpp')
| -rw-r--r-- | game/client/cstrike/clientmode_csnormal.cpp | 1108 |
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 ); + } +} |