diff --git a/src/engine/client/text.cpp b/src/engine/client/text.cpp index 615e631fb..a0c819b69 100644 --- a/src/engine/client/text.cpp +++ b/src/engine/client/text.cpp @@ -960,11 +960,11 @@ public: float ScreenX0, ScreenY0, ScreenX1, ScreenY1; Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); - const float FakeToScreenX = (Graphics()->ScreenWidth() / (ScreenX1 - ScreenX0)); - const float FakeToScreenY = (Graphics()->ScreenHeight() / (ScreenY1 - ScreenY0)); + const float FakeToScreenX = Graphics()->ScreenWidth() / (ScreenX1 - ScreenX0); + const float FakeToScreenY = Graphics()->ScreenHeight() / (ScreenY1 - ScreenY0); - const int ActualX = (int)((pCursor->m_X * FakeToScreenX) + 0.5f); - const int ActualY = (int)((pCursor->m_Y * FakeToScreenY) + 0.5f); + const int ActualX = round_to_int(pCursor->m_X * FakeToScreenX); + const int ActualY = round_to_int(pCursor->m_Y * FakeToScreenY); TextContainer.m_AlignedStartX = ActualX / FakeToScreenX; TextContainer.m_AlignedStartY = ActualY / FakeToScreenY; @@ -1024,11 +1024,11 @@ public: float ScreenX0, ScreenY0, ScreenX1, ScreenY1; Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); - const float FakeToScreenX = (Graphics()->ScreenWidth() / (ScreenX1 - ScreenX0)); - const float FakeToScreenY = (Graphics()->ScreenHeight() / (ScreenY1 - ScreenY0)); + const float FakeToScreenX = Graphics()->ScreenWidth() / (ScreenX1 - ScreenX0); + const float FakeToScreenY = Graphics()->ScreenHeight() / (ScreenY1 - ScreenY0); - const int ActualX = (int)((pCursor->m_X * FakeToScreenX) + 0.5f); - const int ActualY = (int)((pCursor->m_Y * FakeToScreenY) + 0.5f); + const int ActualX = round_to_int(pCursor->m_X * FakeToScreenX); + const int ActualY = round_to_int(pCursor->m_Y * FakeToScreenY); const float CursorX = ActualX / FakeToScreenX; const float CursorY = ActualY / FakeToScreenY; @@ -1142,8 +1142,8 @@ public: DrawY += Size; if((RenderFlags & TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT) == 0) { - DrawX = (int)((DrawX * FakeToScreenX) + 0.5f) / FakeToScreenX; // realign - DrawY = (int)((DrawY * FakeToScreenY) + 0.5f) / FakeToScreenY; + DrawX = round_to_int(DrawX * FakeToScreenX) / FakeToScreenX; // realign + DrawY = round_to_int(DrawY * FakeToScreenY) / FakeToScreenY; } LastSelX = DrawX; LastSelWidth = 0; @@ -1677,10 +1677,10 @@ public: if((TextContainer.m_RenderFlags & TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT) == 0) { - const float FakeToScreenX = (Graphics()->ScreenWidth() / (ScreenX1 - ScreenX0)); - const float FakeToScreenY = (Graphics()->ScreenHeight() / (ScreenY1 - ScreenY0)); - const int ActualX = (int)(((TextContainer.m_X + X) * FakeToScreenX) + 0.5f); - const int ActualY = (int)(((TextContainer.m_Y + Y) * FakeToScreenY) + 0.5f); + const float FakeToScreenX = Graphics()->ScreenWidth() / (ScreenX1 - ScreenX0); + const float FakeToScreenY = Graphics()->ScreenHeight() / (ScreenY1 - ScreenY0); + const int ActualX = round_to_int((TextContainer.m_X + X) * FakeToScreenX); + const int ActualY = round_to_int((TextContainer.m_Y + Y) * FakeToScreenY); const float AlignedX = ActualX / FakeToScreenX; const float AlignedY = ActualY / FakeToScreenY; X = AlignedX - TextContainer.m_AlignedStartX; diff --git a/src/engine/textrender.h b/src/engine/textrender.h index 18340e19d..a29f07d46 100644 --- a/src/engine/textrender.h +++ b/src/engine/textrender.h @@ -105,6 +105,11 @@ public: ETextCursorCursorMode m_CursorMode; // note this is the decoded character offset int m_CursorCharacter; + + float Height() const + { + return m_LineCount * m_AlignedFontSize; + } }; class ITextRender : public IInterface diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 176bf7fac..cbcbcf1fe 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -2332,6 +2332,9 @@ void CMenus::OnStateChange(int NewState, int OldState) // reset active item UI()->SetActiveItem(nullptr); + if(OldState == IClient::STATE_ONLINE || OldState == IClient::STATE_OFFLINE) + TextRender()->DeleteTextContainer(m_MotdTextContainerIndex); + if(NewState == IClient::STATE_OFFLINE) { if(OldState >= IClient::STATE_ONLINE && NewState < IClient::STATE_QUITTING) @@ -2370,6 +2373,11 @@ void CMenus::OnStateChange(int NewState, int OldState) } } +void CMenus::OnWindowResize() +{ + TextRender()->DeleteTextContainer(m_MotdTextContainerIndex); +} + void CMenus::OnRender() { UI()->StartCheck(); diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index 3cd05b500..1e28a672a 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -513,11 +513,13 @@ protected: void RenderStartMenu(CUIRect MainView); // found in menus_ingame.cpp + int m_MotdTextContainerIndex = -1; void RenderGame(CUIRect MainView); void PopupConfirmDisconnect(); void PopupConfirmDisconnectDummy(); void RenderPlayers(CUIRect MainView); void RenderServerInfo(CUIRect MainView); + void RenderServerInfoMotd(CUIRect Motd); void RenderServerControl(CUIRect MainView); bool RenderServerControlKick(CUIRect MainView, bool FilterSpectators); bool RenderServerControlServer(CUIRect MainView); @@ -590,6 +592,7 @@ public: void OnConsoleInit() override; virtual void OnStateChange(int NewState, int OldState) override; + virtual void OnWindowResize() override; virtual void OnReset() override; virtual void OnRender() override; virtual bool OnInput(IInput::CEvent Event) override; diff --git a/src/game/client/components/menus_ingame.cpp b/src/game/client/components/menus_ingame.cpp index b69bbdcb2..3af56ab90 100644 --- a/src/game/client/components/menus_ingame.cpp +++ b/src/game/client/components/menus_ingame.cpp @@ -475,7 +475,11 @@ void CMenus::RenderServerInfo(CUIRect MainView) TextRender()->Text(GameInfo.x + x, GameInfo.y + y, 20, aBuf, GameInfo.w - 10.0f); } - // motd + RenderServerInfoMotd(Motd); +} + +void CMenus::RenderServerInfoMotd(CUIRect Motd) +{ const float MotdFontSize = 16.0f; Motd.HSplitTop(10.0f, nullptr, &Motd); Motd.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 10.0f); @@ -487,6 +491,9 @@ void CMenus::RenderServerInfo(CUIRect MainView) Motd.HSplitTop(5.0f, nullptr, &Motd); TextRender()->Text(MotdHeader.x, MotdHeader.y, 2.0f * MotdFontSize, Localize("MOTD"), -1.0f); + if(!m_pClient->m_Motd.ServerMotd()[0]) + return; + static CScrollRegion s_ScrollRegion; vec2 ScrollOffset(0.0f, 0.0f); CScrollRegionParams ScrollParams; @@ -494,10 +501,24 @@ void CMenus::RenderServerInfo(CUIRect MainView) s_ScrollRegion.Begin(&Motd, &ScrollOffset, &ScrollParams); Motd.y += ScrollOffset.y; + static float s_MotdHeight = 0.0f; + static int64_t s_MotdLastUpdateTime = -1; + if(m_MotdTextContainerIndex == -1 || s_MotdLastUpdateTime == -1 || s_MotdLastUpdateTime != m_pClient->m_Motd.ServerMotdUpdateTime()) + { + CTextCursor Cursor; + TextRender()->SetCursor(&Cursor, 0.0f, 0.0f, MotdFontSize, TEXTFLAG_RENDER); + Cursor.m_LineWidth = Motd.w; + TextRender()->RecreateTextContainer(m_MotdTextContainerIndex, &Cursor, m_pClient->m_Motd.ServerMotd()); + s_MotdHeight = Cursor.Height(); + s_MotdLastUpdateTime = m_pClient->m_Motd.ServerMotdUpdateTime(); + } + CUIRect MotdTextArea; - Motd.HSplitTop((str_countchr(m_pClient->m_Motd.m_aServerMotd, '\n') + 1) * MotdFontSize, &MotdTextArea, &Motd); + Motd.HSplitTop(s_MotdHeight, &MotdTextArea, &Motd); s_ScrollRegion.AddRect(MotdTextArea); - TextRender()->Text(MotdTextArea.x, MotdTextArea.y, MotdFontSize, m_pClient->m_Motd.m_aServerMotd, MotdTextArea.w); + + if(m_MotdTextContainerIndex != -1) + TextRender()->RenderTextContainer(m_MotdTextContainerIndex, TextRender()->DefaultTextColor(), TextRender()->DefaultTextOutlineColor(), MotdTextArea.x, MotdTextArea.y); s_ScrollRegion.End(); } diff --git a/src/game/client/components/motd.cpp b/src/game/client/components/motd.cpp index b7fc8b11e..23db5cbb6 100644 --- a/src/game/client/components/motd.cpp +++ b/src/game/client/components/motd.cpp @@ -10,12 +10,21 @@ #include "motd.h" +CMotd::CMotd() +{ + m_aServerMotd[0] = '\0'; + m_ServerMotdTime = 0; + m_ServerMotdUpdateTime = 0; +} + void CMotd::Clear() { m_ServerMotdTime = 0; + Graphics()->DeleteQuadContainer(m_RectQuadContainer); + TextRender()->DeleteTextContainer(m_TextContainerIndex); } -bool CMotd::IsActive() +bool CMotd::IsActive() const { return time() < m_ServerMotdTime; } @@ -26,24 +35,51 @@ void CMotd::OnStateChange(int NewState, int OldState) Clear(); } +void CMotd::OnWindowResize() +{ + Graphics()->DeleteQuadContainer(m_RectQuadContainer); + TextRender()->DeleteTextContainer(m_TextContainerIndex); +} + void CMotd::OnRender() { if(!IsActive()) return; - float Width = 400 * 3.0f * Graphics()->ScreenAspect(); - float Height = 400 * 3.0f; + const float FontSize = 32.0f; // also the size of the margin and rect rounding + const float ScreenHeight = 40.0f * FontSize; // multiple of the font size to get perfect alignment + const float ScreenWidth = ScreenHeight * Graphics()->ScreenAspect(); + Graphics()->MapScreen(0.0f, 0.0f, ScreenWidth, ScreenHeight); - Graphics()->MapScreen(0, 0, Width, Height); + const float RectHeight = 26.0f * FontSize; + const float RectWidth = 630.0f + 2.0f * FontSize; + const float RectX = ScreenWidth / 2.0f - RectWidth / 2.0f; + const float RectY = 160.0f; - float h = 800.0f; - float w = 650.0f; - float x = Width / 2 - w / 2; - float y = 150.0f; + if(m_RectQuadContainer == -1) + { + Graphics()->SetColor(0.0f, 0.0f, 0.0f, 0.5f); + m_RectQuadContainer = Graphics()->CreateRectQuadContainer(RectX, RectY, RectWidth, RectHeight, FontSize, IGraphics::CORNER_ALL); + Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); + } - Graphics()->DrawRect(x, y, w, h, ColorRGBA(0.0f, 0.0f, 0.0f, 0.5f), IGraphics::CORNER_ALL, 40.0f); + if(m_RectQuadContainer != -1) + Graphics()->RenderQuadContainer(m_RectQuadContainer, -1); - TextRender()->Text(x + 40.0f, y + 40.0f, 32.0f, m_aServerMotd, w - 80.0f); + const float TextWidth = RectWidth - 2.0f * FontSize; + const float TextX = RectX + FontSize; + const float TextY = RectY + FontSize; + + if(m_TextContainerIndex == -1) + { + CTextCursor Cursor; + TextRender()->SetCursor(&Cursor, TextX, TextY, FontSize, TEXTFLAG_RENDER); + Cursor.m_LineWidth = TextWidth; + TextRender()->CreateTextContainer(m_TextContainerIndex, &Cursor, ServerMotd()); + } + + if(m_TextContainerIndex != -1) + TextRender()->RenderTextContainer(m_TextContainerIndex, TextRender()->DefaultTextColor(), TextRender()->DefaultTextOutlineColor()); } void CMotd::OnMessage(int MsgType, void *pRawMsg) @@ -53,13 +89,13 @@ void CMotd::OnMessage(int MsgType, void *pRawMsg) if(MsgType == NETMSGTYPE_SV_MOTD) { - CNetMsg_Sv_Motd *pMsg = (CNetMsg_Sv_Motd *)pRawMsg; + const CNetMsg_Sv_Motd *pMsg = static_cast(pRawMsg); // copy it manually to process all \n const char *pMsgStr = pMsg->m_pMessage; - int MotdLen = str_length(pMsgStr) + 1; + const size_t MotdLen = str_length(pMsgStr) + 1; const char *pLast = m_aServerMotd; // for console printing - for(int i = 0, k = 0; i < MotdLen && k < (int)sizeof(m_aServerMotd); i++, k++) + for(size_t i = 0, k = 0; i < MotdLen && k < sizeof(m_aServerMotd); i++, k++) { // handle incoming "\\n" if(pMsgStr[i] == '\\' && pMsgStr[i + 1] == 'n') @@ -83,10 +119,12 @@ void CMotd::OnMessage(int MsgType, void *pRawMsg) if(g_Config.m_ClPrintMotd && *pLast != '\0') m_pClient->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "motd", pLast, color_cast(ColorHSLA(g_Config.m_ClMessageHighlightColor))); + m_ServerMotdUpdateTime = time(); if(m_aServerMotd[0] && g_Config.m_ClMotdTime) - m_ServerMotdTime = time() + time_freq() * g_Config.m_ClMotdTime; + m_ServerMotdTime = m_ServerMotdUpdateTime + time_freq() * g_Config.m_ClMotdTime; else m_ServerMotdTime = 0; + TextRender()->DeleteTextContainer(m_TextContainerIndex); } } diff --git a/src/game/client/components/motd.h b/src/game/client/components/motd.h index 3678eab00..f6f43dd33 100644 --- a/src/game/client/components/motd.h +++ b/src/game/client/components/motd.h @@ -2,22 +2,31 @@ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #ifndef GAME_CLIENT_COMPONENTS_MOTD_H #define GAME_CLIENT_COMPONENTS_MOTD_H + +#include + #include class CMotd : public CComponent { - // motd + char m_aServerMotd[std::size(g_Config.m_SvMotd)]; int64_t m_ServerMotdTime; + int64_t m_ServerMotdUpdateTime; + int m_RectQuadContainer = -1; + int m_TextContainerIndex = -1; public: - char m_aServerMotd[900]; - + CMotd(); virtual int Sizeof() const override { return sizeof(*this); } + + const char *ServerMotd() const { return m_aServerMotd; } + int64_t ServerMotdUpdateTime() const { return m_ServerMotdUpdateTime; } void Clear(); - bool IsActive(); + bool IsActive() const; virtual void OnRender() override; virtual void OnStateChange(int NewState, int OldState) override; + virtual void OnWindowResize() override; virtual void OnMessage(int MsgType, void *pRawMsg) override; virtual bool OnInput(IInput::CEvent Event) override; };