/*** * * Copyright (c) 1999, Valve LLC. All rights reserved. * * This product contains software technology licensed from Id * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. * All Rights Reserved. * * Use, distribution, and modification of this source code and/or resulting * object code is restricted to non-commercial enhancements to products from * Valve LLC. All other use, distribution, or modification is prohibited * without written permission from Valve LLC. * ****/ // // saytext.cpp // // implementation of CHudSayText class // #include "hud.h" #include "cl_util.h" #include "mod/AvHNetworkMessages.h" #include #include #include "vgui_TeamFortressViewport.h" #include "mod/AvHClientUtil.h" #include "ui/ChatPanel.h" float* GetClientColor(int clientIndex); #define MAX_LINES 5 #define MAX_CHARS_PER_LINE 256 /* it can be less than this, depending on char size */ // allow 20 pixels on either side of the text #define MAX_LINE_WIDTH ( ScreenWidth() - 40 ) #define LINE_START 10 static float SCROLL_SPEED = 5; static char g_szLineBuffer[ MAX_LINES + 1 ][ MAX_CHARS_PER_LINE ]; static float *g_pflNameColors[ MAX_LINES + 1 ]; static int g_iNameLengths[ MAX_LINES + 1 ]; static float flScrollTime = 0; // the time at which the lines next scroll up static int Y_START = 0; static int line_height = 0; DECLARE_MESSAGE( m_SayText, SayText ); int CHudSayText :: Init( void ) { gHUD.AddHudElem( this ); HOOK_MESSAGE( SayText ); InitHUDData(); m_HUD_saytext = gEngfuncs.pfnRegisterVariable( "hud_saytext", "1", 0 ); m_HUD_saytext_time = gEngfuncs.pfnRegisterVariable( "hud_saytext_time", "5", 0 ); m_iFlags |= HUD_INTERMISSION; // is always drawn during an intermission return 1; } void CHudSayText :: InitHUDData( void ) { memset( g_szLineBuffer, 0, sizeof g_szLineBuffer ); memset( g_pflNameColors, 0, sizeof g_pflNameColors ); memset( g_iNameLengths, 0, sizeof g_iNameLengths ); } int CHudSayText :: VidInit( void ) { return 1; } int ScrollTextUp( void ) { ConsolePrint( g_szLineBuffer[0] ); // move the first line into the console buffer g_szLineBuffer[MAX_LINES][0] = 0; memmove( g_szLineBuffer[0], g_szLineBuffer[1], sizeof(g_szLineBuffer) - sizeof(g_szLineBuffer[0]) ); // overwrite the first line memmove( &g_pflNameColors[0], &g_pflNameColors[1], sizeof(g_pflNameColors) - sizeof(g_pflNameColors[0]) ); memmove( &g_iNameLengths[0], &g_iNameLengths[1], sizeof(g_iNameLengths) - sizeof(g_iNameLengths[0]) ); g_szLineBuffer[MAX_LINES-1][0] = 0; if ( g_szLineBuffer[0][0] == ' ' ) // also scroll up following lines { g_szLineBuffer[0][0] = 2; return 1 + ScrollTextUp(); } return 1; } int CHudSayText :: Draw( float flTime ) { int y = Y_START; if ( ( gViewPort && gViewPort->AllowedToPrintText() == FALSE) || !m_HUD_saytext->value ) return 1; // make sure the scrolltime is within reasonable bounds, to guard against the clock being reset flScrollTime = min( flScrollTime, flTime + m_HUD_saytext_time->value ); // make sure the scrolltime is within reasonable bounds, to guard against the clock being reset flScrollTime = min( flScrollTime, flTime + m_HUD_saytext_time->value ); if ( flScrollTime <= flTime ) { if ( *g_szLineBuffer[0] ) { flScrollTime = flTime + m_HUD_saytext_time->value; // push the console up ScrollTextUp(); } else { // buffer is empty, just disable drawing of this section m_iFlags &= ~HUD_ACTIVE; } } for ( int i = 0; i < MAX_LINES; i++ ) { if ( *g_szLineBuffer[i] ) { if ( *g_szLineBuffer[i] == 2 && g_pflNameColors[i] ) { // it's a saytext string static char buf[MAX_PLAYER_NAME_LENGTH+32]; // draw the first x characters in the player color strncpy( buf, g_szLineBuffer[i], min(g_iNameLengths[i], MAX_PLAYER_NAME_LENGTH+32) ); buf[ min(g_iNameLengths[i], MAX_PLAYER_NAME_LENGTH+31) ] = 0; DrawSetTextColor( g_pflNameColors[i][0], g_pflNameColors[i][1], g_pflNameColors[i][2] ); // If we're an alien, move chat over a bit so it doesn't overlap energy bar int theDrawX = LINE_START; //if(gHUD.GetIsAlien()) //{ // theDrawX += .07f*ScreenWidth; //} int x = DrawConsoleString(theDrawX, y, buf ); // color is reset after each string draw DrawConsoleString( x, y, g_szLineBuffer[i] + g_iNameLengths[i] ); } else { // normal draw DrawConsoleString( LINE_START, y, g_szLineBuffer[i] ); } } y += line_height; } return 1; } int CHudSayText :: MsgFunc_SayText( const char *pszName, int iSize, void *pbuf ) { int client_index; string content, location; NetMsg_SayText( pbuf, iSize, client_index, content, location ); string theTranslatedLocation; if(LocalizeString(location.c_str(), theTranslatedLocation)) { // If player is on our team, add location cl_entity_s* theEntity = gEngfuncs.GetEntityByIndex(client_index); cl_entity_s* theLocalPlayer = gEngfuncs.GetLocalPlayer(); if(theEntity && theLocalPlayer && (theEntity->curstate.team == theLocalPlayer->curstate.team)) { // Search for first : so we can insert location int theColonIndex = (int)content.find_first_of(":"); if((theColonIndex > 0) && (theColonIndex < (int)content.length())) { AvHCUTrimExtraneousLocationText(theTranslatedLocation); // Insert location string theNewMessage = content.substr(0, theColonIndex); theNewMessage += " ("; theNewMessage += theTranslatedLocation; theNewMessage += ")"; theNewMessage += content.substr(theColonIndex); // Replace the message with new one content = theNewMessage; } } } SayTextPrint(content.c_str(), (int)content.length(), client_index ); return 1; } void CHudSayText :: SayTextPrint( const char *pszBuf, int iBufSize, int clientIndex ) { if ( gViewPort && gViewPort->AllowedToPrintText() == FALSE ) { // Print it straight to the console ConsolePrint( pszBuf ); return; } // find an empty string slot for ( int i = 0; i < MAX_LINES; i++ ) { if ( ! *g_szLineBuffer[i] ) break; } if ( i == MAX_LINES ) { // force scroll buffer up ScrollTextUp(); i = MAX_LINES - 1; } g_iNameLengths[i] = 0; g_pflNameColors[i] = NULL; // if it's a say message, search for the players name in the string if ( *pszBuf == 2 && clientIndex > 0 ) { GetPlayerInfo( clientIndex, &g_PlayerInfoList[clientIndex] ); const char *pName = g_PlayerInfoList[clientIndex].name; if ( pName ) { const char *nameInString = strstr( pszBuf, pName ); if ( nameInString ) { g_iNameLengths[i] = (int)strlen( pName ) + (nameInString - pszBuf); g_pflNameColors[i] = GetClientColor(clientIndex); } } } // puzl: 0001087 // don't strip last character ( often resulted in no carriage returns in the log ) strncpy( g_szLineBuffer[i], pszBuf, min(iBufSize, MAX_CHARS_PER_LINE-1) ); // make sure the text fits in one line EnsureTextFitsInOneLineAndWrapIfHaveTo( i ); // Set scroll time if ( i == 0 ) { flScrollTime = gHUD.m_flTime + m_HUD_saytext_time->value; } m_iFlags |= HUD_ACTIVE; //PlaySound( "misc/talk.wav", 1 ); gHUD.PlayHUDSound("misc/talk.wav", 1); Y_START = ScreenHeight()*.7f; ChatPanel* theChatPanel = gViewPort->GetChatPanel(); if (theChatPanel != NULL) { int theX; int theY; int theWidth; int theHeight; gViewPort->GetChatPanel()->getPos(theX, theY); gViewPort->GetChatPanel()->getSize(theWidth, theHeight); //Y_START = theY + theHeight + 5; //voogru: this is too high imo. //KGP: then move the viewport Y_START = theY + theHeight + 5; } } void CHudSayText :: EnsureTextFitsInOneLineAndWrapIfHaveTo( int line ) { int line_width = 0; GetConsoleStringSize( g_szLineBuffer[line], &line_width, &line_height ); if ( (line_width + LINE_START) > MAX_LINE_WIDTH ) { // string is too long to fit on line // scan the string until we find what word is too long, and wrap the end of the sentence after the word int length = LINE_START; int tmp_len = 0; char *last_break = NULL; for ( char *x = g_szLineBuffer[line]; *x != 0; x++ ) { // check for a color change, if so skip past it if ( x[0] == '/' && x[1] == '(' ) { x += 2; // skip forward until past mode specifier while ( *x != 0 && *x != ')' ) x++; if ( *x != 0 ) x++; if ( *x == 0 ) break; } char buf[2]; buf[1] = 0; if ( *x == ' ' && x != g_szLineBuffer[line] ) // store each line break, except for the very first character last_break = x; buf[0] = *x; // get the length of the current character GetConsoleStringSize( buf, &tmp_len, &line_height ); length += tmp_len; if ( length > MAX_LINE_WIDTH ) { // needs to be broken up if ( !last_break ) last_break = x-1; x = last_break; // find an empty string slot int j; do { for ( j = 0; j < MAX_LINES; j++ ) { if ( ! *g_szLineBuffer[j] ) break; } if ( j == MAX_LINES ) { // need to make more room to display text, scroll stuff up then fix the pointers int linesmoved = ScrollTextUp(); line -= linesmoved; last_break = last_break - (sizeof(g_szLineBuffer[0]) * linesmoved); } } while ( j == MAX_LINES ); // copy remaining string into next buffer, making sure it starts with a space character if ( (char)*last_break == (char)' ' ) { int linelen = (int)strlen(g_szLineBuffer[j]); int remaininglen = (int)strlen(last_break); if ( (linelen - remaininglen) <= MAX_CHARS_PER_LINE ) strcat( g_szLineBuffer[j], last_break ); } else { if ( (strlen(g_szLineBuffer[j]) - strlen(last_break) - 2) < MAX_CHARS_PER_LINE ) { strcat( g_szLineBuffer[j], " " ); strcat( g_szLineBuffer[j], last_break ); } } *last_break = 0; // cut off the last string EnsureTextFitsInOneLineAndWrapIfHaveTo( j ); break; } } } }