mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-22 14:16:50 +00:00
1109 lines
32 KiB
C++
1109 lines
32 KiB
C++
//========= 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 );
|
|
}
|
|
}
|