1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#include "motd.h"
4
5#include <engine/graphics.h>
6#include <engine/keys.h>
7#include <engine/shared/config.h>
8#include <engine/textrender.h>
9
10#include <generated/protocol.h>
11
12#include <game/client/components/important_alert.h>
13#include <game/client/gameclient.h>
14
15CMotd::CMotd()
16{
17 m_aServerMotd[0] = '\0';
18 m_ServerMotdTime = 0;
19 m_ServerMotdUpdateTime = 0;
20}
21
22void CMotd::Clear()
23{
24 m_ServerMotdTime = 0;
25 Graphics()->DeleteQuadContainer(ContainerIndex&: m_RectQuadContainer);
26 TextRender()->DeleteTextContainer(TextContainerIndex&: m_TextContainerIndex);
27}
28
29bool CMotd::IsActive() const
30{
31 return time() < m_ServerMotdTime;
32}
33
34void CMotd::OnStateChange(int NewState, int OldState)
35{
36 if(OldState == IClient::STATE_ONLINE || OldState == IClient::STATE_OFFLINE)
37 Clear();
38}
39
40void CMotd::OnWindowResize()
41{
42 Graphics()->DeleteQuadContainer(ContainerIndex&: m_RectQuadContainer);
43 TextRender()->DeleteTextContainer(TextContainerIndex&: m_TextContainerIndex);
44}
45
46void CMotd::OnRender()
47{
48 if(Client()->State() != IClient::STATE_ONLINE && Client()->State() != IClient::STATE_DEMOPLAYBACK)
49 return;
50
51 if(!IsActive())
52 return;
53
54 if(GameClient()->m_ImportantAlert.IsActive())
55 {
56 Clear();
57 return;
58 }
59
60 const float FontSize = 32.0f; // also the size of the margin and rect rounding
61 const float ScreenHeight = 40.0f * FontSize; // multiple of the font size to get perfect alignment
62 const float ScreenWidth = ScreenHeight * Graphics()->ScreenAspect();
63 Graphics()->MapScreen(TopLeftX: 0.0f, TopLeftY: 0.0f, BottomRightX: ScreenWidth, BottomRightY: ScreenHeight);
64
65 const float RectHeight = 26.0f * FontSize;
66 const float RectWidth = 630.0f + 2.0f * FontSize;
67 const float RectX = ScreenWidth / 2.0f - RectWidth / 2.0f;
68 const float RectY = 160.0f;
69
70 if(m_RectQuadContainer == -1)
71 {
72 Graphics()->SetColor(r: 0.0f, g: 0.0f, b: 0.0f, a: 0.5f);
73 m_RectQuadContainer = Graphics()->CreateRectQuadContainer(x: RectX, y: RectY, w: RectWidth, h: RectHeight, r: FontSize, Corners: IGraphics::CORNER_ALL);
74 Graphics()->SetColor(r: 1.0f, g: 1.0f, b: 1.0f, a: 1.0f);
75 }
76
77 if(m_RectQuadContainer != -1)
78 {
79 Graphics()->TextureClear();
80 Graphics()->RenderQuadContainer(ContainerIndex: m_RectQuadContainer, QuadDrawNum: -1);
81 }
82
83 if(!m_TextContainerIndex.Valid())
84 {
85 CTextCursor Cursor;
86 Cursor.SetPosition(vec2(RectX + FontSize, RectY + FontSize));
87 Cursor.m_FontSize = FontSize;
88 Cursor.m_LineWidth = RectWidth - 2.0f * FontSize;
89 TextRender()->CreateTextContainer(TextContainerIndex&: m_TextContainerIndex, pCursor: &Cursor, pText: ServerMotd());
90 }
91
92 if(m_TextContainerIndex.Valid())
93 TextRender()->RenderTextContainer(TextContainerIndex: m_TextContainerIndex, TextColor: TextRender()->DefaultTextColor(), TextOutlineColor: TextRender()->DefaultTextOutlineColor());
94}
95
96void CMotd::OnMessage(int MsgType, void *pRawMsg)
97{
98 if(Client()->State() == IClient::STATE_DEMOPLAYBACK)
99 return;
100
101 if(MsgType == NETMSGTYPE_SV_MOTD)
102 {
103 const CNetMsg_Sv_Motd *pMsg = static_cast<CNetMsg_Sv_Motd *>(pRawMsg);
104
105 // copy it manually to process all \n
106 const char *pMsgStr = pMsg->m_pMessage;
107 const size_t MotdLen = str_length(str: pMsgStr) + 1;
108 const char *pLast = m_aServerMotd; // for console printing
109 for(size_t i = 0, k = 0; i < MotdLen && k < sizeof(m_aServerMotd); i++, k++)
110 {
111 // handle incoming "\\n"
112 if(pMsgStr[i] == '\\' && pMsgStr[i + 1] == 'n')
113 {
114 m_aServerMotd[k] = '\n';
115 i++; // skip the 'n'
116 }
117 else
118 m_aServerMotd[k] = pMsgStr[i];
119
120 // print the line to the console when receiving the newline character
121 if(g_Config.m_ClPrintMotd && m_aServerMotd[k] == '\n')
122 {
123 m_aServerMotd[k] = '\0';
124 GameClient()->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "motd", pStr: pLast, PrintColor: color_cast<ColorRGBA>(hsl: ColorHSLA(g_Config.m_ClMessageHighlightColor)));
125 m_aServerMotd[k] = '\n';
126 pLast = m_aServerMotd + k + 1;
127 }
128 }
129 m_aServerMotd[sizeof(m_aServerMotd) - 1] = '\0';
130 if(g_Config.m_ClPrintMotd && *pLast != '\0')
131 GameClient()->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "motd", pStr: pLast, PrintColor: color_cast<ColorRGBA>(hsl: ColorHSLA(g_Config.m_ClMessageHighlightColor)));
132
133 m_ServerMotdUpdateTime = time();
134 if(m_aServerMotd[0] && g_Config.m_ClMotdTime)
135 m_ServerMotdTime = m_ServerMotdUpdateTime + time_freq() * g_Config.m_ClMotdTime;
136 else
137 m_ServerMotdTime = 0;
138 TextRender()->DeleteTextContainer(TextContainerIndex&: m_TextContainerIndex);
139 }
140}
141
142bool CMotd::OnInput(const IInput::CEvent &Event)
143{
144 if(IsActive() && Event.m_Flags & IInput::FLAG_PRESS && Event.m_Key == KEY_ESCAPE)
145 {
146 Clear();
147 return true;
148 }
149 return false;
150}
151