From 8d3585afe1ad1f1e43d4175f8adb7feaa10a01c3 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 25 Oct 2020 12:47:07 +0100 Subject: [PATCH] - console cleanup. The bulk of the console is now free of game dependencies. --- source/CMakeLists.txt | 2 + source/common/console/c_console.h | 5 +- source/common/console/c_notifybufferbase.cpp | 141 +++++++ source/common/console/c_notifybufferbase.h | 39 ++ source/common/engine/printf.h | 2 +- source/core/console/c_console.cpp | 409 +++---------------- source/core/console/c_notifybuffer.cpp | 250 ++++++++++++ source/core/gamecontrol.cpp | 2 + source/core/mainloop.cpp | 4 +- 9 files changed, 490 insertions(+), 364 deletions(-) create mode 100644 source/common/console/c_notifybufferbase.cpp create mode 100644 source/common/console/c_notifybufferbase.h create mode 100644 source/core/console/c_notifybuffer.cpp diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index b50c0ef81..82ba9beb6 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -806,6 +806,7 @@ set (PCH_SOURCES core/gi.cpp core/console/c_console.cpp + core/console/c_notifybuffer.cpp core/console/d_event.cpp common/audio/sound/i_sound.cpp @@ -878,6 +879,7 @@ set (PCH_SOURCES common/console/c_cvars.cpp common/console/c_dispatch.cpp common/console/c_commandbuffer.cpp + common/console/c_notifybufferbase.cpp common/console/c_tabcomplete.cpp common/console/c_expr.cpp common/utility/engineerrors.cpp diff --git a/source/common/console/c_console.h b/source/common/console/c_console.h index b0fb2cf04..b91ba3cee 100644 --- a/source/common/console/c_console.h +++ b/source/common/console/c_console.h @@ -58,7 +58,6 @@ extern constate_e ConsoleState; void C_InitConsole (int width, int height, bool ingame); void C_DeinitConsole (); void C_InitConback(); -void C_ClearMessages(); // Adjust the console for a new screen mode void C_NewModeAdjust (void); @@ -76,9 +75,13 @@ void C_FullConsole (void); void C_HideConsole (void); void C_AdjustBottom (void); void C_FlushDisplay (void); +class FNotifyBufferBase; +void C_SetNotifyBuffer(FNotifyBufferBase *nbb); + bool C_Responder (event_t *ev); +extern double NotifyFontScale; void C_SetNotifyFontScale(double scale); extern const char *console_bar; diff --git a/source/common/console/c_notifybufferbase.cpp b/source/common/console/c_notifybufferbase.cpp new file mode 100644 index 000000000..e31a8f07c --- /dev/null +++ b/source/common/console/c_notifybufferbase.cpp @@ -0,0 +1,141 @@ +/* +** c_notifybufferbase.cpp +** Implements the buffer for the notification message +** +**--------------------------------------------------------------------------- +** Copyright 1998-2006 Randy Heit +** Copyright 2005-2020 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "v_text.h" +#include "i_time.h" +#include "v_draw.h" +#include "c_notifybufferbase.h" + + +void FNotifyBufferBase::Shift(int maxlines) +{ + if (maxlines >= 0 && Text.Size() > (unsigned)maxlines) + { + Text.Delete(0, Text.Size() - maxlines); + } +} + +void FNotifyBufferBase::Clear() +{ + Text.Clear(); +} + + +void FNotifyBufferBase::AddString(int printlevel, FFont *printFont, const FString &source, int formatwidth, float keeptime, int maxlines) +{ + if (printFont == nullptr) return; // Without an initialized font we cannot handle the message (this is for those which come here before the font system is ready.) + LineHeight = printFont->GetHeight(); + TArray lines; + + if (AddType == APPENDLINE && Text.Size() > 0 && Text[Text.Size() - 1].PrintLevel == printlevel) + { + FString str = Text[Text.Size() - 1].Text + source; + lines = V_BreakLines (printFont, formatwidth, str); + } + else + { + lines = V_BreakLines (printFont, formatwidth, source); + if (AddType == APPENDLINE) + { + AddType = NEWLINE; + } + } + + if (lines.Size() == 0) + return; + + for (auto &line : lines) + { + FNotifyText newline; + + newline.Text = line.Text; + newline.TimeOut = int(keeptime * GameTicRate); + newline.Ticker = 0; + newline.PrintLevel = printlevel; + if (AddType == NEWLINE || Text.Size() == 0) + { + if (maxlines > 0) + { + Shift(maxlines - 1); + } + Text.Push(newline); + } + else + { + Text[Text.Size() - 1] = newline; + } + AddType = NEWLINE; + } + + switch (source[source.Len()-1]) + { + case '\r': AddType = REPLACELINE; break; + case '\n': AddType = NEWLINE; break; + default: AddType = APPENDLINE; break; + } + + TopGoal = 0; +} + +void FNotifyBufferBase::Tick() +{ + if (TopGoal > Top) + { + Top++; + } + else if (TopGoal < Top) + { + Top--; + } + + // Remove lines from the beginning that have expired. + unsigned i; + for (i = 0; i < Text.Size(); ++i) + { + Text[i].Ticker++; + } + + for (i = 0; i < Text.Size(); ++i) + { + if (Text[i].TimeOut != 0 && Text[i].TimeOut > Text[i].Ticker) + break; + } + if (i > 0) + { + Text.Delete(0, i); + Top += LineHeight; + } +} + diff --git a/source/common/console/c_notifybufferbase.h b/source/common/console/c_notifybufferbase.h new file mode 100644 index 000000000..0257bb9db --- /dev/null +++ b/source/common/console/c_notifybufferbase.h @@ -0,0 +1,39 @@ +#pragma once +#include "zstring.h" +#include "tarray.h" + +class FFont; + +struct FNotifyText +{ + int TimeOut; + int Ticker; + int PrintLevel; + FString Text; +}; + +class FNotifyBufferBase +{ +public: + virtual ~FNotifyBufferBase() = default; + virtual void AddString(int printlevel, FString source) = 0; + virtual void Shift(int maxlines); + virtual void Clear(); + virtual void Tick(); + virtual void Draw() = 0; + +protected: + TArray Text; + int Top = 0; + int TopGoal = 0; + int LineHeight = 0; + enum { NEWLINE, APPENDLINE, REPLACELINE } AddType = NEWLINE; + + void AddString(int printlevel, FFont *printFont, const FString &source, int formatwidth, float keeptime, int maxlines); + +}; + + + + + diff --git a/source/common/engine/printf.h b/source/common/engine/printf.h index 35efdd5fd..29faefd51 100644 --- a/source/common/engine/printf.h +++ b/source/common/engine/printf.h @@ -58,10 +58,10 @@ enum PRINT_TEAMCHAT, // chat messages from a teammate PRINT_LOG, // only to logfile PRINT_BOLD = 200, // What Printf_Bold used - PRINT_TYPES = 1023, // Bitmask. PRINT_NONOTIFY = 1024, // Flag - do not add to notify buffer PRINT_NOLOG = 2048, // Flag - do not print to log file PRINT_NOTIFY = 4096, // Flag - add to game-native notify display - messages without this only go to the generic notification buffer. + PRINT_TYPES = 1023, // Bitmask. }; enum diff --git a/source/core/console/c_console.cpp b/source/core/console/c_console.cpp index 816b20e44..eff9db322 100644 --- a/source/core/console/c_console.cpp +++ b/source/core/console/c_console.cpp @@ -52,58 +52,30 @@ #include "v_draw.h" #include "v_font.h" #include "printf.h" -#include "inputstate.h" #include "i_time.h" -#include "gamecvars.h" -#include "i_system.h" -#include "s_soundinternal.h" -#include "engineerrors.h" -#include "gamecontrol.h" -#include "v_video.h" -#include "v_draw.h" -#include "g_input.h" -#include "razemenu.h" -#include "raze_music.h" -#include "gstrings.h" -#include "menustate.h" -#include "i_interface.h" -#include "vm.h" -#include "gi.h" #include "texturemanager.h" +#include "v_draw.h" +#include "i_interface.h" +#include "v_video.h" +#include "i_system.h" +#include "menu.h" +#include "menustate.h" +#include "v_2ddrawer.h" +#include "c_notifybufferbase.h" +#include "g_input.h" #include "c_commandbuffer.h" +#include "vm.h" #define LEFTMARGIN 8 #define RIGHTMARGIN 8 #define BOTTOMARGIN 12 -extern bool hud_toggled; -void D_ToggleHud(); - - CUSTOM_CVAR(Int, con_buffersize, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { // ensure a minimum size if (self >= 0 && self < 128) self = 128; } -FConsoleBuffer *conbuffer; - -static FTextureID conback; -static uint32_t conshade; -static bool conline; - -extern bool advancedemo; - -extern FBaseCVar *CVars; -extern FConsoleCommand *Commands[FConsoleCommand::HASH_SIZE]; - -int ConWidth; -bool vidactive = false; -bool cursoron = false; -int ConBottom, ConScroll, RowAdjust; -uint64_t CursorTicker; -constate_e ConsoleState = c_up; - double NotifyFontScale = 1; DEFINE_GLOBAL(NotifyFontScale) @@ -113,6 +85,24 @@ void C_SetNotifyFontScale(double scale) NotifyFontScale = scale; } + +FConsoleBuffer *conbuffer; + +static FTextureID conback; +static uint32_t conshade; +static bool conline; + +extern int chatmodeon; +extern FBaseCVar *CVars; +extern FConsoleCommand *Commands[FConsoleCommand::HASH_SIZE]; + +int ConWidth; +bool vidactive = false; +bool cursoron = false; +int ConBottom, ConScroll, RowAdjust; +uint64_t CursorTicker; +constate_e ConsoleState = c_up; + static int TopLine, InsertLine; static void ClearConsole (); @@ -135,17 +125,6 @@ static GameAtExit *ExitCmdList; static char *work = NULL; static int worklen = 0; -CVAR(Float, con_notifytime, 3.f, CVAR_ARCHIVE) -CUSTOM_CVAR(Float, con_notifyscale, 1, CVAR_ARCHIVE) -{ - if (self < 0.36f) self = 0.36f; - if (self > 1) self = 1; -} - -CVAR(Bool, con_centernotify, false, CVAR_ARCHIVE) -CVAR(Bool, con_notify_advanced, false, CVAR_ARCHIVE) -CVAR(Bool, con_pulsetext, false, CVAR_ARCHIVE) - CUSTOM_CVAR(Int, con_scale, 0, CVAR_ARCHIVE) { if (self < 0) self = 0; @@ -157,13 +136,16 @@ CUSTOM_CVAR(Float, con_alpha, 0.75f, CVAR_ARCHIVE) if (self > 1.f) self = 1.f; } +// Show developer messages if true. +CUSTOM_CVAR(Int, developer, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + FScriptPosition::Developer = self; +} + + // Command to run when Ctrl-D is pressed at start of line CVAR(String, con_ctrl_d, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -CVAR(Int, developer, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) - -EXTERN_CVAR(Int, uiscale); - struct History { @@ -176,42 +158,15 @@ struct History static struct History *HistHead = NULL, *HistTail = NULL, *HistPos = NULL; static int HistSize; -#define NUMNOTIFIES 4 -#define NOTIFYFADETIME 6 +static FNotifyBufferBase *NotifyStrings; -struct FNotifyText +void C_SetNotifyBuffer(FNotifyBufferBase* nbb) { - int TimeOut; - int Ticker; - int PrintLevel; - FString Text; -}; - -struct FNotifyBuffer -{ -public: - FNotifyBuffer(); - void AddString(int printlevel, FString source); - void Shift(int maxlines); - void Clear() { Text.Clear(); } - void Tick(); - void Draw(); - void DrawNative(); - -private: - TArray Text; - int Top; - int TopGoal; - enum { NEWLINE, APPENDLINE, REPLACELINE } AddType; -}; -static FNotifyBuffer NotifyStrings; - -CUSTOM_CVAR(Int, con_notifylines, NUMNOTIFIES, CVAR_GLOBALCONFIG | CVAR_ARCHIVE) -{ - NotifyStrings.Shift(self); + NotifyStrings = nbb; } + int PrintColors[PRINTLEVELS+2] = { CR_UNTRANSLATED, CR_GOLD, CR_GRAY, CR_GREEN, CR_GREEN, CR_UNTRANSLATED }; static void setmsgcolor (int index, int color); @@ -262,7 +217,7 @@ void C_InitConback() if (!conback.isValid()) { - conback = TexMan.GetTextureID (gameinfo.TitlePage, ETextureType::MiscPatch); + conback.SetInvalid(); conshade = MAKEARGB(175,0,0,0); conline = true; } @@ -421,90 +376,6 @@ static void setmsgcolor (int index, int color) PrintColors[index] = color; } -FNotifyBuffer::FNotifyBuffer() -{ - Top = TopGoal = 0; - AddType = NEWLINE; -} - -void FNotifyBuffer::Shift(int maxlines) -{ - if (maxlines >= 0 && Text.Size() > (unsigned)maxlines) - { - Text.Delete(0, Text.Size() - maxlines); - } -} - -void FNotifyBuffer::AddString(int printlevel, FString source) -{ - TArray lines; - int width; - - if (hud_messages == 0 || - screen == nullptr || - source.IsEmpty() || - gamestate == GS_FULLCONSOLE || - gamestate == GS_MENUSCREEN || - con_notifylines == 0) - return; - - auto screenratio = ActiveRatio(screen->GetWidth(), screen->GetHeight()); - - FFont* font = generic_ui ? NewSmallFont : SmallFont ? SmallFont : AlternativeSmallFont; - if (font == nullptr) return; // Without an initialized font we cannot handle the message (this is for those which come here before the font system is ready.) - double fontscale = (generic_ui? 0.7 : NotifyFontScale) * con_notifyscale; - - width = int(320 * (screenratio / 1.333) / fontscale); - - if (AddType == APPENDLINE && Text.Size() > 0 && Text[Text.Size() - 1].PrintLevel == printlevel) - { - FString str = Text[Text.Size() - 1].Text + source; - lines = V_BreakLines (font, width, str); - } - else - { - lines = V_BreakLines (font, width, source); - if (AddType == APPENDLINE) - { - AddType = NEWLINE; - } - } - - if (lines.Size() == 0) - return; - - for (auto &line : lines) - { - FNotifyText newline; - - newline.Text = line.Text; - newline.TimeOut = int(con_notifytime * GameTicRate); - newline.Ticker = 0; - newline.PrintLevel = printlevel; - if (AddType == NEWLINE || Text.Size() == 0) - { - if (con_notifylines > 0) - { - Shift(con_notifylines - 1); - } - Text.Push(newline); - } - else - { - Text[Text.Size() - 1] = newline; - } - AddType = NEWLINE; - } - - switch (source[source.Len()-1]) - { - case '\r': AddType = REPLACELINE; break; - case '\n': AddType = NEWLINE; break; - default: AddType = APPENDLINE; break; - } - - TopGoal = 0; -} void AddToConsole (int printlevel, const char *text) { @@ -573,12 +444,9 @@ int PrintString (int iprintlevel, const char *outline) I_PrintStr(outline); conbuffer->AddText(printlevel, outline); - if (!(iprintlevel & PRINT_NONOTIFY)) + if (vidactive && screen && !(iprintlevel & PRINT_NONOTIFY) && NotifyStrings) { - if (screen && vidactive && ((iprintlevel & PRINT_NOTIFY) || con_notify_advanced)) - { - NotifyStrings.AddString(printlevel, outline); - } + NotifyStrings->AddString(iprintlevel, outline); } } if (Logfile != nullptr && !(iprintlevel & PRINT_NOLOG)) @@ -590,11 +458,6 @@ int PrintString (int iprintlevel, const char *outline) return 0; // Don't waste time on calculating this if nothing at all was printed... } -void C_ClearMessages() -{ - NotifyStrings.Clear(); -} - int VPrintf (int printlevel, const char *format, va_list parms) { FString outline; @@ -646,7 +509,7 @@ int DPrintf (int level, const char *format, ...) void C_FlushDisplay () { - NotifyStrings.Clear(); + if (NotifyStrings) NotifyStrings->Clear(); } void C_AdjustBottom () @@ -701,183 +564,7 @@ void C_Ticker() } lasttic = consoletic; - NotifyStrings.Tick(); -} - -void FNotifyBuffer::Tick() -{ - if (TopGoal > Top) - { - Top++; - } - else if (TopGoal < Top) - { - Top--; - } - - // Remove lines from the beginning that have expired. - unsigned i; - for (i = 0; i < Text.Size(); ++i) - { - Text[i].Ticker++; - } - - for (i = 0; i < Text.Size(); ++i) - { - if (Text[i].TimeOut != 0 && Text[i].TimeOut > Text[i].Ticker) - break; - } - if (i > 0) - { - Text.Delete(0, i); - FFont* font = generic_ui ? NewSmallFont : SmallFont ? SmallFont : AlternativeSmallFont; - Top += font->GetHeight() / NotifyFontScale; - } -} - -void FNotifyBuffer::DrawNative() -{ - // Native display is: - // * centered at the top and pulsing for Duke - // * centered shifted down and not pulsing for Shadow Warrior - // * top left for Exhumed - // * 4 lines with the tiny font for Blood. (same mechanic as the regular one, just a different font and scale.) - - bool center = g_gameType & (GAMEFLAG_DUKE | GAMEFLAG_NAM | GAMEFLAG_WW2GI | GAMEFLAG_RR | GAMEFLAG_SW); - bool pulse = g_gameType & (GAMEFLAG_DUKE | GAMEFLAG_NAM | GAMEFLAG_WW2GI | GAMEFLAG_RR); - unsigned topline = g_gameType & GAMEFLAG_BLOOD ? 0 : Text.Size() - 1; - - FFont* font = g_gameType & GAMEFLAG_BLOOD ? SmallFont2 : SmallFont; - - int line = (g_gameType & GAMEFLAG_BLOOD)? Top : (g_gameType & GAMEFLAG_SW) ? 40 : font->GetDisplacement(); - bool canskip = (g_gameType & GAMEFLAG_BLOOD); - double scale = 1 / (NotifyFontScale * con_notifyscale); - int lineadv = font->GetHeight() / NotifyFontScale; - - for (unsigned i = topline; i < Text.Size(); ++i) - { - FNotifyText& notify = Text[i]; - - if (notify.TimeOut == 0) - continue; - - int j = notify.TimeOut - notify.Ticker; - if (j > 0) - { - double alpha = g_gameType & GAMEFLAG_BLOOD? ((j < NOTIFYFADETIME) ? 1. * j / NOTIFYFADETIME : 1) : 1; - if (pulse) - { - alpha *= 0.7 + 0.3 * sin(I_msTime() / 100.); - } - - if (!center) - { - DrawText(twod, font, CR_UNTRANSLATED, 0, line, notify.Text, - DTA_FullscreenScale, FSMode_ScaleToHeight, - DTA_VirtualWidthF, 320 * scale, DTA_VirtualHeightF, 200 * scale, DTA_KeepRatio, true, - DTA_Alpha, alpha, TAG_DONE); - } - else - { - DrawText(twod, font, CR_UNTRANSLATED, 160 * scale - font->StringWidth(notify.Text) / 2, line, notify.Text, - DTA_FullscreenScale, FSMode_ScaleToHeight, - DTA_VirtualWidthF, 320 * scale, DTA_VirtualHeightF, 200 * scale, - DTA_Alpha, alpha, TAG_DONE); - } - line += lineadv; - canskip = false; - } - else - { - notify.TimeOut = 0; - } - } - if (canskip) - { - Top = TopGoal; - } -} - -static bool printNative() -{ - // Blood originally uses its tiny font for the notify display which does not play along well with localization because it is too small - if (con_notify_advanced) return false; - if (!(g_gameType & GAMEFLAG_BLOOD)) return true; - auto p = GStrings["REQUIRED_CHARACTERS"]; - if (p && *p) return false; - return true; -} - -void FNotifyBuffer::Draw() -{ - if (gamestate == GS_FULLCONSOLE || gamestate == GS_MENUSCREEN) - return; - - if (printNative()) - { - DrawNative(); - return; - } - - bool center = (con_centernotify != 0.f); - int color; - bool canskip = true; - - - FFont* font = generic_ui ? NewSmallFont : SmallFont? SmallFont : AlternativeSmallFont; - double nfscale = (generic_ui? 0.7 : NotifyFontScale); - double scale = 1 / ( * con_notifyscale); - - int line = Top + font->GetDisplacement() / nfscale; - int lineadv = font->GetHeight () / nfscale; - - for (unsigned i = 0; i < Text.Size(); ++ i) - { - FNotifyText ¬ify = Text[i]; - - if (notify.TimeOut == 0) - continue; - - int j = notify.TimeOut - notify.Ticker; - if (j > 0) - { - double alpha = (j < NOTIFYFADETIME) ? 1. * j / NOTIFYFADETIME : 1; - if (con_pulsetext) - { - alpha *= 0.7 + 0.3 * sin(I_msTime() / 100.); - } - - if (notify.PrintLevel >= PRINTLEVELS) - color = CR_UNTRANSLATED; - else - color = PrintColors[notify.PrintLevel]; - - if (!center) - DrawText(twod, font, color, 0, line * NotifyFontScale, notify.Text, - DTA_FullscreenScale, FSMode_ScaleToHeight, - DTA_VirtualWidthF, 320. * scale, - DTA_VirtualHeightF, 200. * scale, - DTA_KeepRatio, true, - DTA_Alpha, alpha, TAG_DONE); - else - DrawText(twod, font, color, 160 * scale - font->StringWidth (notify.Text) / 2., - line, notify.Text, - DTA_FullscreenScale, FSMode_ScaleToHeight, - DTA_VirtualWidthF, 320. * scale, - DTA_VirtualHeightF, 200. * scale, - DTA_Alpha, alpha, TAG_DONE); - line += lineadv; - canskip = false; - } - else - { - notify.TimeOut = 0; - } - } - if (canskip) - { - Top = TopGoal; - } + if (NotifyStrings) NotifyStrings->Tick(); } void C_DrawConsole () @@ -901,9 +588,10 @@ void C_DrawConsole () oldbottom = ConBottom; - if (ConsoleState == c_up && gamestate != GS_INTRO && gamestate != GS_INTERMISSION) + if (ConsoleState == c_up && gamestate != GS_INTRO && gamestate != GS_INTERMISSION && + gamestate != GS_FULLCONSOLE && gamestate != GS_MENUSCREEN) { - NotifyStrings.Draw(); + if (NotifyStrings) NotifyStrings->Draw(); return; } else if (ConBottom) @@ -912,7 +600,7 @@ void C_DrawConsole () visheight = ConBottom; - if (conback.isValid()) + if (conback.isValid() && gamestate != GS_FULLCONSOLE) { DrawTexture (twod, TexMan.GetGameTexture(conback), 0, visheight - screen->GetHeight(), DTA_DestWidth, twod->GetWidth(), @@ -927,6 +615,7 @@ void C_DrawConsole () PalEntry pe((uint8_t)(con_alpha * 255), 0, 0, 0); twod->AddColorOnlyQuad(0, 0, screen->GetWidth(), visheight, pe); } + if (conline && visheight < screen->GetHeight()) { twod->AddColorOnlyQuad(0, visheight, screen->GetWidth(), 1, 0xff000000); diff --git a/source/core/console/c_notifybuffer.cpp b/source/core/console/c_notifybuffer.cpp new file mode 100644 index 000000000..4aea23d37 --- /dev/null +++ b/source/core/console/c_notifybuffer.cpp @@ -0,0 +1,250 @@ +/* +** c_notifybuffer.cpp +** Implements the buffer for the notification message +** +**--------------------------------------------------------------------------- +** Copyright 1998-2006 Randy Heit +** Copyright 2005-2020 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "c_console.h" +#include "vm.h" +#include "gamestate.h" +#include "c_cvars.h" +#include "v_video.h" +#include "i_time.h" +#include "printf.h" +#include "c_console.h" +#include "c_notifybufferbase.h" +#include "v_video.h" +#include "v_font.h" +#include "v_draw.h" +#include "gamecontrol.h" +#include "gstrings.h" + +struct FNotifyBuffer : public FNotifyBufferBase +{ + void DrawNative(); +public: + void AddString(int printlevel, FString source) override; + void Draw() override; + +}; + +static FNotifyBuffer NotifyStrings; +EXTERN_CVAR(Bool, hud_messages) +extern bool generic_ui; +CVAR(Float, con_notifytime, 3.f, CVAR_ARCHIVE) +CVAR(Bool, con_centernotify, false, CVAR_ARCHIVE) +CVAR(Bool, con_pulsetext, false, CVAR_ARCHIVE) +CVAR(Bool, con_notify_advanced, false, CVAR_ARCHIVE) + +enum { NOTIFYFADETIME = 6 }; + +CUSTOM_CVAR(Int, con_notifylines, 4, CVAR_GLOBALCONFIG | CVAR_ARCHIVE) +{ + NotifyStrings.Shift(self); +} + +CUSTOM_CVAR(Float, con_notifyscale, 1, CVAR_ARCHIVE) +{ + if (self < 0.36f) self = 0.36f; + if (self > 1) self = 1; +} + + +void FNotifyBuffer::AddString(int printlevel, FString source) +{ + if (!(printlevel & PRINT_NOTIFY) && !con_notify_advanced) return; + + if (hud_messages == 0 || + screen == nullptr || + source.IsEmpty() || + gamestate == GS_FULLCONSOLE || + gamestate == GS_MENUSCREEN || + con_notifylines == 0) + return; + + auto screenratio = ActiveRatio(screen->GetWidth(), screen->GetHeight()); + + FFont* font = generic_ui ? NewSmallFont : SmallFont ? SmallFont : AlternativeSmallFont; + if (font == nullptr) return; // Without an initialized font we cannot handle the message (this is for those which come here before the font system is ready.) + double fontscale = (generic_ui? 0.7 : NotifyFontScale) * con_notifyscale; + + int width = int(320 * (screenratio / 1.333) / fontscale); + FNotifyBufferBase::AddString(printlevel & PRINT_TYPES, font, source, width, con_notifytime, con_notifylines); +} + + +void FNotifyBuffer::DrawNative() +{ + // Native display is: + // * centered at the top and pulsing for Duke + // * centered shifted down and not pulsing for Shadow Warrior + // * top left for Exhumed + // * 4 lines with the tiny font for Blood. (same mechanic as the regular one, just a different font and scale.) + + bool center = g_gameType & (GAMEFLAG_DUKE | GAMEFLAG_NAM | GAMEFLAG_WW2GI | GAMEFLAG_RR | GAMEFLAG_SW); + bool pulse = g_gameType & (GAMEFLAG_DUKE | GAMEFLAG_NAM | GAMEFLAG_WW2GI | GAMEFLAG_RR); + unsigned topline = g_gameType & GAMEFLAG_BLOOD ? 0 : Text.Size() - 1; + + FFont* font = g_gameType & GAMEFLAG_BLOOD ? SmallFont2 : SmallFont; + + int line = (g_gameType & GAMEFLAG_BLOOD)? Top : (g_gameType & GAMEFLAG_SW) ? 40 : font->GetDisplacement(); + bool canskip = (g_gameType & GAMEFLAG_BLOOD); + double scale = 1 / (NotifyFontScale * con_notifyscale); + int lineadv = font->GetHeight() / NotifyFontScale; + + for (unsigned i = topline; i < Text.Size(); ++i) + { + FNotifyText& notify = Text[i]; + + if (notify.TimeOut == 0) + continue; + + int j = notify.TimeOut - notify.Ticker; + if (j > 0) + { + double alpha = g_gameType & GAMEFLAG_BLOOD? ((j < NOTIFYFADETIME) ? 1. * j / NOTIFYFADETIME : 1) : 1; + if (pulse) + { + alpha *= 0.7 + 0.3 * sin(I_msTime() / 100.); + } + + if (!center) + { + DrawText(twod, font, CR_UNTRANSLATED, 0, line, notify.Text, + DTA_FullscreenScale, FSMode_ScaleToHeight, + DTA_VirtualWidthF, 320 * scale, DTA_VirtualHeightF, 200 * scale, DTA_KeepRatio, true, + DTA_Alpha, alpha, TAG_DONE); + } + else + { + DrawText(twod, font, CR_UNTRANSLATED, 160 * scale - font->StringWidth(notify.Text) / 2, line, notify.Text, + DTA_FullscreenScale, FSMode_ScaleToHeight, + DTA_VirtualWidthF, 320 * scale, DTA_VirtualHeightF, 200 * scale, + DTA_Alpha, alpha, TAG_DONE); + } + line += lineadv; + canskip = false; + } + else + { + notify.TimeOut = 0; + } + } + if (canskip) + { + Top = TopGoal; + } +} + +static bool printNative() +{ + // Blood originally uses its tiny font for the notify display which does not play along well with localization because it is too small, so for non-English switch to the text font. + if (con_notify_advanced) return false; + if (!(g_gameType & GAMEFLAG_BLOOD)) return true; + auto p = GStrings["REQUIRED_CHARACTERS"]; + if (p && *p) return false; + return true; +} + +void FNotifyBuffer::Draw() +{ + if (printNative()) + { + DrawNative(); + return; + } + + bool center = (con_centernotify != 0.f); + int color; + bool canskip = true; + + + FFont* font = generic_ui ? NewSmallFont : SmallFont? SmallFont : AlternativeSmallFont; + double nfscale = (generic_ui? 0.7 : NotifyFontScale); + double scale = 1 / ( * con_notifyscale); + + int line = Top + font->GetDisplacement() / nfscale; + int lineadv = font->GetHeight () / nfscale; + + for (unsigned i = 0; i < Text.Size(); ++ i) + { + FNotifyText ¬ify = Text[i]; + + if (notify.TimeOut == 0) + continue; + + int j = notify.TimeOut - notify.Ticker; + if (j > 0) + { + double alpha = (j < NOTIFYFADETIME) ? 1. * j / NOTIFYFADETIME : 1; + if (con_pulsetext) + { + alpha *= 0.7 + 0.3 * sin(I_msTime() / 100.); + } + + if (notify.PrintLevel >= PRINTLEVELS) + color = CR_UNTRANSLATED; + else + color = PrintColors[notify.PrintLevel]; + + if (!center) + DrawText(twod, font, color, 0, line * NotifyFontScale, notify.Text, + DTA_FullscreenScale, FSMode_ScaleToHeight, + DTA_VirtualWidthF, 320. * scale, + DTA_VirtualHeightF, 200. * scale, + DTA_KeepRatio, true, + DTA_Alpha, alpha, TAG_DONE); + else + DrawText(twod, font, color, 160 * scale - font->StringWidth (notify.Text) / 2., + line, notify.Text, + DTA_FullscreenScale, FSMode_ScaleToHeight, + DTA_VirtualWidthF, 320. * scale, + DTA_VirtualHeightF, 200. * scale, + DTA_Alpha, alpha, TAG_DONE); + line += lineadv; + canskip = false; + } + else + { + notify.TimeOut = 0; + } + } + if (canskip) + { + Top = TopGoal; + } +} + +void SetConsoleNotifyBuffer() +{ + C_SetNotifyBuffer(&NotifyStrings); +} diff --git a/source/core/gamecontrol.cpp b/source/core/gamecontrol.cpp index 1c26794bb..84f71428b 100644 --- a/source/core/gamecontrol.cpp +++ b/source/core/gamecontrol.cpp @@ -129,6 +129,7 @@ void S_ParseSndInfo(); void I_DetectOS(void); void LoadScripts(); void MainLoop(); +void SetConsoleNotifyBuffer(); bool AppActive = true; @@ -495,6 +496,7 @@ int GameMain() { int r; + SetConsoleNotifyBuffer(); sysCallbacks = { System_WantGuiCapture, diff --git a/source/core/mainloop.cpp b/source/core/mainloop.cpp index a5028693b..40d31ad44 100644 --- a/source/core/mainloop.cpp +++ b/source/core/mainloop.cpp @@ -145,7 +145,7 @@ static void GameTicker() switch (ga) { case ga_autoloadgame: - C_ClearMessages(); + C_FlushDisplay(); if (BackupSaveGame.IsNotEmpty() && cl_resumesavegame) { DoLoadGame(BackupSaveGame); @@ -191,7 +191,7 @@ static void GameTicker() newGameStarted = true; FX_SetReverb(0); gi->FreeLevelData(); - C_ClearMessages(); + C_FlushDisplay(); gameaction = ga_level; BackupSaveGame = ""; gi->NewGame(g_nextmap, g_nextskill);