/*** * * 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. * ****/ // // Message.cpp // // implementation of CHudMessage class // #include "hud.h" #include "cl_util.h" #include #include #include "mod/AvHClientVariables.h" #include "mod/AvHNetworkMessages.h" DECLARE_MESSAGE( m_Message, HudText ) DECLARE_MESSAGE( m_Message, HudText2 ) DECLARE_MESSAGE( m_Message, GameTitle ) // 1 Global client_textmessage_t for custom messages that aren't in the titles.txt client_textmessage_t g_pCustomMessage; char *g_pCustomName = "Custom"; char g_pCustomText[1024]; int CHudMessage::Init(void) { HOOK_MESSAGE( HudText ); HOOK_MESSAGE( HudText2 ); HOOK_MESSAGE( GameTitle ); gHUD.AddHudElem(this); Reset(); return 1; }; int CHudMessage::VidInit( void ) { m_HUD_title_half = gHUD.GetSpriteIndex( "title_half" ); m_HUD_title_life = gHUD.GetSpriteIndex( "title_life" ); return 1; }; const int kDefaultRComp = 25; const int kDefaultGComp = 255; const int kDefaultBComp = 25; const int kEnemyRComp = 255; const int kEnemyGComp = 25; const int kEnemyBComp = 25; 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; // Player id stuff this->mPlayerIDTime = 0; this->mPlayerID = NULL; this->mPlayerIDMessage.effect = 1; this->mPlayerIDMessage.r1 = kDefaultRComp; this->mPlayerIDMessage.g1 = kDefaultGComp; this->mPlayerIDMessage.b1 = kDefaultBComp; this->mPlayerIDMessage.a1 = 255; this->mPlayerIDMessage.g2 = 255; this->mPlayerIDMessage.r2 = this->mPlayerIDMessage.b2 = 25; this->mPlayerIDMessage.a2 = 255; this->SetPlayerIDPosition(); this->mPlayerIDMessage.fadein = 0.04; this->mPlayerIDMessage.fadeout = 0.5; this->mPlayerIDMessage.fxtime = 0.25; this->mPlayerIDMessage.holdtime = 5; this->mPlayerIDMessage.pName = NULL; this->mPlayerIDMessage.pMessage = NULL; } void CHudMessage::SetPlayerIDPosition() { if(gEngfuncs.pfnGetCvarFloat(kvCenterEntityID) || gHUD.GetInTopDownMode()) { this->mPlayerIDMessage.x = -1; // Centered this->mPlayerIDMessage.y = -1; // Centered } else { this->mPlayerIDMessage.x = .3; this->mPlayerIDMessage.y = .9; } } 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; } 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; // Alight right else xPos = x * ScreenWidth(); } if ( xPos + width > ScreenWidth() ) xPos = ScreenWidth() - width; else if ( xPos < 0 ) xPos = 0; return xPos; } 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; } 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 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 ) { m_parms.r = destRed; m_parms.g = destGreen; m_parms.b = destBlue; } else if ( blend <= 0 ) { m_parms.r = srcRed; m_parms.g = srcGreen; m_parms.b = srcBlue; } else { 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 ( m_parms.pMessage->effect == 1 && m_parms.charTime != 0 ) { if ( m_parms.x >= 0 && m_parms.y >= 0 && (m_parms.x + gHUD.m_scrinfo.charWidths[ m_parms.text ]) <= ScreenWidth() ) TextMessageDrawChar( m_parms.x, m_parms.y, m_parms.text, m_parms.pMessage->r2, m_parms.pMessage->g2, m_parms.pMessage->b2 ); } } 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; } } void CHudMessage::MessageDrawScan( client_textmessage_t *pMessage, float time ) { int i, j, length, width; const char *pText; //unsigned char line[80]; unsigned char line[256]; pText = pMessage->pMessage; ASSERT(pText != NULL); // Count lines m_parms.lines = 1; m_parms.time = time; m_parms.pMessage = pMessage; length = 0; width = 0; m_parms.totalWidth = 0; while ( *pText ) { if ( *pText == '\n' ) { m_parms.lines++; if ( width > m_parms.totalWidth ) m_parms.totalWidth = width; width = 0; } else width += gHUD.m_scrinfo.charWidths[*pText]; pText++; length++; } m_parms.length = length; m_parms.totalHeight = (m_parms.lines * gHUD.m_scrinfo.iCharHeight); m_parms.y = YPosition( pMessage->y, m_parms.totalHeight ); pText = pMessage->pMessage; m_parms.charTime = 0; MessageScanStart(); for ( i = 0; i < m_parms.lines; i++ ) { m_parms.lineLength = 0; m_parms.width = 0; while ( *pText && *pText != '\n' ) { unsigned char c = *pText; line[m_parms.lineLength] = c; m_parms.width += gHUD.m_scrinfo.charWidths[c]; m_parms.lineLength++; pText++; } pText++; // Skip LF line[m_parms.lineLength] = 0; m_parms.x = XPosition( pMessage->x, m_parms.width, m_parms.totalWidth ); for ( j = 0; j < m_parms.lineLength; j++ ) { m_parms.text = line[j]; int next = m_parms.x + gHUD.m_scrinfo.charWidths[ m_parms.text ]; MessageScanNextChar(); if ( m_parms.x >= 0 && m_parms.y >= 0 && next <= ScreenWidth() ) TextMessageDrawChar( m_parms.x, m_parms.y, m_parms.text, m_parms.r, m_parms.g, m_parms.b ); m_parms.x = next; } m_parms.y += gHUD.m_scrinfo.iCharHeight; } } int CHudMessage::Draw( float fTime ) { int i, drawn; client_textmessage_t *pMessage; drawn = 0; if ( m_gameTitleTime > 0 ) { float localTime = gHUD.m_flTime - m_gameTitleTime; float brightness; // Maybe timer isn't set yet if ( m_gameTitleTime > gHUD.m_flTime ) m_gameTitleTime = gHUD.m_flTime; 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 = gHUD.GetSpriteRect(m_HUD_title_half).right - gHUD.GetSpriteRect(m_HUD_title_half).left; int fullWidth = halfWidth + gHUD.GetSpriteRect(m_HUD_title_life).right - gHUD.GetSpriteRect(m_HUD_title_life).left; int fullHeight = gHUD.GetSpriteRect(m_HUD_title_half).bottom - gHUD.GetSpriteRect(m_HUD_title_half).top; int x = XPosition( m_pGameTitle->x, fullWidth, fullWidth ); int y = YPosition( m_pGameTitle->y, fullHeight ); SPR_Set( gHUD.GetSprite(m_HUD_title_half), brightness * m_pGameTitle->r1, brightness * m_pGameTitle->g1, brightness * m_pGameTitle->b1 ); SPR_DrawAdditive( 0, x, y, &gHUD.GetSpriteRect(m_HUD_title_half) ); SPR_Set( gHUD.GetSprite(m_HUD_title_life), brightness * m_pGameTitle->r1, brightness * m_pGameTitle->g1, brightness * m_pGameTitle->b1 ); SPR_DrawAdditive( 0, x + halfWidth, y, &gHUD.GetSpriteRect(m_HUD_title_life) ); drawn++; } } // Draw player id // if(this->mPlayerIDTime > 0) // { // float localTime = gHUD.m_flTime - this->mPlayerIDTime; // //float brightness; // // // Maybe timer isn't set yet // if ( this->mPlayerIDTime > gHUD.m_flTime ) // this->mPlayerIDTime = gHUD.m_flTime; // // if ( localTime > (this->mPlayerIDMessage.fadein + this->mPlayerIDMessage.holdtime + this->mPlayerIDMessage.fadeout) ) // { // this->mPlayerIDTime = 0; // this->mPlayerIDMessage.pName = NULL; // this->mPlayerIDMessage.pMessage = NULL; // } // else // { // drawn++; // } // } // 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] > gHUD.m_flTime ) m_startTime[i] = gHUD.m_flTime + m_parms.time - m_startTime[i] + 0.2; // Server takes 0.2 seconds to spawn, adjust for this } } for ( i = 0; i < maxHUDMessages; i++ ) { client_textmessage_t* theMessage = this->m_pMessages[i]; if(theMessage) { if(this->DrawMessage(theMessage, this->m_startTime[i], fTime)) { drawn++; } else { // The message is over this->m_pMessages[i] = NULL; } } } if(this->DrawMessage(&this->mPlayerIDMessage, this->mPlayerIDTime, fTime)) { drawn++; } else { this->mPlayerIDMessage.pName = NULL; this->mPlayerIDMessage.pMessage = NULL; } // Remember the time -- to fix up level transitions m_parms.time = gHUD.m_flTime; // Don't call until we get another message if ( !drawn ) m_iFlags &= ~HUD_ACTIVE; return 1; } bool CHudMessage::DrawMessage(client_textmessage_t* inMessage, float inStartTime, float inCurrentTime) { ASSERT(inMessage); bool theDrewMessage = false; if(inMessage->pMessage) { float endTime; // This is when the message is over switch( inMessage->effect ) { case 0: case 1: endTime = inStartTime + inMessage->fadein + inMessage->fadeout + inMessage->holdtime; break; // Fade in is per character in scanning messages case 2: endTime = inStartTime + (inMessage->fadein * strlen( inMessage->pMessage )) + inMessage->fadeout + inMessage->holdtime; break; } if ( inCurrentTime <= endTime ) { float messageTime = inCurrentTime - inStartTime; // Draw the message // effect 0 is fade in/fade out // effect 1 is flickery credits // effect 2 is write out (training room) MessageDrawScan( inMessage, messageTime ); theDrewMessage = true; } } return theDrewMessage; } void CHudMessage::MessageAdd( const char *pName, float time ) { int i,j; client_textmessage_t *tempMessage; for ( i = 0; i < maxHUDMessages; i++ ) { if ( !m_pMessages[i] ) { // Trim off a leading # if it's there if ( pName[0] == '#' ) tempMessage = TextMessageGet( pName+1 ); else tempMessage = TextMessageGet( pName ); // If we couldnt find it in the titles.txt, just create it if ( !tempMessage ) { g_pCustomMessage.effect = 2; g_pCustomMessage.r1 = g_pCustomMessage.g1 = g_pCustomMessage.b1 = g_pCustomMessage.a1 = 100; g_pCustomMessage.r2 = 240; g_pCustomMessage.g2 = 110; g_pCustomMessage.b2 = 0; g_pCustomMessage.a2 = 0; g_pCustomMessage.x = -1; // Centered g_pCustomMessage.y = 0.7; g_pCustomMessage.fadein = 0.01; g_pCustomMessage.fadeout = 1.5; g_pCustomMessage.fxtime = 0.25; g_pCustomMessage.holdtime = 5; g_pCustomMessage.pName = g_pCustomName; strcpy( g_pCustomText, pName ); g_pCustomMessage.pMessage = g_pCustomText; tempMessage = &g_pCustomMessage; } for ( j = 0; j < maxHUDMessages; j++ ) { if ( m_pMessages[j] ) { // is this message already in the list if ( !strcmp( tempMessage->pMessage, m_pMessages[j]->pMessage ) ) { return; } // get rid of any other messages in same location (only one displays at a time) if ( fabs( tempMessage->y - m_pMessages[j]->y ) < 0.0001 ) { if ( fabs( tempMessage->x - m_pMessages[j]->x ) < 0.0001 ) { m_pMessages[j] = NULL; } } } } m_pMessages[i] = tempMessage; m_startTime[i] = time; return; } } } void CHudMessage::MessageAddPlayerID(const char* inName, bool inEnemy) { // If we're already drawing this, extend the time //if(inName && this->mPlayerIDMessage.pMessage && !strcmp(inName, this->mPlayerIDMessage.pMessage)) //{ // //this->mPlayerIDMessage.holdtime = //} //else //{ // else add this message this->mPlayerIDMessage.pMessage = inName; //strcpy( g_pCustomText, pName ); //this->mPlayerIDMessage.pMessage = g_pCustomText; this->mPlayerIDTime = gHUD.m_flTime; // Set color depending on if friend or enemy if(inEnemy) { this->mPlayerIDMessage.r1 = kEnemyRComp; this->mPlayerIDMessage.g1 = kEnemyGComp; this->mPlayerIDMessage.b1 = kEnemyBComp; } else { this->mPlayerIDMessage.r1 = kDefaultRComp; this->mPlayerIDMessage.g1 = kDefaultGComp; this->mPlayerIDMessage.b1 = kDefaultBComp; } this->SetPlayerIDPosition(); // Turn on drawing if(inName) { if ( !(m_iFlags & HUD_ACTIVE) ) m_iFlags |= HUD_ACTIVE; } //} } bool CHudMessage::MessageRemove(const char *pName) { const char* theMessage = pName; bool theSuccess = false; bool theNeedsTranslation = (pName[0] == '#'); // Trim off a leading # if it's there if(theNeedsTranslation) { client_textmessage_t* tempMessage = TextMessageGet(pName + 1); if(tempMessage) { theMessage = tempMessage->pMessage; } } if(theMessage) { for (int j = 0; j < maxHUDMessages; j++ ) { if ( m_pMessages[j] ) { // is this message already in the list if (!strcmp(theMessage, m_pMessages[j]->pMessage)) { m_startTime[j] = -1; theSuccess = true; } } } } return theSuccess; } int CHudMessage::MsgFunc_HudText( const char *pszName, int iSize, void *pbuf ) { string content; NetMsg_HudText( pbuf, iSize, content ); MessageAdd( content.c_str(), gHUD.m_flTime ); // Remember the time -- to fix up level transitions m_parms.time = gHUD.m_flTime; // Turn on drawing if ( !(m_iFlags & HUD_ACTIVE) ) m_iFlags |= HUD_ACTIVE; return 1; } int CHudMessage::MsgFunc_HudText2( const char *pszName, int iSize, void *pbuf ) { string content; int flags; NetMsg_HudText2( pbuf, iSize, content, flags ); bool theIsAutoHelp = (flags & 1) != 0; gHUD.AddTooltip(content.c_str(), theIsAutoHelp); return 2; } int CHudMessage::MsgFunc_GameTitle( const char *pszName, int iSize, void *pbuf ) { m_pGameTitle = TextMessageGet( "GAMETITLE" ); if ( m_pGameTitle != NULL ) { m_gameTitleTime = gHUD.m_flTime; // Turn on drawing if ( !(m_iFlags & HUD_ACTIVE) ) m_iFlags |= HUD_ACTIVE; } return 1; }