diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index a02e49ad5..16b42d758 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -25,6 +25,7 @@ MACRO_CONFIG_INT(ClAutoScreenshot, cl_auto_screenshot, 0, 0, 1, CFGFLAG_SAVE|CFG MACRO_CONFIG_INT(ClAutoScreenshotMax, cl_auto_screenshot_max, 10, 0, 1000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Maximum number of automatically created screenshots (0 = no limit)") MACRO_CONFIG_INT(ClShowServerBroadcast, cl_show_server_broadcast, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Show server broadcast") +MACRO_CONFIG_INT(ClEnableColoredBroadcast, cl_enable_colored_broadcast, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Eanble colored server broadcasts") MACRO_CONFIG_STR(BrFilterString, br_filter_string, 25, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Server browser filtering string") diff --git a/src/game/client/components/broadcast.cpp b/src/game/client/components/broadcast.cpp index 84bd3882c..82fe80b3a 100644 --- a/src/game/client/components/broadcast.cpp +++ b/src/game/client/components/broadcast.cpp @@ -8,11 +8,214 @@ #include -#include -#include - #include "broadcast.h" +#include "chat.h" +#include "scoreboard.h" +#include "motd.h" +inline bool IsCharANum(char c) +{ + return c >= '0' && c <= '9'; +} + +inline int WordLengthBack(const char *pText, int MaxChars) +{ + int s = 0; + while(MaxChars--) + { + if((*pText == '\n' || *pText == '\t' || *pText == ' ')) + return s; + pText--; + s++; + } + return 0; +} + +inline bool IsCharWhitespace(char c) +{ + return c == '\n' || c == '\t' || c == ' '; +} + +void CBroadcast::RenderServerBroadcast() +{ + if(!g_Config.m_ClShowServerBroadcast || m_pClient->m_MuteServerBroadcast) + return; + + const bool ColoredBroadcastEnabled = g_Config.m_ClEnableColoredBroadcast; + const float Height = 300; + const float Width = Height*Graphics()->ScreenAspect(); + + const float DisplayDuration = 10.0f; + const float DisplayStartFade = 9.0f; + const float DeltaTime = Client()->LocalTime() - m_SrvBroadcastReceivedTime; + + if(m_aSrvBroadcastMsg[0] == 0 || DeltaTime > DisplayDuration) + return; + + if(m_pClient->m_pChat->IsActive()) + return; + + const float Fade = 1.0f - max(0.0f, (DeltaTime - DisplayStartFade) / (DisplayDuration - DisplayStartFade)); + + CUIRect ScreenRect = {0, 0, Width, Height}; + + CUIRect BcView = ScreenRect; + BcView.x += Width * 0.25f; + BcView.y += Height * 0.8f; + BcView.w *= 0.5f; + BcView.h *= 0.2f; + + float FontSize = 11.0f; + + vec4 ColorTop(0, 0, 0, 0); + vec4 ColorBot(0, 0, 0, 0.4f * Fade); + CUIRect BgRect; + BcView.HSplitBottom(10.0f, 0, &BgRect); + + RenderTools()->DrawUIRect4(&BgRect, ColorTop, ColorTop, + ColorBot, ColorBot, 0, 0); + + // server broadcast line + CUIRect TitleRect; + BcView.HSplitBottom(10.0f, &BcView, &TitleRect); + TitleRect.y += 1.5f; + TextRender()->TextColor(1, 1, 1, 0.6f * Fade); + UI()->DoLabel(&TitleRect, Localize("Server broadcast"), 5.5f, CUI::ALIGN_CENTER); + + BcView.VMargin(5.0f, &BcView); + BcView.HSplitBottom(2.0f, &BcView, 0); + + const char* pBroadcastMsg = m_aSrvBroadcastMsg; + const int MsgLen = m_aSrvBroadcastMsgLen; + + // broadcast message + // one line == big font + // > one line == small font + + CTextCursor Cursor; + TextRender()->SetCursor(&Cursor, BcView.x, BcView.y, FontSize, 0); + Cursor.m_LineWidth = BcView.w; + TextRender()->TextEx(&Cursor, pBroadcastMsg, -1); + + // can't fit on one line, reduce size + if(Cursor.m_LineCount > 1) + { + FontSize = 6.5f; // smaller font + TextRender()->SetCursor(&Cursor, BcView.x, BcView.y, FontSize, 0); + Cursor.m_LineWidth = BcView.w; + TextRender()->TextEx(&Cursor, pBroadcastMsg, -1); + } + + // make lines + struct CLineInfo + { + const char* m_pStrStart; + int m_StrLen; + float m_Width; + }; + + CLineInfo aLines[10]; + int CurCharCount = 0; + int LineCount = 0; + + while(CurCharCount < MsgLen) + { + const char* RemainingMsg = pBroadcastMsg + CurCharCount; + + TextRender()->SetCursor(&Cursor, 0, 0, FontSize, TEXTFLAG_STOP_AT_END); + Cursor.m_LineWidth = BcView.w; + + TextRender()->TextEx(&Cursor, RemainingMsg, -1); + int StrLen = Cursor.m_CharCount; + + // don't cut words + if(CurCharCount + StrLen < MsgLen) + { + const int WorldLen = WordLengthBack(RemainingMsg + StrLen, StrLen); + if(WorldLen > 0 && WorldLen < StrLen) + { + StrLen -= WorldLen; + TextRender()->SetCursor(&Cursor, 0, 0, FontSize, TEXTFLAG_STOP_AT_END); + Cursor.m_LineWidth = BcView.w; + TextRender()->TextEx(&Cursor, RemainingMsg, StrLen); + } + } + + const float TextWidth = Cursor.m_X-Cursor.m_StartX; + + CLineInfo Line = { RemainingMsg, StrLen, TextWidth }; + aLines[LineCount++] = Line; + CurCharCount += StrLen; + } + + // draw lines + TextRender()->TextColor(1, 1, 1, 1); + TextRender()->TextOutlineColor(0, 0, 0, 0.3f); + float y = BcView.y + BcView.h - LineCount * FontSize; + + for(int l = 0; l < LineCount; l++) + { + const CLineInfo& Line = aLines[l]; + TextRender()->SetCursor(&Cursor, BcView.x + (BcView.w - Line.m_Width) * 0.5f, y, + FontSize, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); + Cursor.m_LineWidth = BcView.w; + + // draw colored text + if(ColoredBroadcastEnabled) + { + int DrawnStrLen = 0; + int ThisCharPos = Line.m_pStrStart - pBroadcastMsg; + while(DrawnStrLen < Line.m_StrLen) + { + int StartColorID = -1; + int ColorStrLen = -1; + + for(int j = 0; j < m_SrvBroadcastColorCount; j++) + { + if((ThisCharPos+DrawnStrLen) >= m_aSrvBroadcastColorList[j].m_CharPos) + { + StartColorID = j; + } + else if(StartColorID >= 0) + { + ColorStrLen = m_aSrvBroadcastColorList[j].m_CharPos - m_aSrvBroadcastColorList[StartColorID].m_CharPos; + break; + } + } + + dbg_assert(StartColorID >= 0, "This should not be -1, color not found"); + + if(ColorStrLen == -1) + ColorStrLen = Line.m_StrLen-DrawnStrLen; + + const CBcColor& TextColor = m_aSrvBroadcastColorList[StartColorID]; + float r = TextColor.m_R/255.f; + float g = TextColor.m_G/255.f; + float b = TextColor.m_B/255.f; + float AvgLum = 0.2126*r + 0.7152*g + 0.0722*b; + + if(AvgLum < 0.25f) + TextRender()->TextOutlineColor(1, 1, 1, 0.6f); + else + TextRender()->TextOutlineColor(0, 0, 0, 0.3f); + + TextRender()->TextColor(r, g, b, Fade); + + TextRender()->TextEx(&Cursor, Line.m_pStrStart+DrawnStrLen, ColorStrLen); + DrawnStrLen += ColorStrLen; + } + } + else + { + TextRender()->TextEx(&Cursor, Line.m_pStrStart, Line.m_StrLen); + } + + y += FontSize; + } + + TextRender()->TextColor(1, 1, 1, 1); + TextRender()->TextOutlineColor(0, 0, 0, 0.3f); +} CBroadcast::CBroadcast() { @@ -27,7 +230,7 @@ void CBroadcast::DoBroadcast(const char *pText) Cursor.m_LineWidth = 300*Graphics()->ScreenAspect(); TextRender()->TextEx(&Cursor, m_aBroadcastText, -1); m_BroadcastRenderOffset = 150*Graphics()->ScreenAspect()-Cursor.m_X/2; - m_BroadcastTime = time_get()+time_freq()*10; + m_BroadcastTime = Client()->LocalTime() + 10.0f; } void CBroadcast::OnReset() @@ -35,6 +238,53 @@ void CBroadcast::OnReset() m_BroadcastTime = 0; } +void CBroadcast::OnMessage(int MsgType, void* pRawMsg) +{ + // process server broadcast message + if(MsgType == NETMSGTYPE_SV_BROADCAST && g_Config.m_ClShowServerBroadcast && + !m_pClient->m_MuteServerBroadcast) + { + CNetMsg_Sv_Broadcast *pMsg = (CNetMsg_Sv_Broadcast *)pRawMsg; + + // new broadcast message + int MsgLen = str_length(pMsg->m_pMessage); + mem_zero(m_aSrvBroadcastMsg, sizeof(m_aSrvBroadcastMsg)); + m_aSrvBroadcastMsgLen = 0; + m_SrvBroadcastReceivedTime = Client()->LocalTime(); + + const CBcColor White = { 255, 255, 255, 0 }; + m_aSrvBroadcastColorList[0] = White; + m_SrvBroadcastColorCount = 1; + + // parse colors + for(int i = 0; i < MsgLen; i++) + { + const char* c = pMsg->m_pMessage + i; + const char* pTmp = c; + int CharUtf8 = str_utf8_decode(&pTmp); + const int Utf8Len = pTmp-c; + + if(*c == CharUtf8 && *c == '^') + { + if(i+3 < MsgLen && IsCharANum(c[1]) && IsCharANum(c[2]) && IsCharANum(c[3])) + { + u8 r = (c[1] - '0') * 24 + 39; + u8 g = (c[2] - '0') * 24 + 39; + u8 b = (c[3] - '0') * 24 + 39; + CBcColor Color = { r, g, b, m_aSrvBroadcastMsgLen }; + if(m_SrvBroadcastColorCount < MAX_BROADCAST_COLORS) + m_aSrvBroadcastColorList[m_SrvBroadcastColorCount++] = Color; + i += 3; + continue; + } + } + + if(m_aSrvBroadcastMsgLen+Utf8Len < MAX_BROADCAST_MSG_LENGTH) + m_aSrvBroadcastMsg[m_aSrvBroadcastMsgLen++] = *c; + } + } +} + void CBroadcast::OnRender() { if(m_pClient->m_pScoreboard->Active() || m_pClient->m_pMotd->IsActive()) @@ -42,12 +292,16 @@ void CBroadcast::OnRender() Graphics()->MapScreen(0, 0, 300*Graphics()->ScreenAspect(), 300); - if(time_get() < m_BroadcastTime) + // client broadcast + if(Client()->LocalTime() < m_BroadcastTime) { CTextCursor Cursor; TextRender()->SetCursor(&Cursor, m_BroadcastRenderOffset, 40.0f, 12.0f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); Cursor.m_LineWidth = 300*Graphics()->ScreenAspect()-m_BroadcastRenderOffset; TextRender()->TextEx(&Cursor, m_aBroadcastText, -1); } + + // server broadcast + RenderServerBroadcast(); } diff --git a/src/game/client/components/broadcast.h b/src/game/client/components/broadcast.h index 8d514af39..943e3f517 100644 --- a/src/game/client/components/broadcast.h +++ b/src/game/client/components/broadcast.h @@ -7,15 +7,37 @@ class CBroadcast : public CComponent { char m_aBroadcastText[128]; - int64 m_BroadcastTime; + float m_BroadcastTime; float m_BroadcastRenderOffset; + // server broadcast + typedef unsigned char u8; + struct CBcColor + { + u8 m_R, m_G, m_B; + int m_CharPos; + }; + + enum { + MAX_BROADCAST_COLORS = 128, + MAX_BROADCAST_MSG_LENGTH = 127 + }; + + CBcColor m_aSrvBroadcastColorList[MAX_BROADCAST_COLORS]; + char m_aSrvBroadcastMsg[MAX_BROADCAST_MSG_LENGTH+1]; + int m_aSrvBroadcastMsgLen; + int m_SrvBroadcastColorCount; + float m_SrvBroadcastReceivedTime; + + void RenderServerBroadcast(); + public: CBroadcast(); void DoBroadcast(const char *pText); virtual void OnReset(); + virtual void OnMessage(int MsgType, void *pRawMsg); virtual void OnRender(); }; diff --git a/src/game/client/components/hud.cpp b/src/game/client/components/hud.cpp index 86e8cf4a8..9511dd482 100644 --- a/src/game/client/components/hud.cpp +++ b/src/game/client/components/hud.cpp @@ -17,9 +17,6 @@ #include "hud.h" #include "voting.h" #include "binds.h" -#include "chat.h" -#include "scoreboard.h" -#include "motd.h" CHud::CHud() { @@ -27,9 +24,6 @@ CHud::CHud() m_AverageFPS = 1.0f; m_WarmupHideTick = 0; - m_BroadcastColorCount = 0; - m_aBroadcastMsg[0] = 0; - m_BroadcastReceivedTime = 0; } void CHud::OnReset() @@ -37,58 +31,6 @@ void CHud::OnReset() m_WarmupHideTick = 0; } -inline bool IsCharANum(char c) -{ - return c >= '0' && c <= '9'; -} - -void CHud::OnMessage(int MsgType, void* pRawMsg) -{ - // process server broadcast message - if(MsgType == NETMSGTYPE_SV_BROADCAST && g_Config.m_ClShowServerBroadcast && - !m_pClient->m_MuteServerBroadcast) - { - CNetMsg_Sv_Broadcast *pMsg = (CNetMsg_Sv_Broadcast *)pRawMsg; - - // new broadcast message - int MsgLen = str_length(pMsg->m_pMessage); - mem_zero(m_aBroadcastMsg, sizeof(m_aBroadcastMsg)); - m_aBroadcastMsgLen = 0; - m_BroadcastReceivedTime = Client()->LocalTime(); - - const CBcColor White = { 255, 255, 255, 0 }; - m_aBroadcastColorList[0] = White; - m_BroadcastColorCount = 1; - - // parse colors - for(int i = 0; i < MsgLen; i++) - { - const char* c = pMsg->m_pMessage + i; - const char* pTmp = c; - int CharUtf8 = str_utf8_decode(&pTmp); - const int Utf8Len = pTmp-c; - - if(*c == CharUtf8 && *c == '^') - { - if(i+3 < MsgLen && IsCharANum(c[1]) && IsCharANum(c[2]) && IsCharANum(c[3])) - { - u8 r = (c[1] - '0') * 24 + 39; - u8 g = (c[2] - '0') * 24 + 39; - u8 b = (c[3] - '0') * 24 + 39; - CBcColor Color = { r, g, b, m_aBroadcastMsgLen }; - if(m_BroadcastColorCount < MAX_BROADCAST_COLORS) - m_aBroadcastColorList[m_BroadcastColorCount++] = Color; - i += 3; - continue; - } - } - - if(m_aBroadcastMsgLen+Utf8Len < MAX_BROADCAST_MSG_LENGTH) - m_aBroadcastMsg[m_aBroadcastMsgLen++] = *c; - } - } -} - void CHud::RenderGameTimer() { float Half = 300.0f*Graphics()->ScreenAspect()/2.0f; @@ -750,7 +692,6 @@ void CHud::OnRender() RenderSuddenDeath(); RenderScoreHud(); RenderWarmupTimer(); - RenderBroadcast(); RenderFps(); if(Client()->State() != IClient::STATE_DEMOPLAYBACK) RenderConnectionWarning(); @@ -759,189 +700,3 @@ void CHud::OnRender() } RenderCursor(); } - -inline int WordLengthBack(const char *pText, int MaxChars) -{ - int s = 0; - while(MaxChars--) - { - if((*pText == '\n' || *pText == '\t' || *pText == ' ')) - return s; - pText--; - s++; - } - return 0; -} - -inline bool IsCharWhitespace(char c) -{ - return c == '\n' || c == '\t' || c == ' '; -} - -void CHud::RenderBroadcast() -{ - if(!g_Config.m_ClShowServerBroadcast || m_pClient->m_MuteServerBroadcast) - return; - - const float DisplayDuration = 10.0f; - const float DisplayStartFade = 9.0f; - const float DeltaTime = Client()->LocalTime() - m_BroadcastReceivedTime; - - if(m_aBroadcastMsg[0] == 0 || DeltaTime > DisplayDuration) - return; - - if(m_pClient->m_pScoreboard->Active() || m_pClient->m_pMotd->IsActive() || - m_pClient->m_pChat->IsActive()) - return; - - const float Fade = 1.0f - max(0.0f, (DeltaTime - DisplayStartFade) / (DisplayDuration - DisplayStartFade)); - - CUIRect ScreenRect = {0, 0, m_Width, m_Height}; - - CUIRect BcView = ScreenRect; - BcView.x += m_Width * 0.25f; - BcView.y += m_Height * 0.8f; - BcView.w *= 0.5f; - BcView.h *= 0.2f; - - float FontSize = 11.0f; - - vec4 ColorTop(0, 0, 0, 0); - vec4 ColorBot(0, 0, 0, 0.4f * Fade); - CUIRect BgRect; - BcView.HSplitBottom(10.0f, 0, &BgRect); - - RenderTools()->DrawUIRect4(&BgRect, ColorTop, ColorTop, - ColorBot, ColorBot, 0, 0); - - // server broadcast line - CUIRect TitleRect; - BcView.HSplitBottom(10.0f, &BcView, &TitleRect); - TitleRect.y += 1.5f; - TextRender()->TextColor(1, 1, 1, 0.6f * Fade); - UI()->DoLabel(&TitleRect, Localize("Server broadcast"), 5.5f, CUI::ALIGN_CENTER); - - BcView.VMargin(5.0f, &BcView); - BcView.HSplitBottom(2.0f, &BcView, 0); - - const char* pBroadcastMsg = m_aBroadcastMsg; - const int MsgLen = m_aBroadcastMsgLen; - - // broadcast message - // one line == big font - // > one line == small font - - CTextCursor Cursor; - TextRender()->SetCursor(&Cursor, BcView.x, BcView.y, FontSize, 0); - Cursor.m_LineWidth = BcView.w; - TextRender()->TextEx(&Cursor, pBroadcastMsg, -1); - - // can't fit on one line, reduce size - if(Cursor.m_LineCount > 1) - { - FontSize = 6.5f; // smaller font - TextRender()->SetCursor(&Cursor, BcView.x, BcView.y, FontSize, 0); - Cursor.m_LineWidth = BcView.w; - TextRender()->TextEx(&Cursor, pBroadcastMsg, -1); - } - - // make lines - struct CLineInfo - { - const char* m_pStrStart; - int m_StrLen; - float m_Width; - }; - - CLineInfo aLines[10]; - int CurCharCount = 0; - int LineCount = 0; - - while(CurCharCount < MsgLen) - { - const char* RemainingMsg = pBroadcastMsg + CurCharCount; - - TextRender()->SetCursor(&Cursor, 0, 0, FontSize, TEXTFLAG_STOP_AT_END); - Cursor.m_LineWidth = BcView.w; - - TextRender()->TextEx(&Cursor, RemainingMsg, -1); - int StrLen = Cursor.m_CharCount; - - // don't cut words - if(CurCharCount + StrLen < MsgLen) - { - const int WorldLen = WordLengthBack(RemainingMsg + StrLen, StrLen); - if(WorldLen > 0 && WorldLen < StrLen) - { - StrLen -= WorldLen; - TextRender()->SetCursor(&Cursor, 0, 0, FontSize, TEXTFLAG_STOP_AT_END); - Cursor.m_LineWidth = BcView.w; - TextRender()->TextEx(&Cursor, RemainingMsg, StrLen); - } - } - - const float TextWidth = Cursor.m_X-Cursor.m_StartX; - - CLineInfo Line = { RemainingMsg, StrLen, TextWidth }; - aLines[LineCount++] = Line; - CurCharCount += StrLen; - } - - // draw lines - float y = BcView.y + BcView.h - LineCount * FontSize; - for(int l = 0; l < LineCount; l++) - { - const CLineInfo& Line = aLines[l]; - TextRender()->SetCursor(&Cursor, BcView.x + (BcView.w - Line.m_Width) * 0.5f, y, - FontSize, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); - Cursor.m_LineWidth = BcView.w; - - // draw colored text - int DrawnStrLen = 0; - int ThisCharPos = Line.m_pStrStart - pBroadcastMsg; - while(DrawnStrLen < Line.m_StrLen) - { - int StartColorID = -1; - int ColorStrLen = -1; - - for(int j = 0; j < m_BroadcastColorCount; j++) - { - if((ThisCharPos+DrawnStrLen) >= m_aBroadcastColorList[j].m_CharPos) - { - StartColorID = j; - } - else if(StartColorID >= 0) - { - ColorStrLen = m_aBroadcastColorList[j].m_CharPos - m_aBroadcastColorList[StartColorID].m_CharPos; - break; - } - } - - dbg_assert(StartColorID >= 0, "This should not be -1, color not found"); - - if(ColorStrLen == -1) - ColorStrLen = Line.m_StrLen-DrawnStrLen; - - const CBcColor& TextColor = m_aBroadcastColorList[StartColorID]; - float r = TextColor.m_R/255.f; - float g = TextColor.m_G/255.f; - float b = TextColor.m_B/255.f; - float AvgLum = 0.2126*r + 0.7152*g + 0.0722*b; - - if(AvgLum < 0.25f) - TextRender()->TextOutlineColor(1, 1, 1, 0.6f); - else - TextRender()->TextOutlineColor(0, 0, 0, 0.3f); - - TextRender()->TextColor(r, g, b, Fade); - - TextRender()->TextEx(&Cursor, Line.m_pStrStart+DrawnStrLen, ColorStrLen); - DrawnStrLen += ColorStrLen; - } - - y += FontSize; - } - - TextRender()->TextColor(1, 1, 1, 1); - TextRender()->TextOutlineColor(0, 0, 0, 0.3f); -} diff --git a/src/game/client/components/hud.h b/src/game/client/components/hud.h index 444a0c023..228e2b3f1 100644 --- a/src/game/client/components/hud.h +++ b/src/game/client/components/hud.h @@ -11,25 +11,6 @@ class CHud : public CComponent float m_AverageFPS; int64 m_WarmupHideTick; - // server broadcast - typedef unsigned char u8; - struct CBcColor - { - u8 m_R, m_G, m_B; - int m_CharPos; - }; - - enum { - MAX_BROADCAST_COLORS = 128, - MAX_BROADCAST_MSG_LENGTH = 127 - }; - - CBcColor m_aBroadcastColorList[MAX_BROADCAST_COLORS]; - char m_aBroadcastMsg[MAX_BROADCAST_MSG_LENGTH+1]; - int m_aBroadcastMsgLen; - int m_BroadcastColorCount; - float m_BroadcastReceivedTime; - void RenderCursor(); void RenderFps(); @@ -46,12 +27,11 @@ class CHud : public CComponent void RenderScoreHud(); void RenderSpectatorHud(); void RenderWarmupTimer(); - void RenderBroadcast(); + public: CHud(); virtual void OnReset(); - virtual void OnMessage(int MsgType, void *pRawMsg); virtual void OnRender(); };