From 6ae09f8ec94980392cd20151b79e03c49af1eada Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 22 May 2021 12:08:08 +0200 Subject: [PATCH] - ported Doom's type-on text screens as a screen job overlay. --- source/common/console/c_commandbuffer.cpp | 6 +- source/common/rendering/v_framebuffer.cpp | 1 - .../scripting/interface/stringformat.cpp | 14 ++ .../common/scripting/interface/vmnatives.cpp | 12 ++ wadsrc/static/zscript/engine/base.zs | 2 + wadsrc/static/zscript/engine/screenjob.zs | 201 +++++++++++++++++- 6 files changed, 230 insertions(+), 6 deletions(-) diff --git a/source/common/console/c_commandbuffer.cpp b/source/common/console/c_commandbuffer.cpp index 313df7567..0ca96ade3 100644 --- a/source/common/console/c_commandbuffer.cpp +++ b/source/common/console/c_commandbuffer.cpp @@ -98,8 +98,7 @@ unsigned FCommandBuffer::CalcCellSize(unsigned length) unsigned cellcount = 0; for (unsigned i = 0; i < length; i++) { - int w; - NewConsoleFont->GetChar(Text[i], CR_UNTRANSLATED, &w); + int w = NewConsoleFont->GetCharWidth(Text[i]); cellcount += w / 9; } return cellcount; @@ -112,8 +111,7 @@ unsigned FCommandBuffer::CharsForCells(unsigned cellin, bool *overflow) int cells = cellin; while (cells > 0) { - int w; - NewConsoleFont->GetChar(Text[chars++], CR_UNTRANSLATED, &w); + int w = NewConsoleFont->GetCharWidth(Text[chars++]); cells -= w / 9; } *overflow = (cells < 0); diff --git a/source/common/rendering/v_framebuffer.cpp b/source/common/rendering/v_framebuffer.cpp index 30192e4a3..d1f148f1b 100644 --- a/source/common/rendering/v_framebuffer.cpp +++ b/source/common/rendering/v_framebuffer.cpp @@ -50,7 +50,6 @@ #include "flatvertices.h" #include "version.h" #include "hw_material.h" -#include "v_2ddrawer.h" #include #include diff --git a/source/common/scripting/interface/stringformat.cpp b/source/common/scripting/interface/stringformat.cpp index da667cbbf..87bfbb415 100644 --- a/source/common/scripting/interface/stringformat.cpp +++ b/source/common/scripting/interface/stringformat.cpp @@ -564,6 +564,20 @@ DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, Substitute, StringSubst) return 0; } +static void StringStripRight(FString* self, const FString& junk) +{ + if (junk.IsNotEmpty()) self->StripRight(junk); + else self->StripRight(); +} + +DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, StripRight, StringStripRight) +{ + PARAM_SELF_STRUCT_PROLOGUE(FString); + PARAM_STRING(junk); + StringStripRight(self, junk); + return 0; +} + static void StringSplit(FString* self, TArray* tokens, const FString& delimiter, int keepEmpty) { self->Split(*tokens, delimiter, static_cast(keepEmpty)); diff --git a/source/common/scripting/interface/vmnatives.cpp b/source/common/scripting/interface/vmnatives.cpp index 477863378..25bbdd2e1 100644 --- a/source/common/scripting/interface/vmnatives.cpp +++ b/source/common/scripting/interface/vmnatives.cpp @@ -698,6 +698,18 @@ DEFINE_ACTION_FUNCTION_NATIVE(FFont, GetGlyphHeight, GetGlyphHeight) PARAM_INT(code); ACTION_RETURN_INT(GetGlyphHeight(self, code)); } + +static int GetDefaultKerning(FFont* font) +{ + return font->GetDefaultKerning(); +} + +DEFINE_ACTION_FUNCTION_NATIVE(FFont, GetDefaultKerning, GetDefaultKerning) +{ + PARAM_SELF_STRUCT_PROLOGUE(FFont); + ACTION_RETURN_INT(self->GetDefaultKerning()); +} + //========================================================================== // // file system diff --git a/wadsrc/static/zscript/engine/base.zs b/wadsrc/static/zscript/engine/base.zs index db626e169..53b87e9ed 100644 --- a/wadsrc/static/zscript/engine/base.zs +++ b/wadsrc/static/zscript/engine/base.zs @@ -517,6 +517,7 @@ struct Font native native static Font GetFont(Name fontname); native BrokenLines BreakLines(String text, int maxlen); native int GetGlyphHeight(int code); + native int GetDefaultKerning(); } struct Console native @@ -687,6 +688,7 @@ struct StringStruct native native int CodePointCount() const; native int, int GetNextCodePoint(int position) const; native void Substitute(String str, String replace); + native void StripRight(String junk = ""); } struct Translation version("2.4") diff --git a/wadsrc/static/zscript/engine/screenjob.zs b/wadsrc/static/zscript/engine/screenjob.zs index dac794f32..71c094896 100644 --- a/wadsrc/static/zscript/engine/screenjob.zs +++ b/wadsrc/static/zscript/engine/screenjob.zs @@ -600,7 +600,7 @@ class TextOverlay bool drawclean; BrokenLines screentext; - void Init(String text, int cr = Font.CR_UNTRANSLATED, int pal = 0, bool clean = false) + void Init(String text, int cr = Font.CR_UNDEFINED, int pal = 0, bool clean = false) { screentext = SmallFont.BreakLines(StringTable.Localize(text), 320); nCrawlY = 199; @@ -639,3 +639,202 @@ class TextOverlay } } +//========================================================================== +// +// type-on text, like Doom +// +//========================================================================== + +class TextTypeOnOverlay +{ + String mText; + Font mFont; + int mTextSpeed; + int mTextX, mTextY; + int mTextCounter; + int mTextLen; + int mTextColor; + int mPalette; + int mLineCount; + int mRowPadding; + bool usesDefault; + int mTicker; + int mTextDelay; + + //========================================================================== + // + // + // + //========================================================================== + + void Init(Font fnt, String text, int x = 10, int y = 10, int rowpadding = 2, int speed = 2, int cr = Font.CR_UNDEFINED, int pal = 0) + { + let tt = StringTable.Localize(text); + Array lines; + + tt.Split(lines, "\n"); + mLineCount = lines.Size(); + + mText = ""; + for (int i = 0; i < lines.Size(); i++) + { + tt = lines[i]; + tt.StripRight(); + mText.AppendFormat("\n%s", tt); + } + + mTextSpeed = speed; + if (x < 0) + { + mTextX = mTextY = 10; + usesDefault = true; + } + else + { + mTextX = x; + mTextY = y; + } + + if (!generic_ui) + { + // Todo: Split too long texts + + // If the text is too wide, center it so that it works better on widescreen displays. + // On 4:3 it'd still be cut off, though. + int width = fnt.StringWidth(mText); + if (usesDefault && mTextX + width > 320 - mTextX) + { + mTextX = (320 - width) / 2; + } + } + else + { + // Todo: Split too long texts + + mTextX *= 2; + mTextY *= 2; + int width = NewSmallFont.StringWidth(mText); + if (usesDefault && mTextX + width > 640 - mTextX) + { + mTextX = (640 - width) / 2; + } + } + + + mTextLen = mText.CodePointCount(); + mTextColor = cr; + mPalette = pal; + } + + //========================================================================== + // + // + // + //========================================================================== + + bool Tick() + { + if (mTicker < mTextDelay + (mTextLen * mTextSpeed)) + { + mTicker++; + return false; + } + return true; + } + + bool Reveal() + { + if (mTicker < mTextDelay + (mTextLen * mTextSpeed)) + { + mTicker = mTextDelay + (mTextLen * mTextSpeed); + return true; + } + return false; + } + + //========================================================================== + // + // + // + //========================================================================== + + void Drawer () + { + if (mTicker >= mTextDelay) + { + int w; + int count; + int c; + let width = Screen.GetWidth(); + let height = Screen.GetHeight(); + + // Count number of rows in this text. Since it does not word-wrap, we just count + // line feed characters. + let font = generic_ui ? NewSmallFont : SmallFont; + let fontscale = MAX(generic_ui ? MIN(width / 640, height / 400) : MIN(width / 400, height / 250), 1); + int cleanwidth = width / fontscale; + int cleanheight = height / fontscale; + int refwidth = generic_ui ? 640 : 320; + int refheight = generic_ui ? 400 : 200; + int kerning = font.GetDefaultKerning(); + + + int rowheight = font.GetHeight() * fontscale; + int rowpadding = mRowPadding; + + int cx = (mTextX - refwidth/2) * fontscale + width / 2; + int cy = (mTextY - refheight/2) * fontscale + height / 2; + cx = MAX(0, cx); + int startx = cx; + + if (usesDefault) + { + int allheight; + while ((allheight = mLineCount * (rowheight + rowpadding)) > height && rowpadding > 0) + { + rowpadding--; + } + allheight = mLineCount * (rowheight + rowpadding); + if (height - cy - allheight < cy) + { + cy = (height - allheight) / 2; + if (cy < 0) cy = 0; + } + } + else + { + // Does this text fall off the end of the screen? If so, try to eliminate some margins first. + while (rowpadding > 0 && cy + mLineCount * (rowheight + rowpadding) - rowpadding > height) + { + rowpadding--; + } + // If it's still off the bottom, you are screwed if the origin is fixed. + } + rowheight += rowpadding; + + // draw some of the text onto the screen + int curpos = 0; + for (count = (mTicker - mTextDelay) / mTextSpeed; count > 0 ; count-- ) + { + [c, curpos] = mText.GetNextCodePoint(curpos); + if (!c) + break; + if (c == "\n") + { + cx = startx; + cy += rowheight; + continue; + } + + w = font.GetCharWidth(c); + w += kerning; + w *= fontscale; + if (cx + w > width) + continue; + + Screen.DrawChar(font, mTextColor, cx/fontscale, cy/fontscale, c, DTA_KeepRatio, true, DTA_VirtualWidth, cleanwidth, DTA_VirtualHeight, cleanheight); + cx += w; + } + } + } +} \ No newline at end of file