Counter Strike : Global Offensive Source Code
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.
 
 
 
 
 
 

665 lines
20 KiB

//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#include "gametext.h"
#include "vgui/ilocalize.h"
#include "vgui/vgui.h"
#include <ctype.h>
#include "gameuisystemsurface.h"
#include "gameuisystemmgr.h"
#include "gameuischeme.h"
#include "graphicgroup.h"
#include "gameuidefinition.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// Class factory for scripting.
class CGameTextClassFactory : IGameUIGraphicClassFactory
{
public:
CGameTextClassFactory()
{
Assert( g_pGameUISystemMgrImpl );
g_pGameUISystemMgrImpl->RegisterGraphicClassFactory( "text", this );
}
// Returns an instance of a graphic interface (keyvalues owned by caller)
virtual CGameGraphic *CreateNewGraphicClass( KeyValues *kvRequest, CGameUIDefinition *pMenu )
{
Assert( pMenu );
CGameText *pNewGraphic = NULL;
const char *pName = kvRequest->GetString( "name", NULL );
if ( pName )
{
pNewGraphic = new CGameText( pName );
pMenu->AddGraphicToLayer( pNewGraphic, SUBLAYER_FONT );
// Now set the attributes.
for ( KeyValues *arg = kvRequest->GetFirstSubKey(); arg != NULL; arg = arg->GetNextKey() )
{
pNewGraphic->HandleScriptCommand( arg );
}
}
return pNewGraphic;
}
};
static CGameTextClassFactory g_CGameTextClassFactory;
BEGIN_DMXELEMENT_UNPACK ( CGameText )
DMXELEMENT_UNPACK_FIELD_UTLSTRING( "name", "", m_pName )
DMXELEMENT_UNPACK_FIELD( "center", "0 0", Vector2D, m_Geometry.m_Center )
DMXELEMENT_UNPACK_FIELD( "scale", "1 1", Vector2D, m_Geometry.m_Scale )
DMXELEMENT_UNPACK_FIELD( "rotation", "0", float, m_Geometry.m_Rotation )
DMXELEMENT_UNPACK_FIELD( "maintainaspectratio", "0", bool, m_Geometry.m_bMaintainAspectRatio )
DMXELEMENT_UNPACK_FIELD( "sublayertype", "0", int, m_Geometry.m_Sublayer )
DMXELEMENT_UNPACK_FIELD( "visible", "1", bool, m_Geometry.m_bVisible )
DMXELEMENT_UNPACK_FIELD( "initialstate", "-1", int, m_CurrentState )
DMXELEMENT_UNPACK_FIELD_UTLSTRING( "unlocalizedtext", "NONE", m_CharText )
DMXELEMENT_UNPACK_FIELD( "allcaps", "0", bool, m_bAllCaps )
DMXELEMENT_UNPACK_FIELD_UTLSTRING( "fontname", "Default", m_FontName )
DMXELEMENT_UNPACK_FIELD( "justfication", "0", int, m_Justification )
DMXELEMENT_UNPACK_FIELD( "color", "255 255 255 255", Color, m_Geometry.m_Color )
DMXELEMENT_UNPACK_FIELD( "topcolor", "255 255 255 255", Color, m_Geometry.m_TopColor )
DMXELEMENT_UNPACK_FIELD( "bottomcolor", "255 255 255 255", Color, m_Geometry.m_BottomColor )
DMXELEMENT_UNPACK_FIELD( "horizgradient", "0", bool, m_Geometry.m_bHorizontalGradient )
// color is gotten from log.
END_DMXELEMENT_UNPACK( CGameText, s_GameTextUnpack )
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CGameText::CGameText( const char *pName )
{
m_UnicodeText = NULL;
m_TextBufferLen = 0;
m_UnlocalizedTextSymbol = vgui::INVALID_STRING_INDEX;
m_Font = vgui::INVALID_FONT;
m_bCanAcceptInput = false;
// DME default values.
m_pName = pName;
m_Geometry.m_Center.x = 0;
m_Geometry.m_Center.y = 0;
m_Geometry.m_Scale.x = 1;
m_Geometry.m_Scale.y = 1;
m_Geometry.m_Rotation = 0;
m_Geometry.m_bMaintainAspectRatio = 1;
m_Geometry.m_Sublayer = 0;
m_Geometry.m_bVisible = true;
m_CurrentState = -1;
m_CharText = "NONE";
m_bAllCaps = false;
m_FontName = "Default";
m_Justification = JUSTIFICATION_LEFT;
m_Geometry.m_Color.r = 255;
m_Geometry.m_Color.g = 255;
m_Geometry.m_Color.b = 255;
m_Geometry.m_Color.a = 255;
m_Geometry.m_TopColor.r = 255;
m_Geometry.m_TopColor.g = 255;
m_Geometry.m_TopColor.b = 255;
m_Geometry.m_TopColor.a = 255;
m_Geometry.m_BottomColor.r = 255;
m_Geometry.m_BottomColor.g = 255;
m_Geometry.m_BottomColor.b = 255;
m_Geometry.m_BottomColor.a = 255;
m_Geometry.m_bHorizontalGradient = false;
SetText( m_CharText );
SetFont( m_FontName );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CGameText::~CGameText()
{
if ( m_UnicodeText )
{
delete[] m_UnicodeText;
m_UnicodeText = NULL;
}
};
//-----------------------------------------------------------------------------
// Create game text using dme elements
//-----------------------------------------------------------------------------
bool CGameText::Unserialize( CDmxElement *pGraphic )
{
pGraphic->UnpackIntoStructure( this, s_GameTextUnpack );
// GEOMETRY
// Geometry for text is generated. This is because you don't know it until you know the localized text.
// ANIMSTATES
CDmxAttribute *pImageAnims = pGraphic->GetAttribute( "imageanims" );
if ( !pImageAnims || pImageAnims->GetType() != AT_ELEMENT_ARRAY )
{
return false;
}
const CUtlVector< CDmxElement * > &imageanims = pImageAnims->GetArray< CDmxElement * >( );
int nCount = imageanims.Count();
for ( int i = 0; i < nCount; ++i )
{
CAnimData *pAnimData = new CAnimData;
if ( !pAnimData->Unserialize( imageanims[i] ) )
{
delete pAnimData;
return false;
}
m_Anims.AddToTail( pAnimData );
}
SetText( m_CharText );
SetFont( m_FontName );
SetState( "default" );
return true;
}
//-----------------------------------------------------------------------------
// The attributes that can be modified by scripting are a subset of the DME ones.
//-----------------------------------------------------------------------------
KeyValues *CGameText::HandleScriptCommand( KeyValues *args )
{
char const *szCommand = args->GetName();
if ( !Q_stricmp( "SetText", szCommand ) )
{
const char *text = args->GetString( "text" );
SetText( text );
return NULL;
}
else if ( !Q_stricmp( "SetAllCaps", szCommand ) )
{
m_bAllCaps = args->GetBool( "allcaps", false );
return NULL;
}
else if ( !Q_stricmp( "SetFont", szCommand ) )
{
SetFont( args->GetString( "fontname" ) );
return NULL;
}
else if ( !Q_stricmp( "SetJustification", szCommand ) )
{
// FIXME
m_Justification = args->GetInt( "justfication", 0 );
return NULL;
}
else if ( !Q_stricmp( "SetTopColor", szCommand ) )
{
Color c = args->GetColor( "color", Color( 255, 255, 255, 255 ) );
m_Geometry.m_TopColor.r = c[0];
m_Geometry.m_TopColor.g = c[1];
m_Geometry.m_TopColor.b = c[2];
m_Geometry.m_TopColor.a = c[3];
return NULL;
}
else if ( !Q_stricmp( "SetBottomColor", szCommand ) )
{
Color c = args->GetColor( "color", Color( 255, 255, 255, 255 ) );
m_Geometry.m_BottomColor.r = c[0];
m_Geometry.m_BottomColor.g = c[1];
m_Geometry.m_BottomColor.b = c[2];
m_Geometry.m_BottomColor.a = c[3];
return NULL;
}
else if ( !Q_stricmp( "GetFont", szCommand ) )
{
return new KeyValues( "", "font", m_FontName.Get() );
}
return CGameGraphic::HandleScriptCommand( args );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CGameText::SetFont( const char *pFontName )
{
m_FontName = pFontName;
if ( m_FontName.Length() )
{
m_Font = g_pGameUISystemMgrImpl->GetCurrentScheme()->GetFont( m_FontName, true );
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CGameText::SetFont( FontHandle_t font )
{
m_Font = font;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
FontHandle_t CGameText::GetFont()
{
return m_Font;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CGameText::SetJustification( Justification_e justify )
{
m_Justification = justify;
}
//-----------------------------------------------------------------------------
// Purpose: takes the string and looks it up in the localization file to convert it to unicode
//-----------------------------------------------------------------------------
void CGameText::SetText( const char *text )
{
if ( !text )
{
text = "";
}
// check for localization
if ( *text == '#' )
{
// try lookup in localization tables
m_UnlocalizedTextSymbol = g_pVGuiLocalize->FindIndex( text + 1 );
if ( m_UnlocalizedTextSymbol != vgui::INVALID_STRING_INDEX )
{
wchar_t *unicode = g_pVGuiLocalize->GetValueByIndex( m_UnlocalizedTextSymbol );
SetText(unicode);
return;
}
}
// convert the ansi string to unicode and use that
wchar_t unicode[1024];
g_pVGuiLocalize->ConvertANSIToUnicode( text, unicode, sizeof(unicode) );
SetText( unicode );
}
//-----------------------------------------------------------------------------
// Purpose: sets unicode text directly
//-----------------------------------------------------------------------------
void CGameText::SetText( const wchar_t *unicode, bool bClearUnlocalizedSymbol )
{
if ( bClearUnlocalizedSymbol )
{
// Clear out unlocalized text symbol so that changing dialog variables
// doesn't stomp over the custom unicode string we're being set to.
m_UnlocalizedTextSymbol = vgui::INVALID_STRING_INDEX;
}
if (!unicode)
{
unicode = L"";
}
// reallocate the buffer if necessary
short textLen = (short)wcslen( unicode );
if ( textLen >= m_TextBufferLen )
{
if ( m_UnicodeText )
{
delete [] m_UnicodeText;
m_UnicodeText = NULL;
}
m_TextBufferLen = (short)( textLen + 1 );
m_UnicodeText = new wchar_t[ m_TextBufferLen ];
}
// store the text as unicode
wcscpy( m_UnicodeText, unicode );
SetupVertexColors();
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CGameText::UpdateGeometry()
{
if ( m_CurrentState == -1 )
return;
Assert( m_CurrentState < m_Anims.Count() );
DmeTime_t flAnimTime = GetAnimationTimePassed();
// Update color
m_Anims[ m_CurrentState ]->m_ColorAnim.GetValue( flAnimTime, &m_Geometry.m_Color );
// Update center location
m_Anims[ m_CurrentState ]->m_CenterPosAnim.GetValue( flAnimTime, &m_Geometry.m_Center );
// Update scale
m_Anims[ m_CurrentState ]->m_ScaleAnim.GetValue( flAnimTime, &m_Geometry.m_Scale );
// Update rotation
m_Anims[ m_CurrentState ]->m_RotationAnim.GetValue( flAnimTime, &m_Geometry.m_Rotation );
// Update rotation
m_Anims[ m_CurrentState ]->m_FontAnim.GetValue( flAnimTime, &m_FontName );
SetFont( m_FontName );
}
//-----------------------------------------------------------------------------
// Rendering helper
// For text rendering the starting position is top left
// Center text according to justification.
//-----------------------------------------------------------------------------
void CGameText::GetStartingTextPosition( int &x, int &y )
{
x = 0;
y = -GetTextRenderHeight()/2;
switch ( m_Justification )
{
case JUSTIFICATION_LEFT:
break;
case JUSTIFICATION_CENTER:
x = -GetTextRenderWidth()/2;
break;
case JUSTIFICATION_RIGHT:
x = -GetTextRenderWidth();
break;
default:
Assert(0);
break;
}
}
//-----------------------------------------------------------------------------
// Rendering helper
// Determine what list to put this quad into, and make an entry slot for it.
//-----------------------------------------------------------------------------
CRenderGeometry *CGameText::GetGeometryEntry( CUtlVector< RenderGeometryList_t > &renderGeometryLists, int firstListIndex, int fontTextureID )
{
CRenderGeometry *pRenderGeometry = NULL;
if ( renderGeometryLists[firstListIndex].Count() != 0 )
{
for ( int j = firstListIndex; j < renderGeometryLists.Count(); ++j )
{
if ( fontTextureID == renderGeometryLists[j][0].m_FontTextureID )
{
int index = renderGeometryLists[j].AddToTail();
pRenderGeometry = &renderGeometryLists[j][index];
break;
}
}
}
// Didn't find a match for this textureID, time to make a new list of quads for this texture.
if ( pRenderGeometry == NULL )
{
int newListIndex;
if ( renderGeometryLists[firstListIndex].Count() == 0 ) // This is the first quad we are adding to this font layer.
{
newListIndex = firstListIndex;
}
else
{
newListIndex = renderGeometryLists.AddToTail();
}
int index = renderGeometryLists[newListIndex].AddToTail();
pRenderGeometry = &renderGeometryLists[newListIndex][index];
}
return pRenderGeometry;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CGameText::SetupVertexColors()
{
// There is no text to generate if these are not set.
if ( !m_UnicodeText )
return;
m_Geometry.m_VertexColors.RemoveAll();
color32 c;
c.r = 255;
c.g = 255;
c.b = 255;
c.a = 255;
for ( wchar_t *wsz = m_UnicodeText; *wsz != 0; wsz++ )
{
// Create 4 vertex colors per letter.
m_Geometry.m_VertexColors.AddToTail( c );
m_Geometry.m_VertexColors.AddToTail( c );
m_Geometry.m_VertexColors.AddToTail( c );
m_Geometry.m_VertexColors.AddToTail( c );
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CGameText::UpdateRenderData( color32 parentColor, CUtlVector< RenderGeometryList_t > &renderGeometryLists, int firstListIndex )
{
if ( !m_Geometry.m_bVisible )
return;
// There is no text to generate if these are not set.
if ( !m_UnicodeText )
return;
if ( m_Font == vgui::INVALID_FONT)
return;
m_Geometry.SetResultantColor( parentColor );
int x, y;
GetStartingTextPosition( x, y );
FontCharRenderInfo info;
info.currentFont = m_Font;
info.drawType = FONT_DRAW_DEFAULT;
int letterIndex = 0;
m_Geometry.m_RelativePositions.RemoveAll();
for ( wchar_t *wsz = m_UnicodeText; *wsz != 0; wsz++, letterIndex += 4 )
{
Assert( letterIndex < m_Geometry.m_VertexColors.Count() );
// Update FontCharRenderInfo
info.x = x;
info.y = y;
info.ch = wsz[0];
if ( m_bAllCaps )
{
info.ch = towupper( info.ch );
}
Vector2D relPositions[4];
g_pGameUISystemSurface->GetUnicodeCharRenderPositions( info, relPositions );
// get the character texture from the cache and the char's texture coords.
float *texCoords = NULL; // note this returns the static from the fonttexturecache... FIXME?
g_pGameUISystemSurface->GetTextureForChar( info, &texCoords );
x += g_pGameUISystemSurface->GetCharacterWidth( m_Font, info.ch );
// Get a geometry from the correct texture list.
CRenderGeometry *pRenderGeometry = GetGeometryEntry( renderGeometryLists, firstListIndex, info.textureId );
Assert( pRenderGeometry != NULL );
// Populate the new entry.
pRenderGeometry->m_FontTextureID = info.textureId;
Vector screenPosition;
Vector relativePosition;
// Top left
relativePosition.Init( relPositions[0].x, relPositions[0].y, 0 );
m_Geometry.m_RelativePositions.AddToTail( Vector2D( relPositions[0].x, relPositions[0].y ) );
VectorTransform( relativePosition, m_Geometry.m_RenderToScreen, screenPosition );
pRenderGeometry->m_Positions.AddToTail( Vector2D( floor( screenPosition.x ), floor( screenPosition.y ) ) );
pRenderGeometry->m_VertexColors.AddToTail( m_Geometry.m_VertexColors[letterIndex] );
pRenderGeometry->m_TextureCoords.AddToTail( Vector2D( texCoords[0], texCoords[1] ) );
// Top right
relativePosition.Init( relPositions[1].x, relPositions[1].y, 0 );
m_Geometry.m_RelativePositions.AddToTail( Vector2D( relPositions[1].x, relPositions[1].y ) );
VectorTransform( relativePosition, m_Geometry.m_RenderToScreen, screenPosition );
pRenderGeometry->m_Positions.AddToTail( Vector2D( floor( screenPosition.x ), floor( screenPosition.y ) ) );
pRenderGeometry->m_VertexColors.AddToTail( m_Geometry.m_VertexColors[letterIndex + 1] );
pRenderGeometry->m_TextureCoords.AddToTail( Vector2D( texCoords[2], texCoords[1] ) );
// Bottom right
relativePosition.Init( relPositions[2].x, relPositions[2].y, 0 );
m_Geometry.m_RelativePositions.AddToTail( Vector2D( relPositions[2].x, relPositions[2].y ) );
VectorTransform( relativePosition, m_Geometry.m_RenderToScreen, screenPosition );
pRenderGeometry->m_Positions.AddToTail( Vector2D( floor( screenPosition.x ), floor( screenPosition.y ) ) );
pRenderGeometry->m_VertexColors.AddToTail( m_Geometry.m_VertexColors[letterIndex + 2] );
pRenderGeometry->m_TextureCoords.AddToTail( Vector2D( texCoords[2], texCoords[3] ) );
// Bottom left
relativePosition.Init( relPositions[3].x, relPositions[3].y, 0 );
m_Geometry.m_RelativePositions.AddToTail( Vector2D( relPositions[3].x, relPositions[3].y ) );
VectorTransform( relativePosition, m_Geometry.m_RenderToScreen, screenPosition );
pRenderGeometry->m_Positions.AddToTail( Vector2D( floor( screenPosition.x ), floor( screenPosition.y ) ) );
pRenderGeometry->m_VertexColors.AddToTail( m_Geometry.m_VertexColors[letterIndex + 3] );
pRenderGeometry->m_TextureCoords.AddToTail( Vector2D( texCoords[0], texCoords[3] ) );
pRenderGeometry->m_AnimationRate = m_Geometry.m_AnimationRate;
pRenderGeometry->m_AnimStartTime = m_Geometry.m_AnimStartTime;
pRenderGeometry->m_bAnimate = m_Geometry.m_bAnimate;
pRenderGeometry->m_pImageAlias = NULL;
}
m_Geometry.CalculateExtents();
}
//-----------------------------------------------------------------------------
// Have to do this separately because extents are drawn as rects.
//-----------------------------------------------------------------------------
void CGameText::DrawExtents( CUtlVector< RenderGeometryList_t > &renderGeometryLists, int firstListIndex )
{
color32 extentLineColor = { 0, 25, 255, 255 };
m_Geometry.DrawExtents( renderGeometryLists, firstListIndex, extentLineColor );
}
//-----------------------------------------------------------------------------
// Purpose: Get the size of a text string in pixels
//-----------------------------------------------------------------------------
void CGameText::GetTextSize( int &wide, int &tall )
{
wide = 0;
tall = 0;
if ( m_Font == vgui::INVALID_FONT )
return;
// For height, use the remapped font
tall = GetTextRenderHeight();
wide = GetTextRenderWidth();
}
//-----------------------------------------------------------------------------
// Return height of text in pixels
//-----------------------------------------------------------------------------
int CGameText::GetTextRenderWidth()
{
int wordWidth = 0;
int textLen = wcslen( m_UnicodeText );
for ( int i = 0; i < textLen; i++ )
{
wchar_t ch = m_UnicodeText[ i ];
if ( m_bAllCaps )
{
ch = towupper( ch );
}
// handle stupid special characters, these should be removed
if ( ch == '&' && m_UnicodeText[ i + 1 ] != 0 )
{
continue;
}
wordWidth += g_pGameUISystemSurface->GetCharacterWidth( m_Font, ch );
}
return wordWidth;
}
//-----------------------------------------------------------------------------
// Return width of text in pixels
//-----------------------------------------------------------------------------
int CGameText::GetTextRenderHeight()
{
return g_pGameUISystemSurface->GetFontTall( m_Font );
}
//-----------------------------------------------------------------------------
// Note corner colors will be stomped if the base graphic's color changes.
//-----------------------------------------------------------------------------
void CGameText::SetColor( color32 c )
{
m_Geometry.m_TopColor = c;
m_Geometry.m_BottomColor = c;
// Remove first to force the new colors in.
m_Geometry.m_VertexColors.RemoveAll();
SetupVertexColors();
}
//-----------------------------------------------------------------------------
// Determine if x,y is inside the graphic.
//-----------------------------------------------------------------------------
bool CGameText::HitTest( int x, int y )
{
if ( !m_Geometry.m_bVisible )
return false;
// Just using extents for now, note extents don't take into account rotation.
Vector2D point0( m_Geometry.m_Extents.m_TopLeft.x, m_Geometry.m_Extents.m_TopLeft.y );
Vector2D point1( m_Geometry.m_Extents.m_BottomRight.x, m_Geometry.m_Extents.m_TopLeft.y );
Vector2D point2( m_Geometry.m_Extents.m_BottomRight.x, m_Geometry.m_Extents.m_BottomRight.y );
Vector2D point3( m_Geometry.m_Extents.m_TopLeft.x, m_Geometry.m_Extents.m_BottomRight.y );
if ( PointTriangleHitTest( point0, point1, point2, Vector2D( x, y ) ) )
{
//Msg( "%d, %d hit\n", x, y );
return true;
}
if ( PointTriangleHitTest( point0, point2, point3, Vector2D( x, y ) ) )
{
//Msg( "%d, %d hit\n", x, y );
return true;
}
return false;
}