|
|
//====== Copyright � 1996-2004, Valve Corporation, All rights reserved. =======
//
// Purpose:
//
//=============================================================================
#include "cbase.h"
#include "hud.h"
#include "hudelement.h"
#include "hud_macros.h"
#include "iclientmode.h"
#include "vgui_controls/AnimationController.h"
#include "vgui_controls/Label.h"
#include "vgui/ILocalize.h"
#include "vgui/ISurface.h"
#include "text_message.h"
#include "c_baseplayer.h"
#include "IGameUIFuncs.h"
#include "inputsystem/iinputsystem.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Purpose: Displays hints across the center of the screen
//-----------------------------------------------------------------------------
class CHudHintDisplay : public vgui::Panel, public CHudElement { DECLARE_CLASS_SIMPLE( CHudHintDisplay, vgui::Panel );
public: explicit CHudHintDisplay( const char *pElementName );
void Init(); void Reset(); bool MsgFunc_HintText( const CCSUsrMsg_HintText &msg ); void FireGameEvent( IGameEvent * event);
bool SetHintText( wchar_t *text ); void LocalizeAndDisplay( const char *pszHudTxtMsg, const char *szRawString );
virtual void PerformLayout();
virtual bool ShouldDraw() { // [jason] Moving into Scaleform: sfhudinfopanel
#if defined( CSTRIKE15 )
return false; #else
return Super::ShouldDraw(); #endif
}
CUserMessageBinder m_UMCMsgHintText;
protected: virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); virtual void OnThink();
protected: vgui::HFont m_hFont; Color m_bgColor; vgui::Label *m_pLabel; CUtlVector<vgui::Label *> m_Labels; CPanelAnimationVarAliasType( int, m_iTextX, "text_xpos", "8", "proportional_int" ); CPanelAnimationVarAliasType( int, m_iTextY, "text_ypos", "8", "proportional_int" ); CPanelAnimationVarAliasType( int, m_iCenterX, "center_x", "0", "proportional_int" ); CPanelAnimationVarAliasType( int, m_iCenterY, "center_y", "0", "proportional_int" );
bool m_bLastLabelUpdateHack; CPanelAnimationVar( float, m_flLabelSizePercentage, "HintSize", "0" ); };
DECLARE_HUDELEMENT( CHudHintDisplay ); DECLARE_HUD_MESSAGE( CHudHintDisplay, HintText );
#define MAX_HINT_STRINGS 5
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CHudHintDisplay::CHudHintDisplay( const char *pElementName ) : BaseClass(NULL, "HudHintDisplay"), CHudElement( pElementName ) { vgui::Panel *pParent = GetClientMode()->GetViewport(); SetParent( pParent ); SetVisible( false ); m_pLabel = new vgui::Label( this, "HudHintDisplayLabel", "" ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudHintDisplay::Init() { HOOK_HUD_MESSAGE( CHudHintDisplay, HintText );
// listen for client side events
ListenForGameEvent( "player_hintmessage" ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudHintDisplay::Reset() { SetHintText( NULL ); GetClientMode()->GetViewportAnimationController()->StartAnimationSequence( "HintMessageHide" ); m_bLastLabelUpdateHack = true; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudHintDisplay::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme );
SetFgColor( GetSchemeColor("HintMessageFg", pScheme) ); m_hFont = pScheme->GetFont( "HudHintText", true ); m_pLabel->SetBgColor( GetSchemeColor("HintMessageBg", pScheme) ); m_pLabel->SetPaintBackgroundType( 2 ); m_pLabel->SetSize( 0, GetTall() ); // Start tiny, it'll grow.
}
//-----------------------------------------------------------------------------
// Purpose: Sets the hint text, replacing variables as necessary
//-----------------------------------------------------------------------------
bool CHudHintDisplay::SetHintText( wchar_t *text ) { // clear the existing text
for (int i = 0; i < m_Labels.Count(); i++) { m_Labels[i]->MarkForDeletion(); } m_Labels.RemoveAll();
wchar_t *p = text;
while ( p ) { wchar_t *line = p; wchar_t *end = wcschr( p, L'\n' ); int linelengthbytes = 0; if ( end ) { //*end = 0; //eek
p = end+1; linelengthbytes = ( end - line ) * 2; } else { p = NULL; }
// replace any key references with bound keys
wchar_t buf[512]; UTIL_ReplaceKeyBindings( line, linelengthbytes, buf, sizeof( buf ) );
// put it in a label
vgui::Label *label = vgui::SETUP_PANEL(new vgui::Label(this, NULL, buf)); label->SetFont( m_hFont ); label->SetPaintBackgroundEnabled( false ); label->SetPaintBorderEnabled( false ); label->SizeToContents(); label->SetContentAlignment( vgui::Label::a_west ); label->SetFgColor( GetFgColor() ); m_Labels.AddToTail( vgui::SETUP_PANEL(label) ); }
InvalidateLayout( true );
return true; }
//-----------------------------------------------------------------------------
// Purpose: Resizes the label
//-----------------------------------------------------------------------------
void CHudHintDisplay::PerformLayout() { BaseClass::PerformLayout(); int i;
int wide, tall; GetSize( wide, tall );
// find the widest line
int iDesiredLabelWide = 0; for ( i=0; i < m_Labels.Count(); ++i ) { iDesiredLabelWide = MAX( iDesiredLabelWide, m_Labels[i]->GetWide() ); }
// find the total height
int fontTall = vgui::surface()->GetFontTall( m_hFont ); int labelTall = fontTall * m_Labels.Count();
iDesiredLabelWide += m_iTextX*2; labelTall += m_iTextY*2;
// Now clamp it to our animation size
iDesiredLabelWide = (iDesiredLabelWide * m_flLabelSizePercentage);
int x, y; if ( m_iCenterX < 0 ) { x = 0; } else if ( m_iCenterX > 0 ) { x = wide - iDesiredLabelWide; } else { x = (wide - iDesiredLabelWide) / 2; }
if ( m_iCenterY > 0 ) { y = 0; } else if ( m_iCenterY < 0 ) { y = tall - labelTall; } else { y = (tall - labelTall) / 2; }
x = MAX(x,0); y = MAX(y,0);
iDesiredLabelWide = MIN(iDesiredLabelWide,wide); m_pLabel->SetBounds( x, y, iDesiredLabelWide, labelTall );
// now lay out the sub-labels
for ( i=0; i<m_Labels.Count(); ++i ) { int xOffset = (wide - m_Labels[i]->GetWide()) * 0.5; m_Labels[i]->SetPos( xOffset, y + m_iTextY + i*fontTall ); } }
//-----------------------------------------------------------------------------
// Purpose: Updates the label color each frame
//-----------------------------------------------------------------------------
void CHudHintDisplay::OnThink() { m_pLabel->SetFgColor(GetFgColor()); for (int i = 0; i < m_Labels.Count(); i++) { m_Labels[i]->SetFgColor(GetFgColor()); }
// If our label size isn't at the extreme's, we're sliding open / closed
// This is a hack to get around InvalideLayout() not getting called when
// m_flLabelSizePercentage is changed via a HudAnimation.
if ( m_flLabelSizePercentage != 0.0 && m_flLabelSizePercentage != 1.0 || m_bLastLabelUpdateHack ) { m_bLastLabelUpdateHack = (m_flLabelSizePercentage != 0.0 && m_flLabelSizePercentage != 1.0); InvalidateLayout(); } }
//-----------------------------------------------------------------------------
// Purpose: Activates the hint display
//-----------------------------------------------------------------------------
bool CHudHintDisplay::MsgFunc_HintText( const CCSUsrMsg_HintText &msg ) { #if defined( CSTRIKE15 )
// csgo handles their hint text in sfhudinfopanel
return true; #endif
char *tmpStr = hudtextmessage->LookupString( msg.text().c_str(), NULL ); LocalizeAndDisplay( tmpStr, msg.text().c_str() );
return true; }
//-----------------------------------------------------------------------------
// Purpose: Activates the hint display upon recieving a hint
//-----------------------------------------------------------------------------
void CHudHintDisplay::FireGameEvent( IGameEvent * event) { const char *hintmessage = event->GetString( "hintmessage" ); char *tmpStr = hudtextmessage->LookupString( hintmessage, NULL ); LocalizeAndDisplay( tmpStr, hintmessage ); }
ConVar sv_hudhint_sound( "sv_hudhint_sound", "1", FCVAR_REPLICATED );
//-----------------------------------------------------------------------------
// Purpose: Localize, display, and animate the hud element
//-----------------------------------------------------------------------------
void CHudHintDisplay::LocalizeAndDisplay( const char *pszHudTxtMsg, const char *szRawString ) { static wchar_t szBuf[128]; static wchar_t *pszBuf;
// init buffers & pointers
szBuf[0] = 0; pszBuf = szBuf;
// try to localize
if ( pszHudTxtMsg ) { pszBuf = g_pVGuiLocalize->Find( pszHudTxtMsg ); } else { pszBuf = g_pVGuiLocalize->Find( szRawString ); }
if ( !pszBuf ) { // use plain ASCII string
g_pVGuiLocalize->ConvertANSIToUnicode( szRawString, szBuf, sizeof(szBuf) ); pszBuf = szBuf; }
// make it visible
if ( SetHintText( pszBuf ) ) { SetVisible( true ); GetClientMode()->GetViewportAnimationController()->StartAnimationSequence( "HintMessageShow" );
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); if ( pLocalPlayer ) { #ifndef HL2MP
if ( sv_hudhint_sound.GetBool() ) { pLocalPlayer->EmitSound( "Hud.Hint" ); } #endif // HL2MP
if ( pLocalPlayer->Hints() ) { pLocalPlayer->Hints()->PlayedAHint(); } } } else { GetClientMode()->GetViewportAnimationController()->StartAnimationSequence( "HintMessageHide" ); } }
//-----------------------------------------------------------------------------
// Purpose: Displays small key-centric hints on the right hand side of the screen
//-----------------------------------------------------------------------------
class CHudHintKeyDisplay : public vgui::Panel, public CHudElement { DECLARE_CLASS_SIMPLE( CHudHintKeyDisplay, vgui::Panel );
public: explicit CHudHintKeyDisplay( const char *pElementName ); void Init(); void Reset(); bool MsgFunc_KeyHintText( const CCSUsrMsg_KeyHintText &msg ); bool ShouldDraw();
bool SetHintText( const char *text );
CUserMessageBinder m_UMCMsgKeyHintText;
protected: virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); virtual void OnThink();
private: CUtlVector<vgui::Label *> m_Labels; vgui::HFont m_hSmallFont, m_hLargeFont; int m_iBaseY;
CPanelAnimationVarAliasType( float, m_iTextX, "text_xpos", "8", "proportional_float" ); CPanelAnimationVarAliasType( float, m_iTextY, "text_ypos", "8", "proportional_float" ); CPanelAnimationVarAliasType( float, m_iTextGapX, "text_xgap", "8", "proportional_float" ); CPanelAnimationVarAliasType( float, m_iTextGapY, "text_ygap", "8", "proportional_float" ); CPanelAnimationVarAliasType( float, m_iYOffset, "YOffset", "0", "proportional_float" ); };
DECLARE_HUDELEMENT( CHudHintKeyDisplay ); DECLARE_HUD_MESSAGE( CHudHintKeyDisplay, KeyHintText );
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CHudHintKeyDisplay::CHudHintKeyDisplay( const char *pElementName ) : BaseClass(NULL, "HudHintKeyDisplay"), CHudElement( pElementName ) { vgui::Panel *pParent = GetClientMode()->GetViewport(); SetParent( pParent ); SetVisible( false ); SetAlpha( 0 ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudHintKeyDisplay::Init() { HOOK_HUD_MESSAGE( CHudHintKeyDisplay, KeyHintText ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudHintKeyDisplay::Reset() { SetHintText( NULL ); SetAlpha( 0 ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudHintKeyDisplay::ApplySchemeSettings( vgui::IScheme *pScheme ) { m_hSmallFont = pScheme->GetFont( "HudHintTextSmall", true ); m_hLargeFont = pScheme->GetFont( "HudHintTextLarge", true );
BaseClass::ApplySchemeSettings( pScheme ); }
//-----------------------------------------------------------------------------
// Purpose: Save CPU cycles by letting the HUD system early cull
// costly traversal. Called per frame, return true if thinking and
// painting need to occur.
//-----------------------------------------------------------------------------
bool CHudHintKeyDisplay::ShouldDraw( void ) { // [jason] Moving into Scaleform: sfhudinfopanel
#if defined( CSTRIKE15 )
return false; #endif
return ( ( GetAlpha() > 0 ) && CHudElement::ShouldDraw() ); }
//-----------------------------------------------------------------------------
// Purpose: Updates the label color each frame
//-----------------------------------------------------------------------------
void CHudHintKeyDisplay::OnThink() { for (int i = 0; i < m_Labels.Count(); i++) { if ( IsGameConsole() && ( i & 1 ) == 0 ) { // Don't change the fg color for buttons (even numbered labels)
m_Labels[i]->SetAlpha( GetFgColor().a() ); } else { m_Labels[i]->SetFgColor(GetFgColor()); } }
int ox, oy; GetPos(ox, oy); SetPos( ox, m_iBaseY + m_iYOffset ); }
//-----------------------------------------------------------------------------
// Purpose: Sets the hint text, replacing variables as necessary
//-----------------------------------------------------------------------------
bool CHudHintKeyDisplay::SetHintText( const char *text ) { if ( text == NULL || text[0] == L'\0' ) return false; // clear the existing text
for (int i = 0; i < m_Labels.Count(); i++) { m_Labels[i]->MarkForDeletion(); } m_Labels.RemoveAll();
// look up the text string
wchar_t *ws = g_pVGuiLocalize->Find( text ); wchar_t wszBuf[256]; if ( !ws || wcslen(ws) <= 0) { g_pVGuiLocalize->ConvertANSIToUnicode(text, wszBuf, sizeof(wszBuf)); ws = wszBuf; }
// parse out the text into a label set
while ( *ws ) { wchar_t token[256]; bool isVar = false;
// check for variables
if ( *ws == '%' ) { isVar = true; ++ws; }
// parse out the string
wchar_t *end = wcschr( ws, '%' ); if ( end ) { wcsncpy( token, ws, MIN( end - ws, ARRAYSIZE(token)) ); token[end - ws] = L'\0'; // force null termination
} else { wcsncpy( token, ws, ARRAYSIZE(token) ); token[ ARRAYSIZE(token) - 1 ] = L'\0'; // force null termination
}
ws += wcslen( token ); if ( isVar ) { // move over the end of the variable
++ws; }
// put it in a label
vgui::Label *label = vgui::SETUP_PANEL(new vgui::Label(this, NULL, token));
bool bIsBitmap = false;
// modify the label if necessary
if ( isVar ) { label->SetFont( m_hLargeFont );
// lookup key names
char binding[64]; g_pVGuiLocalize->ConvertUnicodeToANSI( token, binding, sizeof(binding) );
//!! change some key names into better names
char friendlyName[64];
if ( IsGameConsole() ) { int iNumBinds = 0;
char szBuff[ 512 ]; wchar_t szWideBuff[ 64 ];
for ( int iCode = 0; iCode < BUTTON_CODE_LAST; ++iCode ) { ButtonCode_t code = static_cast<ButtonCode_t>( iCode );
bool bUseThisKey = false;
// Only check against bind name if we haven't already forced this binding to be used
const char *pBinding = gameuifuncs->GetBindingForButtonCode( code );
if ( !pBinding ) continue;
bUseThisKey = ( Q_stricmp( pBinding, binding ) == 0 );
if ( !bUseThisKey && ( Q_stricmp( pBinding, "+duck" ) == 0 || Q_stricmp( pBinding, "toggle_duck" ) == 0 ) && ( Q_stricmp( binding, "+duck" ) == 0 || Q_stricmp( binding, "toggle_duck" ) == 0 ) ) { // +duck and toggle_duck are interchangable
bUseThisKey = true; }
if ( !bUseThisKey && ( Q_stricmp( pBinding, "+zoom" ) == 0 || Q_stricmp( pBinding, "toggle_zoom" ) == 0 ) && ( Q_stricmp( binding, "+zoom" ) == 0 || Q_stricmp( binding, "toggle_zoom" ) == 0 ) ) { // +zoom and toggle_zoom are interchangable
bUseThisKey = true; }
// Don't use this bind in out list
if ( !bUseThisKey ) continue;
// Turn localized string into icon character
Q_snprintf( szBuff, sizeof( szBuff ), "#GameUI_Icons_%s", g_pInputSystem->ButtonCodeToString( static_cast<ButtonCode_t>( iCode ) ) ); g_pVGuiLocalize->ConstructString( szWideBuff, sizeof( szWideBuff ), g_pVGuiLocalize->Find( szBuff ), 0 ); g_pVGuiLocalize->ConvertUnicodeToANSI( szWideBuff, szBuff, sizeof( szBuff ) );
// Add this icon to our list of keys to display
friendlyName[ iNumBinds ] = szBuff[ 0 ]; ++iNumBinds; }
friendlyName[ iNumBinds ] = '\0';
if ( iNumBinds == 0 ) { friendlyName[ 0 ] = '\0'; label->SetFont( m_hSmallFont ); label->SetText( "#GameUI_Icons_NONE" ); } else { // 360 always uses bitmaps
bIsBitmap = true; label->SetText( friendlyName ); } } else { const char *key = engine->Key_LookupBinding( *binding == '+' ? binding + 1 : binding ); if ( !key ) { key = "< not bound >"; }
Q_snprintf( friendlyName, sizeof(friendlyName), "#%s", key ); Q_strupr( friendlyName );
// set the variable text - key may need to be localized (button images for example)
wchar_t *locName = g_pVGuiLocalize->Find( friendlyName ); if ( !locName || wcslen(locName) <= 0) { label->SetText( friendlyName + 1 ); } else { // Assuming localized vars must be using a bitmap image. *May* not be the case, but since
// keyboard bindings have never been locaized in the past, they probably won't in the future either.
bIsBitmap = true; label->SetText( locName ); } } } else { label->SetFont( m_hSmallFont ); }
label->SetPaintBackgroundEnabled( false ); label->SetPaintBorderEnabled( false ); label->SizeToContents(); label->SetContentAlignment( vgui::Label::a_west ); if ( bIsBitmap && isVar ) { // Don't change the color of the button art
label->SetFgColor( Color(255,255,255,255) ); } else { label->SetFgColor( GetFgColor() ); } m_Labels.AddToTail( vgui::SETUP_PANEL(label) ); }
// find the bounds we need to show
int widest1 = 0, widest2 = 0; for (int i = 0; i < m_Labels.Count(); i++) { vgui::Label *label = m_Labels[i];
if (i & 1) { // help text
if (label->GetWide() > widest2) { widest2 = label->GetWide(); } } else { // variable
if (label->GetWide() > widest1) { widest1 = label->GetWide(); } } }
int tallest1 = 0, tallest2 = 0; for (int i = 0; i < m_Labels.Count(); i++) { vgui::Label *label = m_Labels[i];
if (i & 1) { // help text
if (label->GetTall() > tallest2) { tallest2 = label->GetTall(); } } else { // variable
if (label->GetTall() > tallest1) { tallest1 = label->GetTall(); } } } int tallest = MAX( tallest1, tallest2 );
// position the labels
int col1_x = m_iTextX; int col2_x = m_iTextX + widest1 + m_iTextGapX; int col_y = m_iTextY;
// Difference between the variable and the help text
int col_y_extra = ( (tallest1 - tallest2) / 2 ); if ( col_y_extra < 0 ) { // text is taller than the variable (multi-line text).
// Push the variable's y down by subtracting the negative value.
col_y -= col_y_extra; }
for (int i = 0; i < m_Labels.Count(); i++) { vgui::Label *label = m_Labels[i];
if (i & 1) { label->SetPos( col2_x, col_y + col_y_extra );
col_y += tallest; col_y += m_iTextGapY; } else { // variable
label->SetPos( col1_x, col_y ); } }
// move ourselves relative to our start position
int newWide = col2_x + widest2 + m_iTextX; int newTall = col_y; int ox, oy; GetPos(ox, oy);
if (IsRightAligned()) { int oldWide = GetWide(); int diff = newWide - oldWide; ox -= diff; }
if (IsBottomAligned()) { int oldTall = GetTall(); int diff = newTall - oldTall; oy -= diff; }
// set the size of the hint panel to fit
SetPos( ox, oy ); SetSize( newWide, newTall );
m_iBaseY = oy;
return true; }
//-----------------------------------------------------------------------------
// Purpose: Activates the hint display
//-----------------------------------------------------------------------------
bool CHudHintKeyDisplay::MsgFunc_KeyHintText( const CCSUsrMsg_KeyHintText &msg ) { // how many strings do we receive ?
int count = msg.hints_size();
// here we expect only one string
if ( count != 1 ) { DevMsg("CHudHintKeyDisplay::MsgFunc_KeyHintText: string count != 1.\n"); return true; }
// make it visible
if ( SetHintText( msg.hints(0).c_str() ) ) { SetVisible( true ); GetClientMode()->GetViewportAnimationController()->StartAnimationSequence( "KeyHintMessageShow" ); } else { // it's being cleared, hide the panel
GetClientMode()->GetViewportAnimationController()->StartAnimationSequence( "KeyHintMessageHide" ); }
return true; }
|