1 /*
2 * Copyright 2014, 2016 Peter Olsson
3 *
4 * This file is part of Brum Brum Rally.
5 *
6 * Brum Brum Rally is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Brum Brum Rally is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "Chat.h"
21 #include "GameState.h"
22 #include "Settings.h"
23 #include "StateManager.h"
24 #include "Font.h"
25 #include <cstddef>
26
Chat()27 Chat::Chat()
28 : needUpdate(false),
29 gameState(0),
30 enabled(true),
31 specialMessageVisible(false)
32 {
33 specialMessage.renderObject.moveTo(2,2);
34 specialMessage.timestamp = 0;
35 }
36
addMessage(const std::string & message,Color color)37 void Chat::addMessage(const std::string& message, Color color)
38 {
39 const Font& font = getFont(Settings::network.chatFont ? BIG_FONT : SMALL_FONT);
40 int messageWidth = font.calculateTextWidth(message);
41 if (messageWidth >= GAME_WIDTH)
42 {
43 std::size_t halfWidth = message.size() / 2;
44 addMessage(message.substr(0, halfWidth) + "...", color);
45 addMessage("..." + message.substr(halfWidth), color);
46 return;
47 }
48
49 messages.push_back(ChatMessage());
50 ChatMessage& cm = messages.back();
51
52 cm.renderObject.setSurface(Surface(message, Settings::network.chatFont ? BIG_FONT : SMALL_FONT, color, TEXT_BORDER_COLOR));
53 if (gameState && enabled)
54 {
55 if (specialMessageVisible)
56 {
57 gameState->removeRenderObject(specialMessage.renderObject);
58 specialMessageVisible = false;
59 }
60 gameState->addRenderObject(cm.renderObject);
61 }
62 // The exact time when the message was written is not important.
63 // What's important is that it's shown long enough on the screen.
64 cm.timestamp = SDL_GetTicks();
65
66 needUpdate = true;
67
68 if (messages.size() > Settings::network.maxChatMessages)
69 {
70 if (gameState)
71 {
72 gameState->removeRenderObject(messages.front().renderObject);
73 }
74 messages.pop_front();
75 }
76 }
77
update()78 void Chat::update()
79 {
80 Uint32 now = SDL_GetTicks();
81 if (Settings::network.chatMessageDuration > 0)
82 {
83 if (SDL_GetAppState() & SDL_APPINPUTFOCUS)
84 {
85 while (!messages.empty() && messages.front().timestamp + Settings::network.chatMessageDuration * 1000 < now)
86 {
87 if (gameState)
88 {
89 gameState->removeRenderObject(messages.front().renderObject);
90 }
91 messages.pop_front();
92 needUpdate = true;
93 }
94 }
95 else
96 {
97 // Don't let the chat messages time out when the window isn't focused.
98 std::deque<ChatMessage>::iterator it;
99 for (it = messages.begin(); it != messages.end(); ++it)
100 {
101 // Reset timeout.
102 it->timestamp = now;
103 }
104 }
105 }
106
107 if (specialMessageVisible && specialMessage.timestamp + 2500 < now)
108 {
109 if (gameState)
110 {
111 gameState->removeRenderObject(specialMessage.renderObject);
112 }
113 specialMessageVisible = false;
114 }
115
116 if (!needUpdate)
117 {
118 return;
119 }
120
121 int x = 2;
122 int y = 2;
123 std::deque<ChatMessage>::iterator it;
124 for (it = messages.begin(); it != messages.end(); ++it)
125 {
126 it->renderObject.moveTo(x, y);
127 y += Settings::network.chatFont ? 10 : 8;
128 }
129
130 needUpdate = false;
131 }
132
setGameState(GameState * newGameState)133 void Chat::setGameState(GameState* newGameState)
134 {
135 std::deque<ChatMessage>::iterator it;
136 for (it = messages.begin(); it != messages.end(); ++it)
137 {
138 if (gameState)
139 {
140 gameState->removeRenderObject(it->renderObject);
141 }
142 if (newGameState && enabled)
143 {
144 newGameState->addRenderObject(it->renderObject);
145 }
146 }
147
148 gameState = newGameState;
149 }
150
toggle()151 void Chat::toggle()
152 {
153 specialMessageVisible = false;
154 if (gameState)
155 {
156 gameState->removeRenderObject(specialMessage.renderObject);
157 }
158
159 std::deque<ChatMessage>::iterator it;
160 if (enabled)
161 {
162 if (gameState)
163 {
164 for (it = messages.begin(); it != messages.end(); ++it)
165 {
166 gameState->removeRenderObject(it->renderObject);
167 }
168 setSpecialMessage("Chat off");
169 }
170 enabled = false;
171 }
172 else
173 {
174 if (gameState)
175 {
176 // Avoid showing messages with less than a second left to timeout.
177 if (Settings::network.chatMessageDuration > 0)
178 {
179 const int MINIMUM_SHOW_TIME = 1000;
180 while (!messages.empty() && messages.front().timestamp + Settings::network.chatMessageDuration * 1000 - MINIMUM_SHOW_TIME < SDL_GetTicks())
181 {
182 messages.front().timestamp -= MINIMUM_SHOW_TIME;
183 }
184 update();
185 }
186
187 if (messages.empty())
188 {
189 setSpecialMessage("Chat on");
190 }
191 else
192 {
193 for (it = messages.begin(); it != messages.end(); ++it)
194 {
195 gameState->addRenderObject(it->renderObject);
196 }
197 }
198 }
199 enabled = true;
200 }
201 }
202
toggle(bool enable)203 void Chat::toggle(bool enable)
204 {
205 if (enable != isEnabled())
206 {
207 toggle();
208 }
209 }
210
isEnabled() const211 bool Chat::isEnabled() const
212 {
213 return enabled;
214 }
215
setSpecialMessage(const std::string & message)216 void Chat::setSpecialMessage(const std::string& message)
217 {
218 specialMessage.renderObject.setSurface(Surface(message, Settings::network.chatFont ? BIG_FONT : SMALL_FONT, CHAT_COLOR, TEXT_BORDER_COLOR));
219 if (gameState)
220 {
221 gameState->addRenderObject(specialMessage.renderObject);
222 }
223 specialMessage.timestamp = SDL_GetTicks();
224 specialMessageVisible = true;
225 }
226
clear()227 void Chat::clear()
228 {
229 setGameState(0);
230 messages.clear();
231 }
232