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