You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1108 lines
32 KiB
1108 lines
32 KiB
//========= 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 ); |
|
} |
|
}
|
|
|