1 /*
2 * OpenClonk, http://www.openclonk.org
3 *
4 * Copyright (c) 1998-2000, Matthes Bender
5 * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
6 * Copyright (c) 2009-2016, The OpenClonk Team and contributors
7 *
8 * Distributed under the terms of the ISC license; see accompanying file
9 * "COPYING" for details.
10 *
11 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12 * See accompanying file "TRADEMARK" for details.
13 *
14 * To redistribute this file separately, substitute the full license texts
15 * for the above references.
16 */
17
18 /* Fullscreen startup log and chat type-in */
19
20 #include "C4Include.h"
21 #include "gui/C4MessageBoard.h"
22
23 #include "game/C4Application.h"
24 #include "game/C4FullScreen.h"
25 #include "game/C4GraphicsSystem.h"
26 #include "graphics/C4Draw.h"
27 #include "graphics/C4GraphicsResource.h"
28 #include "gui/C4Gui.h"
29 #include "gui/C4LoaderScreen.h"
30 #include "gui/C4MessageInput.h"
31 #include "lib/StdColors.h"
32 #include "player/C4Player.h"
33 #include "player/C4PlayerList.h"
34
35 const int C4LogSize=30000, C4LogMaxLines=1000;
36
C4MessageBoard()37 C4MessageBoard::C4MessageBoard() : LogBuffer(C4LogSize, C4LogMaxLines, 0, " ", false)
38 {
39 Delay = -1;
40 Fader = 0;
41 Speed = 2;
42 Output.Default();
43 Startup = false;
44 ScreenFader = 0;
45 iBackScroll = -1;
46 ScrollUpBinding = nullptr;
47 ScrollDownBinding = nullptr;
48 iLineHgt = 1; // Prevent unitialized access with USE_CONSOLE
49 }
50
~C4MessageBoard()51 C4MessageBoard::~C4MessageBoard()
52 {
53 LogBuffer.Clear();
54 LogBuffer.SetLBWidth(0);
55 }
56
Execute()57 void C4MessageBoard::Execute()
58 {
59 // Startup? draw only
60 if (Startup) { Draw(Output); return; }
61
62 // typein or messages waiting? fade in
63 if (::MessageInput.IsTypeIn() || iBackScroll >= 0)
64 ScreenFader = std::max(ScreenFader - 0.20f, -1.0f);
65
66 // no curr msg?
67 if (iBackScroll<0)
68 {
69 // draw anyway
70 Draw(Output);
71 if (!::MessageInput.IsTypeIn())
72 ScreenFader = std::min(ScreenFader + 0.05f, 1.0f);
73 return;
74 }
75
76 // recalc fade/delay speed
77 Speed = std::max(1, iBackScroll / 5);
78 // fade msg in?
79 if (Fader > 0)
80 Fader = std::max(Fader - Speed, 0);
81 // hold curr msg? (delay)
82 if (Fader <= 0)
83 {
84 // no delay set yet?
85 if (Delay == -1)
86 {
87 // set delay based on msg length
88 const char *szCurrMsg = LogBuffer.GetLine(std::min(-iBackScroll, -1), nullptr, nullptr, nullptr);
89 if (szCurrMsg) Delay = strlen(szCurrMsg); else Delay = 0;
90 }
91 // wait...
92 if (Delay > 0) Delay = std::max(Delay - Speed, 0);
93 // end of delay
94 if (Delay == 0)
95 {
96 // set cursor to next msg (or at end of log)
97 iBackScroll = std::max(iBackScroll - 1, -1);
98 // reset fade
99 Fader = iLineHgt;
100 Delay = -1;
101 }
102 }
103
104 // Draw
105 Draw(Output);
106 }
107
Init(C4Facet & cgo,bool fStartup)108 void C4MessageBoard::Init(C4Facet &cgo, bool fStartup)
109 {
110 Output=cgo;
111 Startup=fStartup;
112 iLineHgt=::GraphicsResource.FontRegular.GetLineHeight();
113 LogBuffer.SetLBWidth(Output.Wdt);
114
115 if (!Startup)
116 {
117 // set cursor to end of log
118 iBackScroll = -1;
119 Fader = 0;
120 Speed = 2;
121 ScreenFader = 1.0f; // msgs faded out
122
123 LogBuffer.SetLBWidth(Output.Wdt);
124 }
125
126 // messageboard
127 ScrollUpBinding = std::make_unique<C4KeyBinding>(C4KeyCodeEx(K_UP, KEYS_Shift), "MsgBoardScrollUp", KEYSCOPE_Fullscreen, new C4KeyCB <C4MessageBoard>(*GraphicsSystem.MessageBoard, &C4MessageBoard::ControlScrollUp));
128 ScrollDownBinding = std::make_unique<C4KeyBinding>(C4KeyCodeEx(K_DOWN, KEYS_Shift), "MsgBoardScrollDown", KEYSCOPE_Fullscreen, new C4KeyCB <C4MessageBoard>(*GraphicsSystem.MessageBoard, &C4MessageBoard::ControlScrollDown));
129 }
130
Draw(C4Facet & cgo)131 void C4MessageBoard::Draw(C4Facet &cgo)
132 {
133 if (!Application.Active) return;
134
135 // Startup: draw Loader
136 if (Startup)
137 {
138 if (::GraphicsSystem.pLoaderScreen)
139 ::GraphicsSystem.pLoaderScreen->Draw(cgo, C4LoaderScreen::Flag::ALL, Game.InitProgress, &LogBuffer);
140 else
141 // loader not yet loaded: black BG
142 pDraw->DrawBoxDw(cgo.Surface, 0,0, cgo.Wdt, cgo.Hgt, 0x00000000);
143 return;
144 }
145
146 // Game running: message fader
147
148 // draw messages
149 // how many "extra" messages should be shown?
150 int iMsgFader = C4MSGB_MaxMsgFading;
151 // check screenfader range
152 if (ScreenFader >= 1.0f)
153 {
154 return;
155 }
156 ::GraphicsSystem.OverwriteBg();
157 // show msgs
158 for (int iMsg = -iMsgFader; iMsg < 0; iMsg++)
159 {
160 // get message at pos
161 if (iMsg-iBackScroll >= 0) break;
162 const char *Message = LogBuffer.GetLine(iMsg-iBackScroll, nullptr, nullptr, nullptr);
163 if (!Message || !*Message) continue;
164 // calc target position (y)
165 int iMsgY = cgo.Y + cgo.Hgt + iMsg * iLineHgt + Fader;
166
167 // player message color?
168 C4Player *pPlr = GetMessagePlayer(Message);
169
170 DWORD dwColor;
171 if (pPlr)
172 dwColor = PlrClr2TxtClr(pPlr->ColorDw) & 0xffffff;
173 else
174 dwColor = 0xffffff;
175 // fade out (msg fade)
176 float fade = std::max(ScreenFader, 0.0f) + ((iMsg + 2.0f + float(Fader) / iLineHgt) / std::min(2-iMsgFader, -1));
177 DWORD dwFade = (0xff - Clamp(int(fade * 0xff), 0, 0xff)) << 24;
178 dwColor |= dwFade;
179 // Draw
180 pDraw->StringOut(Message,::GraphicsResource.FontRegular,1.0,cgo.Surface,cgo.X,iMsgY,dwColor);
181 }
182 }
183
EnsureLastMessage()184 void C4MessageBoard::EnsureLastMessage()
185 {
186 // Ingore if startup or typein
187 if (Startup) return;
188 // scroll until end of log
189 for (int i = 0; i < 100; i++)
190 {
191 ::GraphicsSystem.Execute();
192 Execute();
193 if (iBackScroll < 0) break;
194 Delay=0;
195 }
196 }
197
AddLog(const char * szMessage)198 void C4MessageBoard::AddLog(const char *szMessage)
199 {
200 // safety
201 if (!szMessage || !*szMessage) return;
202 // make sure new message will be drawn
203 ++iBackScroll;
204 // register message in standard messageboard font
205 LogBuffer.AppendLines(szMessage, &::GraphicsResource.FontRegular, 0, nullptr);
206 }
207
ClearLog()208 void C4MessageBoard::ClearLog()
209 {
210 LogBuffer.Clear();
211 }
212
LogNotify()213 void C4MessageBoard::LogNotify()
214 {
215 // do not show startup board if GUI is active
216 if (::pGUI->IsActive()) return;
217 // Reset
218 iBackScroll=0;
219 // Draw
220 if (pDraw)
221 {
222 Draw(Output);
223 // startup: Draw message board only and do page flip
224 if (Startup) FullScreen.pSurface->PageFlip();
225 }
226 }
227
GetMessagePlayer(const char * szMessage)228 C4Player* C4MessageBoard::GetMessagePlayer(const char *szMessage)
229 {
230 // Scan message text for heading player name
231 if (SEqual2(szMessage, "* "))
232 {
233 StdStrBuf str;
234 str.CopyUntil(szMessage + 2,' ');
235 return ::Players.GetByName(str.getData());
236 }
237 if (SCharCount(':',szMessage))
238 {
239 StdStrBuf str;
240 str.CopyUntil(szMessage + 2,':');
241 return ::Players.GetByName(str.getData());
242 }
243 return nullptr;
244 }
245
ControlScrollUp()246 bool C4MessageBoard::ControlScrollUp()
247 {
248 Delay=-1; Fader=0;
249 iBackScroll++;
250 return true;
251 }
252
ControlScrollDown()253 bool C4MessageBoard::ControlScrollDown()
254 {
255 Delay=-1; Fader=0;
256 if (iBackScroll > -1) iBackScroll--;
257 return true;
258 }
259