/* * OpenClonk, http://www.openclonk.org * * Copyright (c) 1998-2000, Matthes Bender * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/ * Copyright (c) 2009-2016, The OpenClonk Team and contributors * * Distributed under the terms of the ISC license; see accompanying file * "COPYING" for details. * * "Clonk" is a registered trademark of Matthes Bender, used with permission. * See accompanying file "TRADEMARK" for details. * * To redistribute this file separately, substitute the full license texts * for the above references. */ /* Fullscreen startup log and chat type-in */ #include "C4Include.h" #include "gui/C4MessageBoard.h" #include "game/C4Application.h" #include "game/C4FullScreen.h" #include "game/C4GraphicsSystem.h" #include "graphics/C4Draw.h" #include "graphics/C4GraphicsResource.h" #include "gui/C4Gui.h" #include "gui/C4LoaderScreen.h" #include "gui/C4MessageInput.h" #include "lib/StdColors.h" #include "player/C4Player.h" #include "player/C4PlayerList.h" const int C4LogSize=30000, C4LogMaxLines=1000; C4MessageBoard::C4MessageBoard() : LogBuffer(C4LogSize, C4LogMaxLines, 0, " ", false) { Delay = -1; Fader = 0; Speed = 2; Output.Default(); Startup = false; ScreenFader = 0; iBackScroll = -1; ScrollUpBinding = nullptr; ScrollDownBinding = nullptr; iLineHgt = 1; // Prevent unitialized access with USE_CONSOLE } C4MessageBoard::~C4MessageBoard() { LogBuffer.Clear(); LogBuffer.SetLBWidth(0); } void C4MessageBoard::Execute() { // Startup? draw only if (Startup) { Draw(Output); return; } // typein or messages waiting? fade in if (::MessageInput.IsTypeIn() || iBackScroll >= 0) ScreenFader = std::max(ScreenFader - 0.20f, -1.0f); // no curr msg? if (iBackScroll<0) { // draw anyway Draw(Output); if (!::MessageInput.IsTypeIn()) ScreenFader = std::min(ScreenFader + 0.05f, 1.0f); return; } // recalc fade/delay speed Speed = std::max(1, iBackScroll / 5); // fade msg in? if (Fader > 0) Fader = std::max(Fader - Speed, 0); // hold curr msg? (delay) if (Fader <= 0) { // no delay set yet? if (Delay == -1) { // set delay based on msg length const char *szCurrMsg = LogBuffer.GetLine(std::min(-iBackScroll, -1), nullptr, nullptr, nullptr); if (szCurrMsg) Delay = strlen(szCurrMsg); else Delay = 0; } // wait... if (Delay > 0) Delay = std::max(Delay - Speed, 0); // end of delay if (Delay == 0) { // set cursor to next msg (or at end of log) iBackScroll = std::max(iBackScroll - 1, -1); // reset fade Fader = iLineHgt; Delay = -1; } } // Draw Draw(Output); } void C4MessageBoard::Init(C4Facet &cgo, bool fStartup) { Output=cgo; Startup=fStartup; iLineHgt=::GraphicsResource.FontRegular.GetLineHeight(); LogBuffer.SetLBWidth(Output.Wdt); if (!Startup) { // set cursor to end of log iBackScroll = -1; Fader = 0; Speed = 2; ScreenFader = 1.0f; // msgs faded out LogBuffer.SetLBWidth(Output.Wdt); } // messageboard ScrollUpBinding = std::make_unique(C4KeyCodeEx(K_UP, KEYS_Shift), "MsgBoardScrollUp", KEYSCOPE_Fullscreen, new C4KeyCB (*GraphicsSystem.MessageBoard, &C4MessageBoard::ControlScrollUp)); ScrollDownBinding = std::make_unique(C4KeyCodeEx(K_DOWN, KEYS_Shift), "MsgBoardScrollDown", KEYSCOPE_Fullscreen, new C4KeyCB (*GraphicsSystem.MessageBoard, &C4MessageBoard::ControlScrollDown)); } void C4MessageBoard::Draw(C4Facet &cgo) { if (!Application.Active) return; // Startup: draw Loader if (Startup) { if (::GraphicsSystem.pLoaderScreen) ::GraphicsSystem.pLoaderScreen->Draw(cgo, C4LoaderScreen::Flag::ALL, Game.InitProgress, &LogBuffer); else // loader not yet loaded: black BG pDraw->DrawBoxDw(cgo.Surface, 0,0, cgo.Wdt, cgo.Hgt, 0x00000000); return; } // Game running: message fader // draw messages // how many "extra" messages should be shown? int iMsgFader = C4MSGB_MaxMsgFading; // check screenfader range if (ScreenFader >= 1.0f) { return; } ::GraphicsSystem.OverwriteBg(); // show msgs for (int iMsg = -iMsgFader; iMsg < 0; iMsg++) { // get message at pos if (iMsg-iBackScroll >= 0) break; const char *Message = LogBuffer.GetLine(iMsg-iBackScroll, nullptr, nullptr, nullptr); if (!Message || !*Message) continue; // calc target position (y) int iMsgY = cgo.Y + cgo.Hgt + iMsg * iLineHgt + Fader; // player message color? C4Player *pPlr = GetMessagePlayer(Message); DWORD dwColor; if (pPlr) dwColor = PlrClr2TxtClr(pPlr->ColorDw) & 0xffffff; else dwColor = 0xffffff; // fade out (msg fade) float fade = std::max(ScreenFader, 0.0f) + ((iMsg + 2.0f + float(Fader) / iLineHgt) / std::min(2-iMsgFader, -1)); DWORD dwFade = (0xff - Clamp(int(fade * 0xff), 0, 0xff)) << 24; dwColor |= dwFade; // Draw pDraw->StringOut(Message,::GraphicsResource.FontRegular,1.0,cgo.Surface,cgo.X,iMsgY,dwColor); } } void C4MessageBoard::EnsureLastMessage() { // Ingore if startup or typein if (Startup) return; // scroll until end of log for (int i = 0; i < 100; i++) { ::GraphicsSystem.Execute(); Execute(); if (iBackScroll < 0) break; Delay=0; } } void C4MessageBoard::AddLog(const char *szMessage) { // safety if (!szMessage || !*szMessage) return; // make sure new message will be drawn ++iBackScroll; // register message in standard messageboard font LogBuffer.AppendLines(szMessage, &::GraphicsResource.FontRegular, 0, nullptr); } void C4MessageBoard::ClearLog() { LogBuffer.Clear(); } void C4MessageBoard::LogNotify() { // do not show startup board if GUI is active if (::pGUI->IsActive()) return; // Reset iBackScroll=0; // Draw if (pDraw) { Draw(Output); // startup: Draw message board only and do page flip if (Startup) FullScreen.pSurface->PageFlip(); } } C4Player* C4MessageBoard::GetMessagePlayer(const char *szMessage) { // Scan message text for heading player name if (SEqual2(szMessage, "* ")) { StdStrBuf str; str.CopyUntil(szMessage + 2,' '); return ::Players.GetByName(str.getData()); } if (SCharCount(':',szMessage)) { StdStrBuf str; str.CopyUntil(szMessage + 2,':'); return ::Players.GetByName(str.getData()); } return nullptr; } bool C4MessageBoard::ControlScrollUp() { Delay=-1; Fader=0; iBackScroll++; return true; } bool C4MessageBoard::ControlScrollDown() { Delay=-1; Fader=0; if (iBackScroll > -1) iBackScroll--; return true; }