//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// // // Message.cpp // // implementation of CHudMessage class // #include "cbase.h" #include "message.h" #include "client_textmessage.h" #include "hud_macros.h" #include "iclientmode.h" #include "vgui_controls/Controls.h" #include "vgui/ILocalize.h" #include "vgui/IScheme.h" #include "vgui/ISurface.h" #include "VGuiMatSurface/IMatSystemSurface.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #include #define NETWORK_MESSAGE1 "__NETMESSAGE__1" #define NETWORK_MESSAGE2 "__NETMESSAGE__2" #define NETWORK_MESSAGE3 "__NETMESSAGE__3" #define NETWORK_MESSAGE4 "__NETMESSAGE__4" #define NETWORK_MESSAGE5 "__NETMESSAGE__5" #define NETWORK_MESSAGE6 "__NETMESSAGE__6" #define MAX_NETMESSAGE 6 // Simultaneous message limit #define MAX_TEXTMESSAGE_CHARS 2048 static const char *s_NetworkMessageNames[MAX_NETMESSAGE] = { NETWORK_MESSAGE1, NETWORK_MESSAGE2, NETWORK_MESSAGE3, NETWORK_MESSAGE4, NETWORK_MESSAGE5, NETWORK_MESSAGE6 }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void DispatchHudText( const char *pszText ) { if ( pszText == NULL ) { (GET_HUDELEMENT( CHudMessage ))->Reset(); } else { (GET_HUDELEMENT( CHudMessage ))->MessageAdd( pszText ); } } // //----------------------------------------------------- // DECLARE_HUDELEMENT( CHudMessage ); DECLARE_HUD_MESSAGE( CHudMessage, HudText ); DECLARE_HUD_MESSAGE( CHudMessage, GameTitle ); DECLARE_HUD_MESSAGE( CHudMessage, HudMsg ); ITextMessage *textmessage = NULL; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CHudMessage::CHudMessage( const char *pElementName ) : CHudElement( pElementName ), BaseClass( NULL, "HudMessage" ) { vgui::Panel *pParent = GetClientMode()->GetViewport(); SetParent( pParent ); if( textmessage == NULL ) //HACKHACK: Fixes center print text in when MAX_SPLITSCREEN_PLAYERS is greater than 1 { textmessage = this; } m_hFont = g_hFontTrebuchet24; m_hDefaultFont = m_hFont; // Clear memory out ResetCharacters(); } CHudMessage::~CHudMessage() { textmessage = NULL; } void CHudMessage::ApplySchemeSettings( IScheme *scheme ) { BaseClass::ApplySchemeSettings( scheme ); SetPaintBackgroundEnabled( false ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudMessage::Init(void) { HOOK_HUD_MESSAGE( CHudMessage, HudText ); HOOK_HUD_MESSAGE( CHudMessage, GameTitle ); HOOK_HUD_MESSAGE( CHudMessage, HudMsg ); Reset(); }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudMessage::VidInit( void ) { m_iconTitleHalf = HudIcons().GetIcon( "title_half" ); m_iconTitleLife = HudIcons().GetIcon( "title_life" ); }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudMessage::Reset( void ) { memset( m_pMessages, 0, sizeof( m_pMessages[0] ) * maxHUDMessages ); memset( m_startTime, 0, sizeof( m_startTime[0] ) * maxHUDMessages ); m_gameTitleTime = 0; m_pGameTitle = NULL; m_bHaveMessage = false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- float CHudMessage::FadeBlend( float fadein, float fadeout, float hold, float localTime ) { float fadeTime = fadein + hold; float fadeBlend; if ( localTime < 0 ) return 0; if ( localTime < fadein ) { fadeBlend = 1 - ((fadein - localTime) / fadein); } else if ( localTime > fadeTime ) { if ( fadeout > 0 ) fadeBlend = 1 - ((localTime - fadeTime) / fadeout); else fadeBlend = 0; } else fadeBlend = 1; return fadeBlend; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CHudMessage::XPosition( float x, int width, int totalWidth ) { int xPos; if ( x == -1 ) { xPos = (ScreenWidth() - width) / 2; } else { if ( x < 0 ) xPos = (1.0 + x) * ScreenWidth() - totalWidth; // Align to right else xPos = x * ScreenWidth(); } if ( xPos + width > ScreenWidth() ) xPos = ScreenWidth() - width; else if ( xPos < 0 ) xPos = 0; return xPos; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CHudMessage::YPosition( float y, int height ) { int yPos; if ( y == -1 ) // Centered? yPos = (ScreenHeight() - height) * 0.5; else { // Alight bottom? if ( y < 0 ) yPos = (1.0 + y) * ScreenHeight() - height; // Alight bottom else // align top yPos = y * ScreenHeight(); } if ( yPos + height > ScreenHeight() ) yPos = ScreenHeight() - height; else if ( yPos < 0 ) yPos = 0; return yPos; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudMessage::MessageScanNextChar( void ) { int srcRed, srcGreen, srcBlue, destRed, destGreen, destBlue; int blend; srcRed = m_parms.pMessage->r1; srcGreen = m_parms.pMessage->g1; srcBlue = m_parms.pMessage->b1; blend = 0; // Pure source destRed = destGreen = destBlue = 0; switch( m_parms.pMessage->effect ) { // Fade-in / Fade-out case 0: case 1: destRed = destGreen = destBlue = 0; blend = m_parms.fadeBlend; break; case 2: m_parms.charTime += m_parms.pMessage->fadein; if ( m_parms.charTime > m_parms.time ) { srcRed = srcGreen = srcBlue = 0; blend = 0; // pure source } else { float deltaTime = m_parms.time - m_parms.charTime; destRed = destGreen = destBlue = 0; if ( m_parms.time > m_parms.fadeTime ) { blend = m_parms.fadeBlend; } else if ( deltaTime > m_parms.pMessage->fxtime ) blend = 0; // pure dest else { destRed = m_parms.pMessage->r2; destGreen = m_parms.pMessage->g2; destBlue = m_parms.pMessage->b2; blend = 255 - (deltaTime * (1.0/m_parms.pMessage->fxtime) * 255.0 + 0.5); } } break; } if ( blend > 255 ) blend = 255; else if ( blend < 0 ) blend = 0; m_parms.r = ((srcRed * (255-blend)) + (destRed * blend)) >> 8; m_parms.g = ((srcGreen * (255-blend)) + (destGreen * blend)) >> 8; m_parms.b = ((srcBlue * (255-blend)) + (destBlue * blend)) >> 8; #if 0 if ( m_parms.pMessage->effect == 1 && m_parms.charTime != 0 ) { textmessage->AddChar( m_parms.pMessage->r2, m_parms.pMessage->g2, m_parms.pMessage->b2, 255, m_parms.text ); } #endif } void CHudMessage::SetFont( HScheme scheme, const char *pFontName ) { vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( scheme ); if ( pScheme ) { bool bProportional = false; #ifdef PORTAL2 bProportional = true; #endif vgui::HFont font = pScheme->GetFont( pFontName, bProportional ); textmessage->SetFont( font ); m_parms.font = font; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudMessage::MessageScanStart( void ) { switch( m_parms.pMessage->effect ) { // Fade-in / out with flicker case 1: case 0: m_parms.fadeTime = m_parms.pMessage->fadein + m_parms.pMessage->holdtime; if ( m_parms.time < m_parms.pMessage->fadein ) { m_parms.fadeBlend = ((m_parms.pMessage->fadein - m_parms.time) * (1.0/m_parms.pMessage->fadein) * 255); } else if ( m_parms.time > m_parms.fadeTime ) { if ( m_parms.pMessage->fadeout > 0 ) m_parms.fadeBlend = (((m_parms.time - m_parms.fadeTime) / m_parms.pMessage->fadeout) * 255); else m_parms.fadeBlend = 255; // Pure dest (off) } else m_parms.fadeBlend = 0; // Pure source (on) m_parms.charTime = 0; if ( m_parms.pMessage->effect == 1 && (rand()%100) < 10 ) m_parms.charTime = 1; break; case 2: m_parms.fadeTime = (m_parms.pMessage->fadein * m_parms.length) + m_parms.pMessage->holdtime; if ( m_parms.time > m_parms.fadeTime && m_parms.pMessage->fadeout > 0 ) m_parms.fadeBlend = (((m_parms.time - m_parms.fadeTime) / m_parms.pMessage->fadeout) * 255); else m_parms.fadeBlend = 0; break; } m_parms.font = g_hFontTrebuchet24; if ( m_parms.vguiFontName != NULL && m_parms.vguiFontName[ 0 ] ) { #ifdef PORTAL2 SetFont( vgui::scheme()->GetScheme( "basemodui_scheme" ), m_parms.vguiFontName ); #else SetFont( vgui::scheme()->GetDefaultScheme(), m_parms.vguiFontName ); #endif } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudMessage::MessageDrawScan( client_textmessage_t *pMessage, float time ) { int i, j, length, width; const wchar_t *pText; wchar_t textBuf[ 1024 ]; { // look up in localization table // strip off any trailing newlines int len = Q_strlen( pMessage->pMessage ); int tempLen = len + 2; char *localString = (char *)stackalloc( tempLen ); Q_strncpy( localString, pMessage->pMessage, tempLen ); if (len > 0 && V_iscntrl(localString[len - 1])) { localString[len - 1] = 0; } pText = g_pVGuiLocalize->Find( localString ); if ( !pText ) { g_pVGuiLocalize->ConvertANSIToUnicode( pMessage->pMessage, textBuf, sizeof( textBuf ) ); pText = textBuf; } } const wchar_t *pPerm = pText; // Count lines m_parms.lines = 1; m_parms.time = time; m_parms.pMessage = pMessage; length = 0; width = 0; m_parms.totalWidth = 0; m_parms.vguiFontName = pMessage->pVGuiSchemeFontName; if ( m_parms.font == 0 ) { if ( m_parms.vguiFontName != NULL && m_parms.vguiFontName[ 0 ] ) { #ifdef PORTAL2 SetFont( vgui::scheme()->GetScheme( "basemodui_scheme" ), m_parms.vguiFontName ); #else SetFont( vgui::scheme()->GetDefaultScheme(), m_parms.vguiFontName ); #endif } else { m_parms.font = g_hFontTrebuchet24; } } while ( *pText ) { if ( *pText == '\n' ) { m_parms.lines++; if ( width > m_parms.totalWidth ) m_parms.totalWidth = width; width = 0; } else { width += vgui::surface()->GetCharacterWidth( m_parms.font, *pText ); } pText++; length++; } if ( width > m_parms.totalWidth ) m_parms.totalWidth = width; m_parms.length = length; int fontHeight = vgui::surface()->GetFontTall( m_parms.font ); m_parms.totalHeight = ( m_parms.lines * fontHeight ); m_parms.y = YPosition( pMessage->y, m_parms.totalHeight ); pText = pPerm; m_parms.charTime = 0; float flBoxPixels = 0.0f; MessageScanStart(); if ( pMessage->bRoundedRectBackdropBox ) { // 2.0f since we have extra space at both the top and bottom flBoxPixels = pMessage->flBoxSize * fontHeight; // Draw the box int boxx = XPosition( pMessage->x, m_parms.totalWidth, m_parms.totalWidth ); int boxy = YPosition( pMessage->y, m_parms.totalHeight ); boxx -= flBoxPixels; boxy -= flBoxPixels * 0.5f; float flAlphaScale = clamp( ( 255.0f - (float)m_parms.fadeBlend ) / 255.0f, 0.0f, 1.0f ); Color boxColor( pMessage->boxcolor[ 0 ], pMessage->boxcolor[ 1 ], pMessage->boxcolor[ 2 ], pMessage->boxcolor[ 3 ] * flAlphaScale ); DrawBox( boxx, boxy, m_parms.totalWidth + 2.0f * flBoxPixels, m_parms.totalHeight + 2.0f * flBoxPixels * 0.5f, boxColor, 1.0f ); } wchar_t line[ 512 ]; for ( i = 0; i < m_parms.lines; i++ ) { m_parms.lineLength = 0; m_parms.width = 0; while ( *pText && *pText != '\n' ) { wchar_t c = *pText; line[m_parms.lineLength] = c; m_parms.width += vgui::surface()->GetCharacterWidth( m_parms.font, c); m_parms.lineLength++; if ( m_parms.lineLength > (ARRAYSIZE(line)-1) ) { m_parms.lineLength = ARRAYSIZE(line)-1; } pText++; } pText++; // Skip LF line[m_parms.lineLength] = 0; m_parms.x = XPosition( pMessage->x, m_parms.width, m_parms.totalWidth ); textmessage->SetPosition( m_parms.x, m_parms.y ); if (m_parms.fadeBlend > 255) m_parms.fadeBlend = 255; for ( j = 0; j < m_parms.lineLength; j++ ) { m_parms.text = line[j]; MessageScanNextChar(); textmessage->AddChar( m_parms.r, m_parms.g, m_parms.b, 255 - m_parms.fadeBlend, m_parms.text ); } m_parms.y += vgui::surface()->GetFontTall( m_parms.font ); } // Restore default font textmessage->SetDefaultFont(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CHudMessage::ShouldDraw( void ) { return ( CHudElement::ShouldDraw() && ( m_bHaveMessage || m_Messages.Count() ) ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudMessage::Paint() { int i, drawn; client_textmessage_t *pMessage; float endTime; drawn = 0; if ( m_gameTitleTime > 0 ) { float localTime = gpGlobals->curtime - m_gameTitleTime; float brightness; // Maybe timer isn't set yet if ( m_gameTitleTime > gpGlobals->curtime ) { m_gameTitleTime = gpGlobals->curtime; } if ( localTime > (m_pGameTitle->fadein + m_pGameTitle->holdtime + m_pGameTitle->fadeout) ) { m_gameTitleTime = 0; } else { brightness = FadeBlend( m_pGameTitle->fadein, m_pGameTitle->fadeout, m_pGameTitle->holdtime, localTime ); int halfWidth = m_iconTitleHalf->Width(); int fullWidth = halfWidth + m_iconTitleLife->Width(); int fullHeight = m_iconTitleHalf->Height(); int x = XPosition( m_pGameTitle->x, fullWidth, fullWidth ); int y = YPosition( m_pGameTitle->y, fullHeight ); m_iconTitleHalf->DrawSelf( x, y, Color( m_pGameTitle->r1, m_pGameTitle->g1, m_pGameTitle->b1, brightness * 255 ) ); m_iconTitleLife->DrawSelf( x + halfWidth, y, Color( m_pGameTitle->r1, m_pGameTitle->g1, m_pGameTitle->b1, brightness * 255 ) ); drawn = 1; } } // Fixup level transitions for ( i = 0; i < maxHUDMessages; i++ ) { // Assume m_parms.time contains last time if ( m_pMessages[i] ) { pMessage = m_pMessages[i]; if ( m_startTime[i] > gpGlobals->curtime ) m_startTime[i] = gpGlobals->curtime + m_parms.time - m_startTime[i] + 0.2; // Server takes 0.2 seconds to spawn, adjust for this } } for ( i = 0; i < maxHUDMessages; i++ ) { if ( m_pMessages[i] ) { pMessage = m_pMessages[i]; // This is when the message is over switch( pMessage->effect ) { case 0: case 1: endTime = m_startTime[i] + pMessage->fadein + pMessage->fadeout + pMessage->holdtime; break; // Fade in is per character in scanning messages case 2: endTime = m_startTime[i] + (pMessage->fadein * strlen( pMessage->pMessage )) + pMessage->fadeout + pMessage->holdtime; break; default: endTime = 0; break; } if ( gpGlobals->curtime <= endTime ) { float messageTime = gpGlobals->curtime - m_startTime[i]; // Draw the message // effect 0 is fade in/fade out // effect 1 is flickery credits // effect 2 is write out (training room) MessageDrawScan( pMessage, messageTime ); drawn++; } else { // The message is over m_pMessages[i] = NULL; } } } // Remember the time -- to fix up level transitions m_parms.time = gpGlobals->curtime; // Did we draw any messages? if ( !drawn ) { m_bHaveMessage = false; } PaintCharacters(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudMessage::MessageAdd( const char *pName ) { int i; float time = gpGlobals->curtime; client_textmessage_t *pMessage = NULL; if ( pName[0] == '#' ) { pMessage = TextMessageGet( pName+1 ); } else { pMessage = TextMessageGet( pName ); } if ( !pMessage ) return; if ( pMessage->pClearMessage ) { for ( i = 0; i < maxHUDMessages; i++ ) { if ( m_pMessages[ i ] && !Q_stricmp( m_pMessages[ i ]->pName, pMessage->pClearMessage ) ) { m_startTime[ i ] = 0.0f; m_pMessages[ i ] = NULL; break; } } } for ( i = 0; i < maxHUDMessages; i++ ) { if ( !m_pMessages[i] ) { m_pMessages[i] = pMessage; m_startTime[i] = time; break; } } // Remember the time -- to fix up level transitions m_parms.time = time; m_bHaveMessage = true; // Force this now so that SCR_UpdateScreen will paint the panel immediately!!! SetVisible( true ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CHudMessage::MsgFunc_HudText(const CCSUsrMsg_HudText &msg) { MessageAdd( msg.text().c_str() ); return true; } #include "ivieweffects.h" #include "shake.h" //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CHudMessage::MsgFunc_GameTitle(const CCSUsrMsg_GameTitle &msg) { m_pGameTitle = TextMessageGet( "GAMETITLE" ); if ( m_pGameTitle != NULL ) { m_gameTitleTime = gpGlobals->curtime; m_bHaveMessage = true; } // if ( READ_BYTE() ) { ScreenFade_t sf; memset( &sf, 0, sizeof( sf ) ); sf.a = 255; sf.r = 0; sf.g = 0; sf.b = 0; sf.duration = (float)(1<Fade( sf ); } Msg( "%i gametitle fade\n", gpGlobals->framecount ); } return true; } bool CHudMessage::MsgFunc_HudMsg(const CCSUsrMsg_HudMsg &msg) { // Position command $position x y // x & y are from 0 to 1 to be screen resolution independent // -1 means center in each dimension // Effect command $effect // effect 0 is fade in/fade out // effect 1 is flickery credits // effect 2 is write out (training room) // Text color r g b command $color // Text color r g b command $color2 // fadein time fadeout time / hold time // $fadein (message fade in time - per character in effect 2) // $fadeout (message fade out time) // $holdtime (stay on the screen for this long) int channel = msg.channel() % MAX_NETMESSAGE; // Pick the buffer client_textmessage_t *pNetMessage = TextMessageGet( s_NetworkMessageNames[ channel ] ); if ( !pNetMessage || !pNetMessage->pMessage ) return true; pNetMessage->x = msg.pos().x(); pNetMessage->y = msg.pos().y(); pNetMessage->r1 = msg.clr1().r(); pNetMessage->g1 = msg.clr1().g(); pNetMessage->b1 = msg.clr1().b(); pNetMessage->a1 = msg.clr1().a(); pNetMessage->r2 = msg.clr2().r(); pNetMessage->g2 = msg.clr2().g(); pNetMessage->b2 = msg.clr2().b(); pNetMessage->a2 = msg.clr2().a(); pNetMessage->effect = msg.effect(); pNetMessage->fadein = msg.fade_in_time(); pNetMessage->fadeout = msg.fade_out_time(); pNetMessage->holdtime = msg.hold_time(); pNetMessage->fxtime = msg.fx_time(); #ifdef PORTAL2 // hack to make the chapter title channel define the font size in Portal 2 if ( channel == 2 || channel == 3 ) { const char *pFontName; if ( channel == 2 ) pFontName = "InGameChapterTitle"; else pFontName = "InGameChapterSubtitle"; pNetMessage->pVGuiSchemeFontName = pFontName; } #endif pNetMessage->pName = s_NetworkMessageNames[ channel ]; // see tmessage.cpp why 512 Q_strncpy( (char*)pNetMessage->pMessage, msg.text().c_str(), 512 ); MessageAdd( pNetMessage->pName ); return true; } //----------------------------------------------------------------------------- // Purpose: Get font sizes // Input : *pWidth - // Output : int //----------------------------------------------------------------------------- int CHudMessage::GetFontInfo( FONTABC *pABCs, vgui::HFont hFont ) { int i; if ( !hFont ) { hFont = m_hFont; } if ( !hFont ) return 0; if ( pABCs ) { for ( i =0; i < 256; i++ ) { int a, b, c; vgui::surface()->GetCharABCwide( hFont, (char)i, a, b, c ); pABCs[i].abcA = a; pABCs[i].abcB = b; pABCs[i].abcC = c; pABCs[i].total = a+b+c; } } return vgui::surface()->GetFontTall( hFont ); } //----------------------------------------------------------------------------- // Purpose: Clear all messages out of active list, etc. //----------------------------------------------------------------------------- void CHudMessage::ResetCharacters( void ) { m_Messages.Purge(); } //----------------------------------------------------------------------------- // Purpose: Grab next free message, if any // Output : CTextMessagePanel::message_t //----------------------------------------------------------------------------- CHudMessage::message_t *CHudMessage::AllocMessage( void ) { message_t *msg; if ( m_Messages.Count() >= MAX_TEXTMESSAGE_CHARS ) return NULL; msg = &m_Messages[ m_Messages.AddToTail() ]; msg->type = TYPE_UNKNOWN; msg->x = 0; msg->y = 0; msg->ch = 0; msg->r = 0; msg->g = 0; msg->b = 0; msg->a = 0; msg->font = 0; SetVisible( true ); return msg; } //----------------------------------------------------------------------------- // Purpose: // Input : x - // y - //----------------------------------------------------------------------------- void CHudMessage::SetPosition( int x, int y ) { message_t *msg = AllocMessage(); if ( !msg ) return; msg->type = TYPE_POSITION; // Used fields msg->x = x; msg->y = y; } //----------------------------------------------------------------------------- // Purpose: Adds a character to the active list, if possible // Input : x - // y - // r - // g - // b - // a - // ch - // Output : int //----------------------------------------------------------------------------- void CHudMessage::AddChar( int r, int g, int b, int a, wchar_t ch ) { message_t *msg = AllocMessage(); if ( !msg ) return; msg->type = TYPE_CHARACTER; // Used fields msg->r = r; msg->g = g; msg->b = b; msg->a = a; msg->ch = ch; } //----------------------------------------------------------------------------- // Purpose: Determine width and height of specified string // Input : *wide - // *tall - // *string - //----------------------------------------------------------------------------- void CHudMessage::GetTextExtents( int *wide, int *tall, const char *string ) { *wide = g_pMatSystemSurface->DrawTextLen( m_hFont, "%s", (char *)string ); *tall = vgui::surface()->GetFontTall( m_hFont ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudMessage::SetFont( vgui::HFont hCustomFont ) { m_hFont = hCustomFont; message_t *msg = AllocMessage(); if ( !msg ) return; msg->type = TYPE_FONT; // Used fields msg->font = m_hFont; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudMessage::SetDefaultFont( void ) { SetFont( m_hDefaultFont ); } //----------------------------------------------------------------------------- // Purpose: Draw current text items //----------------------------------------------------------------------------- void CHudMessage::PaintCharacters() { int xpos = 0, ypos = 0; vgui::surface()->DrawSetTextFont( m_hFont ); int messageCount = m_Messages.Count(); for ( int i = 0 ; i < messageCount; ++i ) { message_t *msg = &m_Messages[ i ]; switch ( msg->type ) { default: case TYPE_UNKNOWN: Assert( 0 ); break; case TYPE_POSITION: xpos = msg->x; ypos = msg->y; break; case TYPE_FONT: m_hFont = msg->font; vgui::surface()->DrawSetTextFont( m_hFont ); break; case TYPE_CHARACTER: if ( m_hFont ) { int a, b, c; vgui::surface()->GetCharABCwide( m_hFont, msg->ch, a, b, c ); if ( msg->ch > 32 ) { vgui::surface()->DrawSetTextColor( msg->r, msg->g, msg->b, msg->a ); vgui::surface()->DrawSetTextPos( xpos, ypos ); vgui::surface()->DrawUnicodeChar( msg->ch ); } xpos += a + b + c; } break; } } ResetCharacters(); } void CHudMessage::GetLength( int *wide, int *tall, const char *string ) { GetTextExtents( wide, tall, string ); }