diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index 9496f6a97..50a4c644e 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -807,6 +807,7 @@ set (PCH_SOURCES
 	core/palette.cpp
 	core/zcompile.cpp
 	core/statusbar.cpp
+	core/statusbar2.cpp
 	core/gi.cpp
 
 	core/console/c_notifybuffer.cpp
diff --git a/source/blood/src/sbar.cpp b/source/blood/src/sbar.cpp
index 721c00b4c..f872634ce 100644
--- a/source/blood/src/sbar.cpp
+++ b/source/blood/src/sbar.cpp
@@ -108,7 +108,7 @@ struct POWERUPDISPLAY
 
 
 
-class DBloodStatusBar : public DBaseStatusBar
+class DBloodStatusBar : public DStatusBarCore
 {
     enum NewRSFlags
     {
@@ -241,7 +241,7 @@ private:
                 stats.spacing = 6;
             }
             if (hud_size <= Hud_StbarOverlay) stats.screenbottomspace = 56;
-            DBaseStatusBar::PrintAutomapInfo(stats, textfont);
+            DStatusBarCore::PrintAutomapInfo(stats, textfont);
 		}
         if (automapMode == am_off && hud_stats)
         {
@@ -253,7 +253,7 @@ private:
             stats.secrets = gSecretMgr.Founds + gSecretMgr.Super;
             stats.maxsecrets = gSecretMgr.Total;
 
-            DBaseStatusBar::PrintLevelStats(stats);
+            DStatusBarCore::PrintLevelStats(stats);
         }
     }
 
diff --git a/source/core/gamecontrol.cpp b/source/core/gamecontrol.cpp
index ef7d7a427..f8885ea29 100644
--- a/source/core/gamecontrol.cpp
+++ b/source/core/gamecontrol.cpp
@@ -1203,146 +1203,16 @@ void GameInterface::FreeLevelData()
 	currentLevel = nullptr;
 }
 
-//---------------------------------------------------------------------------
-//
-// Load crosshair definitions
-//
-//---------------------------------------------------------------------------
-
-FGameTexture* CrosshairImage;
-int CrosshairNum;
-
-CVAR(Int, crosshair, 0, CVAR_ARCHIVE)
-CVAR(Color, crosshaircolor, 0xff0000, CVAR_ARCHIVE);
-CVAR(Int, crosshairhealth, 2, CVAR_ARCHIVE);
-
-void ST_LoadCrosshair(int num, bool alwaysload)
-{
-	char name[16];
-
-	if (!alwaysload && CrosshairNum == num && CrosshairImage != NULL)
-	{ // No change.
-		return;
-	}
-
-	if (num == 0)
-	{
-		CrosshairNum = 0;
-		CrosshairImage = NULL;
-		return;
-	}
-	if (num < 0)
-	{
-		num = -num;
-	}
-
-	mysnprintf(name, countof(name), "XHAIRB%d", num);
-	FTextureID texid = TexMan.CheckForTexture(name, ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ShortNameOnly);
-	if (!texid.isValid())
-	{
-		mysnprintf(name, countof(name), "XHAIRB1");
-		texid = TexMan.CheckForTexture(name, ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ShortNameOnly);
-		if (!texid.isValid())
-		{
-			texid = TexMan.CheckForTexture("XHAIRS1", ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ShortNameOnly);
-		}
-	}
-	CrosshairNum = num;
-	CrosshairImage = TexMan.GetGameTexture(texid);
-}
-
 //---------------------------------------------------------------------------
 //
 // DrawCrosshair
 //
 //---------------------------------------------------------------------------
 
-void DrawGenericCrosshair(int num, int phealth, double xdelta)
-{
-	uint32_t color;
-	double size;
-	int w, h;
-
-	ST_LoadCrosshair(num, false);
-
-	// Don't draw the crosshair if there is none
-	if (CrosshairImage == NULL)
-	{
-		return;
-	}
-
-	float crosshairscale = cl_crosshairscale * 0.005;
-	if (crosshairscale > 0.0f)
-	{
-		size = twod->GetHeight() * crosshairscale / 200.;
-	}
-	else
-	{
-		size = 1.;
-	}
-
-	w = int(CrosshairImage->GetDisplayWidth() * size);
-	h = int(CrosshairImage->GetDisplayHeight() * size);
-
-	if (crosshairhealth == 1) 
-	{
-		// "Standard" crosshair health (green-red)
-		int health = phealth;
-
-		if (health >= 85)
-		{
-			color = 0x00ff00;
-		}
-		else
-		{
-			int red, green;
-			health -= 25;
-			if (health < 0)
-			{
-				health = 0;
-			}
-			if (health < 30)
-			{
-				red = 255;
-				green = health * 255 / 30;
-			}
-			else
-			{
-				red = (60 - health) * 255 / 30;
-				green = 255;
-			}
-			color = (red << 16) | (green << 8);
-		}
-	}
-	else if (crosshairhealth == 2)
-	{
-		// "Enhanced" crosshair health (blue-green-yellow-red)
-		int health = clamp(phealth, 0, 200);
-		float rr, gg, bb;
-
-		float saturation = health < 150 ? 1.f : 1.f - (health - 150) / 100.f;
-
-		HSVtoRGB(&rr, &gg, &bb, health * 1.2f, saturation, 1);
-		int red = int(rr * 255);
-		int green = int(gg * 255);
-		int blue = int(bb * 255);
-
-		color = (red << 16) | (green << 8) | blue;
-	}
-	else
-	{
-		color = crosshaircolor;
-	}
-
-	DrawTexture(twod, CrosshairImage,
-		(windowxy1.x + windowxy2.x) / 2 + xdelta * (windowxy2.y - windowxy1.y) / 240.,
-		(windowxy1.y + windowxy2.y) / 2,
-		DTA_DestWidth, w,
-		DTA_DestHeight, h,
-		DTA_AlphaChannel, true,
-		DTA_FillColor, color & 0xFFFFFF,
-		TAG_DONE);
-}
+void ST_DrawCrosshair(int phealth, double xpos, double ypos, double scale);
+//void DrawGenericCrosshair(int num, int phealth, double xdelta);
+void ST_LoadCrosshair(int num, bool alwaysload);
+CVAR(Int, crosshair, 0, CVAR_ARCHIVE)
 
 
 void DrawCrosshair(int deftile, int health, double xdelta, double ydelta, double scale, PalEntry color)
@@ -1363,7 +1233,12 @@ void DrawCrosshair(int deftile, int health, double xdelta, double ydelta, double
 				return;
 			}
 		}
-		DrawGenericCrosshair(crosshair == 0 ? 2 : *crosshair, health, xdelta);
+		// 0 means 'game provided crosshair' - use type 2 as fallback.
+		ST_LoadCrosshair(crosshair == 0 ? 2 : *crosshair, false);
+
+		double xpos = (windowxy1.x + windowxy2.x) / 2 + xdelta * (windowxy2.y - windowxy1.y) / 240.;
+		double ypos = (windowxy1.y + windowxy2.y) / 2;
+		ST_DrawCrosshair(health, xpos, ypos, 1);
 	}
 }
 //---------------------------------------------------------------------------
diff --git a/source/core/statusbar.cpp b/source/core/statusbar.cpp
index fba769692..b9cd33eaa 100644
--- a/source/core/statusbar.cpp
+++ b/source/core/statusbar.cpp
@@ -77,46 +77,195 @@ EXTERN_CVAR (Bool, am_showtotaltime)
 EXTERN_CVAR (Bool, noisedebug)
 EXTERN_CVAR(Bool, vid_fps)
 EXTERN_CVAR(Bool, inter_subtitles)
-CVAR(Bool, log_vgafont, false, CVAR_ARCHIVE)
 
-DBaseStatusBar *StatusBar;
+DStatusBarCore *StatusBar;
 
 extern int setblocks;
 
 CVAR (Bool, idmypos, false, 0);
 
 
-//---------------------------------------------------------------------------
-// ST_Clear
-//
-//---------------------------------------------------------------------------
+FGameTexture* CrosshairImage;
+static int CrosshairNum;
 
-void ST_Clear()
+CVAR(Color, crosshaircolor, 0xff0000, CVAR_ARCHIVE);
+CVAR(Int, crosshairhealth, 2, CVAR_ARCHIVE);
+CVAR(Float, crosshairscale, 1.0, CVAR_ARCHIVE);
+CVAR(Bool, crosshairgrow, false, CVAR_ARCHIVE);
+EXTERN_CVAR(Bool, vid_fps)
+
+void ST_LoadCrosshair(int num, bool alwaysload)
 {
-	if (StatusBar != NULL)
-	{
-		delete StatusBar;
-		StatusBar = NULL;
+	char name[16];
+	char size;
+
+	if (!alwaysload && CrosshairNum == num && CrosshairImage != NULL)
+	{ // No change.
+		return;
 	}
+
+	if (num == 0)
+	{
+		CrosshairNum = 0;
+		CrosshairImage = NULL;
+		return;
+	}
+	if (num < 0)
+	{
+		num = -num;
+	}
+	size = (twod->GetWidth() < 640) ? 'S' : 'B';
+
+	mysnprintf(name, countof(name), "XHAIR%c%d", size, num);
+	FTextureID texid = TexMan.CheckForTexture(name, ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ShortNameOnly);
+	if (!texid.isValid())
+	{
+		mysnprintf(name, countof(name), "XHAIR%c1", size);
+		texid = TexMan.CheckForTexture(name, ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ShortNameOnly);
+		if (!texid.isValid())
+		{
+			texid = TexMan.CheckForTexture("XHAIRS1", ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ShortNameOnly);
+		}
+	}
+	CrosshairNum = num;
+	CrosshairImage = TexMan.GetGameTexture(texid);
 }
 
-//---------------------------------------------------------------------------
-//
-// Constructor
-//
-//---------------------------------------------------------------------------
-DBaseStatusBar::DBaseStatusBar ()
+void ST_UnloadCrosshair()
 {
-	CompleteBorder = false;
-	Centering = false;
-	FixedOrigin = false;
-	CrosshairSize = 1.;
-	Displacement = 0;
-	ShowLog = false;
-	SetSize(0);
+	CrosshairImage = NULL;
+	CrosshairNum = 0;
 }
 
-static void ValidateResolution(int &hres, int &vres)
+
+//---------------------------------------------------------------------------
+//
+// DrawCrosshair
+//
+//---------------------------------------------------------------------------
+
+void ST_DrawCrosshair(int phealth, double xpos, double ypos, double scale)
+{
+	uint32_t color;
+	double size;
+	int w, h;
+
+	// Don't draw the crosshair if there is none
+	if (CrosshairImage == NULL)
+	{
+		return;
+	}
+
+	if (crosshairscale > 0.0f)
+	{
+		size = twod->GetHeight() * crosshairscale * 0.005;
+	}
+	else
+	{
+		size = 1.;
+	}
+
+	if (crosshairgrow)
+	{
+		size *= scale;
+	}
+
+	w = int(CrosshairImage->GetDisplayWidth() * size);
+	h = int(CrosshairImage->GetDisplayHeight() * size);
+
+	if (crosshairhealth == 1)
+	{
+		// "Standard" crosshair health (green-red)
+		int health = phealth;
+
+		if (health >= 85)
+		{
+			color = 0x00ff00;
+		}
+		else
+		{
+			int red, green;
+			health -= 25;
+			if (health < 0)
+			{
+				health = 0;
+			}
+			if (health < 30)
+			{
+				red = 255;
+				green = health * 255 / 30;
+			}
+			else
+			{
+				red = (60 - health) * 255 / 30;
+				green = 255;
+			}
+			color = (red << 16) | (green << 8);
+		}
+	}
+	else if (crosshairhealth == 2)
+	{
+		// "Enhanced" crosshair health (blue-green-yellow-red)
+		int health = clamp(phealth, 0, 200);
+		float rr, gg, bb;
+
+		float saturation = health < 150 ? 1.f : 1.f - (health - 150) / 100.f;
+
+		HSVtoRGB(&rr, &gg, &bb, health * 1.2f, saturation, 1);
+		int red = int(rr * 255);
+		int green = int(gg * 255);
+		int blue = int(bb * 255);
+
+		color = (red << 16) | (green << 8) | blue;
+	}
+	else
+	{
+		color = crosshaircolor;
+	}
+
+	DrawTexture(twod, CrosshairImage,
+		xpos, ypos,
+		DTA_DestWidth, w,
+		DTA_DestHeight, h,
+		DTA_AlphaChannel, true,
+		DTA_FillColor, color & 0xFFFFFF,
+		TAG_DONE);
+}
+
+
+//---------------------------------------------------------------------------
+//
+//
+//
+//---------------------------------------------------------------------------
+
+enum ENumFlags
+{
+	FNF_WHENNOTZERO = 0x1,
+	FNF_FILLZEROS = 0x2,
+};
+
+void FormatNumber(int number, int minsize, int maxsize, int flags, const FString& prefix, FString* result)
+{
+	static int maxvals[] = { 1, 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999 };
+
+	if (number == 0 && (flags & FNF_WHENNOTZERO))
+	{
+		*result = "";
+		return;
+	}
+	if (maxsize > 0 && maxsize < 10)
+	{
+		number = clamp(number, -maxvals[maxsize - 1], maxvals[maxsize]);
+	}
+	FString& fmt = *result;
+	if (minsize <= 1) fmt.Format("%s%d", prefix.GetChars(), number);
+	else if (flags & FNF_FILLZEROS) fmt.Format("%s%0*d", prefix.GetChars(), minsize, number);
+	else fmt.Format("%s%*d", prefix.GetChars(), minsize, number);
+}
+
+
+void ValidateResolution(int &hres, int &vres)
 {
 	if (hres == 0)
 	{
@@ -131,121 +280,6 @@ static void ValidateResolution(int &hres, int &vres)
 	}
 }
 
-void DBaseStatusBar::SetSize(int reltop, int hres, int vres, int hhres, int hvres)
-{
-	ValidateResolution(hres, vres);
-
-	BaseRelTop = reltop;
-	BaseSBarHorizontalResolution = hres;
-	BaseSBarVerticalResolution = vres;
-	BaseHUDHorizontalResolution = hhres < 0? hres : hhres;
-	BaseHUDVerticalResolution = hvres < 0? vres : hvres;
-	SetDrawSize(reltop, hres, vres);
-}
-
-void DBaseStatusBar::SetDrawSize(int reltop, int hres, int vres)
-{
-	ValidateResolution(hres, vres);
-
-	RelTop = reltop;
-	HorizontalResolution = hres;
-	VerticalResolution = vres;
-	SetScale();	// recalculate positioning info.
-}
-
-//---------------------------------------------------------------------------
-//
-// PROC SetScaled
-//
-//---------------------------------------------------------------------------
-
-void DBaseStatusBar::SetScale ()
-{
-	ValidateResolution(HorizontalResolution, VerticalResolution);
-
-	double w = SCREENWIDTH;
-	double h = SCREENHEIGHT;
-	double refw, refh;
-
-	int horz = HorizontalResolution;
-	int vert = VerticalResolution;
-	double refaspect = horz / double(vert);
-	double screenaspect = w / double(h);
-
-	if ((horz == 320 && vert == 200) || (horz == 640 && vert == 400))
-	{
-		refaspect = 1.333;
-	}
-	
-	if (screenaspect < refaspect)
-	{
-		refw = w;
-		refh = w / refaspect;
-	}
-	else
-	{
-		refh = h;
-		refw = h * refaspect;
-	}
-	refw *= hud_scale;
-	refh *= hud_scale;
-
-	int sby = VerticalResolution - RelTop;
-	// Use full pixels for destination size.
-
-	ST_X = xs_CRoundToInt((w - refw) / 2);
-	ST_Y = xs_CRoundToInt(h - refh);
-	SBarTop = Scale(sby, h, VerticalResolution);
-	SBarScale.X = refw / horz;
-	SBarScale.Y = refh / vert;
-}
-
-//---------------------------------------------------------------------------
-//
-// PROC GetHUDScale
-//
-//---------------------------------------------------------------------------
-
-DVector2 DBaseStatusBar::GetHUDScale() const
-{
-	return SBarScale;
-}
-
-//---------------------------------------------------------------------------
-//
-//  
-//
-//---------------------------------------------------------------------------
-
-void DBaseStatusBar::BeginStatusBar(int resW, int resH, int relTop)
-{
-	SetDrawSize(relTop < 0? BaseRelTop : relTop, resW < 0? BaseSBarHorizontalResolution : resW, resH < 0? BaseSBarVerticalResolution : resH);
-	fullscreenOffsets = false;
-}
-
-//---------------------------------------------------------------------------
-//
-//  
-//
-//---------------------------------------------------------------------------
-
-void DBaseStatusBar::BeginHUD(int resW, int resH, double Alpha)
-{
-	SetDrawSize(RelTop, resW < 0? BaseHUDHorizontalResolution : resW, resH < 0? BaseHUDVerticalResolution : resH);	
-	this->Alpha = Alpha;
-	CompleteBorder = false;
-	fullscreenOffsets = true;
-}
-
-//---------------------------------------------------------------------------
-//
-// PROC Tick
-//
-//---------------------------------------------------------------------------
-
-void DBaseStatusBar::Tick ()
-{
-}
 
 //============================================================================
 //
@@ -253,7 +287,7 @@ void DBaseStatusBar::Tick ()
 //
 //============================================================================
 
-void DBaseStatusBar::StatusbarToRealCoords(double &x, double &y, double &w, double &h) const
+void DStatusBarCore::StatusbarToRealCoords(double &x, double &y, double &w, double &h) const
 {
 	x = ST_X + x * SBarScale.X;
 	y = ST_Y + y * SBarScale.Y;
@@ -267,7 +301,7 @@ void DBaseStatusBar::StatusbarToRealCoords(double &x, double &y, double &w, doub
 //
 //============================================================================
 
-void DBaseStatusBar::DrawGraphic(FTextureID texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, PalEntry color, int translation, double rotate, ERenderStyle style)
+void DStatusBarCore::DrawGraphic(FTextureID texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, PalEntry color, int translation, double rotate, ERenderStyle style)
 {
 	if (!texture.isValid())
 		return;
@@ -276,7 +310,7 @@ void DBaseStatusBar::DrawGraphic(FTextureID texture, double x, double y, int fla
 	DrawGraphic(tex, x, y, flags, Alpha, boxwidth, boxheight, scaleX, scaleY, color, translation, rotate, style);
 }
 
-void DBaseStatusBar::DrawGraphic(FGameTexture* tex, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, PalEntry color, int translation, double rotate, ERenderStyle style)
+void DStatusBarCore::DrawGraphic(FGameTexture* tex, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, PalEntry color, int translation, double rotate, ERenderStyle style)
 {
 	double texwidth = tex->GetDisplayWidth() * scaleX;
 	double texheight = tex->GetDisplayHeight() * scaleY;
@@ -349,8 +383,6 @@ void DBaseStatusBar::DrawGraphic(FGameTexture* tex, double x, double y, int flag
 		case DI_ITEM_VOFFSET: yo = tex->GetDisplayTopOffset(); break;
 		}
 	}
-	//xo *= scaleX;
-	//yo *= scaleY;
 
 	if (!fullscreenOffsets)
 	{
@@ -363,19 +395,19 @@ void DBaseStatusBar::DrawGraphic(FGameTexture* tex, double x, double y, int flag
 		switch (flags & DI_SCREEN_HMASK)
 		{
 		default: orgx = 0; break;
-		case DI_SCREEN_HCENTER: orgx = screen->GetWidth() / 2; break;
-		case DI_SCREEN_RIGHT:   orgx = screen->GetWidth(); break;
+		case DI_SCREEN_HCENTER: orgx = twod->GetWidth() / 2; break;
+		case DI_SCREEN_RIGHT:   orgx = twod->GetWidth(); break;
 		}
 
 		switch (flags & DI_SCREEN_VMASK)
 		{
 		default: orgy = 0; break;
-		case DI_SCREEN_VCENTER: orgy = screen->GetHeight() / 2; break;
-		case DI_SCREEN_BOTTOM: orgy = screen->GetHeight(); break;
+		case DI_SCREEN_VCENTER: orgy = twod->GetHeight() / 2; break;
+		case DI_SCREEN_BOTTOM: orgy = twod->GetHeight(); break;
 		}
 
 		// move stuff in the top right corner a bit down if the fps counter is on.
-		if ((flags & (DI_SCREEN_HMASK|DI_SCREEN_VMASK)) == DI_SCREEN_RIGHT_TOP && vid_fps) y += 10;
+		if ((flags & (DI_SCREEN_HMASK | DI_SCREEN_VMASK)) == DI_SCREEN_RIGHT_TOP && vid_fps) y += 10;
 
 		DVector2 Scale = GetHUDScale();
 
@@ -413,7 +445,7 @@ void DBaseStatusBar::DrawGraphic(FGameTexture* tex, double x, double y, int flag
 //
 //============================================================================
 
-void DBaseStatusBar::DrawString(FFont *font, const FString &cstring, double x, double y, int flags, double Alpha, int translation, int spacing, EMonospacing monospacing, int shadowX, int shadowY, double scaleX, double scaleY)
+void DStatusBarCore::DrawString(FFont *font, const FString &cstring, double x, double y, int flags, double Alpha, int translation, int spacing, EMonospacing monospacing, int shadowX, int shadowY, double scaleX, double scaleY)
 {
 	bool monospaced = monospacing != EMonospacing::Off;
 	double dx = 0;
@@ -452,15 +484,15 @@ void DBaseStatusBar::DrawString(FFont *font, const FString &cstring, double x, d
 		switch (flags & DI_SCREEN_HMASK)
 		{
 		default: orgx = 0; break;
-		case DI_SCREEN_HCENTER: orgx = screen->GetWidth() / 2; break;
-		case DI_SCREEN_RIGHT:   orgx = screen->GetWidth(); break;
+		case DI_SCREEN_HCENTER: orgx = twod->GetWidth() / 2; break;
+		case DI_SCREEN_RIGHT:   orgx = twod->GetWidth(); break;
 		}
 
 		switch (flags & DI_SCREEN_VMASK)
 		{
 		default: orgy = 0; break;
-		case DI_SCREEN_VCENTER: orgy = screen->GetHeight() / 2; break;
-		case DI_SCREEN_BOTTOM: orgy = screen->GetHeight(); break;
+		case DI_SCREEN_VCENTER: orgy = twod->GetHeight() / 2; break;
+		case DI_SCREEN_BOTTOM: orgy = twod->GetHeight(); break;
 		}
 
 		// move stuff in the top right corner a bit down if the fps counter is on.
@@ -531,15 +563,12 @@ void DBaseStatusBar::DrawString(FFont *font, const FString &cstring, double x, d
 		// This may have to be changed to draw the shadow text up front separately.
 		if ((shadowX != 0 || shadowY != 0) && !(flags & DI_NOSHADOW))
 		{
-#if 0
-			// This doesn't work with the limited backend the engine currently uses.
 			DrawChar(twod, font, CR_UNTRANSLATED, rx + shadowX, ry + shadowY, ch,
 				DTA_DestWidthF, rw,
 				DTA_DestHeightF, rh,
-				DTA_Alpha, (Alpha * 0.33),
+				DTA_Alpha, (Alpha * 0.4),
 				DTA_FillColor, 0,
 				TAG_DONE);
-#endif
 		}
 		DrawChar(twod, font, fontcolor, rx, ry, ch,
 			DTA_DestWidthF, rw,
@@ -556,7 +585,7 @@ void DBaseStatusBar::DrawString(FFont *font, const FString &cstring, double x, d
 	}
 }
 
-void SBar_DrawString(DBaseStatusBar *self, DHUDFont *font, const FString &string, double x, double y, int flags, int trans, double alpha, int wrapwidth, int linespacing, double scaleX, double scaleY)
+void SBar_DrawString(DStatusBarCore *self, DHUDFont *font, const FString &string, double x, double y, int flags, int trans, double alpha, int wrapwidth, int linespacing, double scaleX, double scaleY)
 {
 	//if (font == nullptr) ThrowAbortException(X_READ_NIL, nullptr);
 	//if (!screen->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function");
@@ -592,7 +621,7 @@ void SBar_DrawString(DBaseStatusBar *self, DHUDFont *font, const FString &string
 //
 //============================================================================
 
-void DBaseStatusBar::TransformRect(double &x, double &y, double &w, double &h, int flags)
+void DStatusBarCore::TransformRect(double &x, double &y, double &w, double &h, int flags)
 {
 	// resolve auto-alignment before making any adjustments to the position values.
 	if (!(flags & DI_SCREEN_MANUAL_ALIGN))
@@ -617,15 +646,15 @@ void DBaseStatusBar::TransformRect(double &x, double &y, double &w, double &h, i
 		switch (flags & DI_SCREEN_HMASK)
 		{
 		default: orgx = 0; break;
-		case DI_SCREEN_HCENTER: orgx = screen->GetWidth() / 2; break;
-		case DI_SCREEN_RIGHT:   orgx = screen->GetWidth(); break;
+		case DI_SCREEN_HCENTER: orgx = twod->GetWidth() / 2; break;
+		case DI_SCREEN_RIGHT:   orgx = twod->GetWidth(); break;
 		}
 
 		switch (flags & DI_SCREEN_VMASK)
 		{
 		default: orgy = 0; break;
-		case DI_SCREEN_VCENTER: orgy = screen->GetHeight() / 2; break;
-		case DI_SCREEN_BOTTOM: orgy = screen->GetHeight(); break;
+		case DI_SCREEN_VCENTER: orgy = twod->GetHeight() / 2; break;
+		case DI_SCREEN_BOTTOM: orgy = twod->GetHeight(); break;
 		}
 
 		// move stuff in the top right corner a bit down if the fps counter is on.
@@ -643,286 +672,3 @@ void DBaseStatusBar::TransformRect(double &x, double &y, double &w, double &h, i
 }
 
 
-static DObject *InitObject(PClass *type, int paramnum, VM_ARGS)
-{
-	auto obj =  type->CreateNew();
-	// Todo: init
-	return obj;
-}
-
-//============================================================================
-//
-//
-//
-//============================================================================
-
-enum ENumFlags
-{
-	FNF_WHENNOTZERO = 0x1,
-	FNF_FILLZEROS = 0x2,
-};
-
-void FormatNumber(int number, int minsize, int maxsize, int flags, const FString &prefix, FString *result)
-{
-	static int maxvals[] = { 1, 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999 };
-
-	if (number == 0 && (flags & FNF_WHENNOTZERO))
-	{
-		*result = "";
-		return;
-	}
-	if (maxsize > 0 && maxsize < 10)
-	{
-		number = clamp(number, -maxvals[maxsize - 1], maxvals[maxsize]);
-	}
-	FString &fmt = *result;
-	if (minsize <= 1) fmt.Format("%s%d", prefix.GetChars(), number);
-	else if (flags & FNF_FILLZEROS) fmt.Format("%s%0*d", prefix.GetChars(), minsize, number);
-	else fmt.Format("%s%*d", prefix.GetChars(), minsize, number);
-}
-
-//============================================================================
-//
-//
-//
-//============================================================================
-
-void DBaseStatusBar::PrintLevelStats(FLevelStats &stats)
-{
-	double y;
-	double scale = stats.fontscale * hud_statscale;
-	if (stats.spacing <= 0) stats.spacing = stats.font->GetHeight() * stats.fontscale;
-	double spacing = stats.spacing * hud_statscale;
-	if (stats.screenbottomspace < 0)
-	{
-		y = 200 - RelTop - spacing;
-	}
-	else
-	{
-		y = 200 - stats.screenbottomspace - spacing;
-	}
-
-	double y1, y2, y3;
-
-	if (stats.maxsecrets > 0)	// don't bother if there are no secrets.
-	{
-		y1 = y;
-		y -= spacing;
-	}
-	if (stats.frags >= 0 || stats.maxkills != -1)
-	{
-		y2 = y;
-		y -= spacing;
-	}
-	y3 = y;
-
-
-	FString text;
-	int black = 0x80000000;
-
-	text.Format(TEXTCOLOR_ESCAPESTR "%cT: " TEXTCOLOR_ESCAPESTR "%c%d:%02d", stats.letterColor + 'A', stats.standardColor + 'A', stats.time / 60000, (stats.time % 60000) / 1000);
-	DrawText(twod, stats.font, CR_UNTRANSLATED, 2 * hud_statscale + scale, y3 + scale, text, DTA_FullscreenScale, FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200,
-		DTA_KeepRatio, true, DTA_ScaleX, scale, DTA_ScaleY, scale, DTA_LegacyRenderStyle, STYLE_TranslucentStencil, DTA_Color, black, TAG_DONE);
-	DrawText(twod, stats.font, CR_UNTRANSLATED, 2 * hud_statscale, y3, text, DTA_FullscreenScale, FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200,
-		DTA_KeepRatio, true, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE);
-
-	text = "";
-	if (stats.frags > -1) text.Format(TEXTCOLOR_ESCAPESTR "%cF: " TEXTCOLOR_ESCAPESTR "%c%d", stats.letterColor + 'A', stats.standardColor + 'A', stats.frags);
-	else if (stats.maxkills == -2) text.Format(TEXTCOLOR_ESCAPESTR "%cK: " TEXTCOLOR_ESCAPESTR "%c%d", stats.letterColor + 'A', stats.standardColor + 'A', stats.kills);
-	else if (stats.maxkills != -1) text.Format(TEXTCOLOR_ESCAPESTR "%cK: " TEXTCOLOR_ESCAPESTR "%c%d/%d",
-		stats.letterColor + 'A', stats.kills == stats.maxkills ? stats.completeColor + 'A' : stats.standardColor + 'A', stats.kills, stats.maxkills);
-
-	if (text.IsNotEmpty())
-	{
-		DrawText(twod, stats.font, CR_UNTRANSLATED, 2 * hud_statscale+scale, y2+scale, text, DTA_FullscreenScale, FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200,
-			DTA_KeepRatio, true, DTA_ScaleX, scale, DTA_ScaleY, scale, DTA_LegacyRenderStyle, STYLE_TranslucentStencil, DTA_Color, black, TAG_DONE);
-
-		DrawText(twod, stats.font, CR_UNTRANSLATED, 2 * hud_statscale, y2, text, DTA_FullscreenScale, FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200,
-			DTA_KeepRatio, true, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE);
-	}
-
-	if (stats.maxsecrets > 0)	// don't bother if there are no secrets.
-	{
-		text.Format(TEXTCOLOR_ESCAPESTR "%cS: " TEXTCOLOR_ESCAPESTR "%c%d/%d",
-			stats.letterColor + 'A', stats.secrets == stats.maxsecrets ? stats.completeColor + 'A' : stats.standardColor + 'A', stats.secrets, stats.maxsecrets);
-
-		DrawText(twod, stats.font, CR_UNTRANSLATED, 2 * hud_statscale + scale, y1 + scale, text, DTA_FullscreenScale, FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200,
-			DTA_KeepRatio, true, DTA_ScaleX, scale, DTA_ScaleY, scale, DTA_LegacyRenderStyle, STYLE_TranslucentStencil, DTA_Color, black, TAG_DONE);
-
-		DrawText(twod, stats.font, CR_UNTRANSLATED, 2 * hud_statscale, y1, text, DTA_FullscreenScale, FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200,
-			DTA_KeepRatio, true, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE);
-	}
-}
-
-//============================================================================
-//
-//
-//
-//============================================================================
-
-void DBaseStatusBar::PrintAutomapInfo(FLevelStats& stats, bool forcetextfont)
-{
-	auto lev = currentLevel;
-	FString mapname;
-	if (am_showlabel) 
-		mapname.Format(TEXTCOLOR_ESCAPESTR "%c%s: " TEXTCOLOR_ESCAPESTR "%c%s", stats.letterColor+'A', lev->LabelName(), stats.standardColor+'A', lev->DisplayName());
-	else 
-		mapname = lev->DisplayName();
-
-	forcetextfont |= am_textfont;
-	double y;
-	double scale = stats.fontscale * (forcetextfont ? *hud_statscale : 1);	// the tiny default font used by all games here cannot be scaled for readability purposes.
-	if (stats.spacing <= 0) stats.spacing = stats.font->GetHeight() * stats.fontscale;
-	double spacing = stats.spacing * (forcetextfont ? *hud_statscale : 1);
-	if (am_nameontop)
-	{
-		y = spacing + 1;
-	}
-	else if (stats.screenbottomspace < 0)
-	{
-		y = 200 - RelTop - spacing;
-	}
-	else
-	{
-		y = 200 - stats.screenbottomspace - spacing;
-	}
-	const auto &volname = gVolumeNames[volfromlevelnum(lev->levelNumber)];
-	if (volname.IsEmpty() && am_nameontop) y = 1;
-
-	DrawText(twod, stats.font, stats.standardColor, 2 * hud_statscale, y, mapname, DTA_FullscreenScale, FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200,
-		DTA_ScaleX, scale, DTA_ScaleY, scale, DTA_KeepRatio, true, TAG_DONE);
-	y -= spacing;
-	if (!(lev->flags & MI_USERMAP) && !(g_gameType & GAMEFLAG_PSEXHUMED) && volname.IsNotEmpty())
-		DrawText(twod, stats.font, stats.standardColor, 2 * hud_statscale, y, GStrings.localize(volname),
-			DTA_FullscreenScale, FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200,
-			DTA_ScaleX, scale, DTA_ScaleY, scale, DTA_KeepRatio, true, TAG_DONE);
-}
-
-//============================================================================
-//
-// 
-//
-//============================================================================
-
-short DBaseStatusBar::CalcMagazineAmount(short ammo_remaining, short clip_capacity, bool reloading)
-{
-	// Determine amount in clip.
-	short clip_amount = ammo_remaining % clip_capacity;
-
-	// Set current clip value to clip capacity if wrapped around to zero, otherwise use determined value.
-	short clip_current = ammo_remaining != 0 && clip_amount == 0 ? clip_capacity : clip_amount;
-
-	// Return current clip value if weapon has rounds or is not on a reload cycle.
-	return ammo_remaining == 0 || (reloading && clip_amount == 0) ? 0 : clip_current;
-}
-
-//============================================================================
-//
-// 
-//
-//============================================================================
-
-void DBaseStatusBar::Set43ClipRect()
-{
-	auto GetWidth = [=]() { return twod->GetWidth(); };
-	auto GetHeight = [=]() {return twod->GetHeight(); };
-
-	auto screenratio = ActiveRatio(GetWidth(), GetHeight());
-	if (screenratio < 1.34) return;
-
-	int width = xs_CRoundToInt(GetWidth() * 1.333 / screenratio);
-	int left = (GetWidth() - width) / 2;
-	twod->SetClipRect(left, 0, width, GetHeight());
-}
-
-//============================================================================
-//
-// 
-//
-//============================================================================
-
-void setViewport(int viewSize)
-{
-	int x0, y0, x1, y1;
-	if (screen == nullptr) return;
-	int xdim = screen->GetWidth();
-	int ydim = screen->GetHeight();
-	if (xdim == 0 || ydim == 0) return;
-	auto reserved = gi->GetReservedScreenSpace(viewSize);
-	reserved.top = xs_CRoundToInt((reserved.top * hud_scale * ydim) / 200);
-	reserved.statusbar = xs_CRoundToInt((reserved.statusbar * hud_scale * ydim) / 200);
-
-	int xdimcorrect = std::min(Scale(ydim, 4, 3), xdim);
-	if (viewSize > Hud_Stbar)
-	{
-		x0 = 0;
-		x1 = xdim - 1;
-		y0 = reserved.top;
-		y1 = ydim - 1;
-	}
-	else
-	{
-		x0 = 0;
-		y0 = reserved.top;
-		x1 = xdim - 1;
-		y1 = ydim - 1 - reserved.statusbar;
-
-		int height = y1 - y0;
-		int frameheight = (height * (5 - viewSize) / 20);
-		int framewidth = Scale(frameheight, xdim, y1+1);
-		x0 += framewidth;
-		x1 -= framewidth;
-		y0 += frameheight;
-		y1 -= frameheight;
-	}
-	videoSetViewableArea(x0, y0, x1, y1);
-}
-
-//============================================================================
-//
-//
-//
-//============================================================================
-
-int levelTextTime;
-
-void SerializeHud(FSerializer &arc)
-{
-	if (arc.BeginObject("hud"))
-	{
-		arc("texttimer", levelTextTime)
-		.EndObject();
-	}
-}
-
-void setLevelStarted(MapRecord *mi)
-{
-	levelTextTime = 85;
-	Printf(PRINT_NONOTIFY, TEXTCOLOR_GOLD "%s: %s\n", mi->LabelName(), mi->DisplayName());
-}
-
-void drawMapTitle()
-{
-    if (!hud_showmapname || levelTextTime <= 0 || M_Active())
-        return;
-	
-	double alpha = levelTextTime > 16? 1.0 : levelTextTime / 16.;
-    if (alpha > 0)
-    {
-		double scale = (g_gameType & GAMEFLAG_RRALL)? 0.4 : (g_gameType & GAMEFLAG_SW)? 0.7 : 1.0;
-		auto text = currentLevel->DisplayName();
-		double x = 160 - BigFont->StringWidth(text) * scale / 2.;
-		double y = (g_gameType & GAMEFLAG_BLOOD)? 50 : 100 - BigFont->GetHeight()/2.;
-		bool shadow = true;
-
-		if (shadow)
-		{
-			DrawText(twod, BigFont, CR_UNDEFINED, x+1, y+1, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_Color, 0xff000000, DTA_Alpha, alpha / 2., DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE);
-		}
-		DrawText(twod, BigFont, CR_UNDEFINED, x, y, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_Alpha, alpha, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE);
-    }
-}
-
-
-
-
diff --git a/source/core/statusbar.h b/source/core/statusbar.h
index ec9ede161..a37a202ec 100644
--- a/source/core/statusbar.h
+++ b/source/core/statusbar.h
@@ -108,9 +108,9 @@ public:
 };
 
 
-class DBaseStatusBar //: public DObject
+class DStatusBarCore //: public DObject
 {
-	//DECLARE_CLASS (DBaseStatusBar, DObject)
+	//DECLARE_CLASS (DStatusBarCore, DObject)
 	//HAS_OBJECT_POINTERS
 public:
 	// Popup screens for Strife's status bar
@@ -161,8 +161,8 @@ public:
 		CENTER_BOTTOM = BOTTOM | HCENTER
 	};
 
-	DBaseStatusBar ();
-	virtual ~DBaseStatusBar() = default;
+	DStatusBarCore ();
+	virtual ~DStatusBarCore() = default;
 	void SetSize(int reltop = 32, int hres = 320, int vres = 200, int hhres = -1, int hvres = -1);
 
 	void ShowPlayerName ();
@@ -199,6 +199,8 @@ public:
 	short CalcMagazineAmount(short ammo_remaining, short clip_capacity, bool reloading);
 	void Set43ClipRect();
 
+	void SetClipRect(double x, double y, double w, double h, int flags);
+
 //protected:
 	void DrawPowerups ();
 
@@ -245,11 +247,11 @@ private:
 
 };
 
-extern DBaseStatusBar *StatusBar;
+extern DStatusBarCore *StatusBar;
 
 // Status bar factories -----------------------------------------------------
 
-DBaseStatusBar *CreateCustomStatusBar(int script=0);
+DStatusBarCore *CreateCustomStatusBar(int script=0);
 
 // Crosshair stuff ----------------------------------------------------------
 
@@ -343,7 +345,7 @@ enum DI_Flags
 
 };
 
-void SBar_DrawString(DBaseStatusBar* self, DHUDFont* font, const FString& string, double x, double y, int flags, int trans, double alpha, int wrapwidth, int linespacing, double scaleX, double scaleY);
+void SBar_DrawString(DStatusBarCore* self, DHUDFont* font, const FString& string, double x, double y, int flags, int trans, double alpha, int wrapwidth, int linespacing, double scaleX, double scaleY);
 void setViewport(int viewSize);
 struct MapRecord;
 void setLevelStarted(MapRecord *);
diff --git a/source/core/statusbar2.cpp b/source/core/statusbar2.cpp
new file mode 100644
index 000000000..b45623e1f
--- /dev/null
+++ b/source/core/statusbar2.cpp
@@ -0,0 +1,485 @@
+/*
+** shared_sbar.cpp
+** Base status bar implementation
+**
+**---------------------------------------------------------------------------
+** Copyright 1998-2006 Randy Heit
+** Copyright 2017 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 <assert.h>
+
+#include "build.h"
+#include "templates.h"
+#include "statusbar.h"
+#include "c_cvars.h"
+#include "c_dispatch.h"
+#include "c_console.h"
+#include "v_video.h"
+#include "filesystem.h"
+#include "s_soundinternal.h"
+#include "serializer.h"
+#include "serialize_obj.h"
+#include "cmdlib.h"
+#include "vm.h"
+#include "gstrings.h"
+#include "utf8.h"
+#include "texturemanager.h"
+#include "cmdlib.h"
+#include "v_draw.h"
+#include "v_font.h"
+#include "v_draw.h"
+#include "gamecvars.h"
+#include "m_fixed.h"
+#include "gamecontrol.h"
+#include "gamestruct.h"
+#include "razemenu.h"
+#include "mapinfo.h"
+
+#include "../version.h"
+
+#define XHAIRSHRINKSIZE		(1./18)
+#define XHAIRPICKUPSIZE		(2+XHAIRSHRINKSIZE)
+#define POWERUPICONSIZE		32
+
+//IMPLEMENT_CLASS(DHUDFont, true, false);
+
+EXTERN_CVAR (Bool, am_showmonsters)
+EXTERN_CVAR (Bool, am_showsecrets)
+EXTERN_CVAR (Bool, am_showtime)
+EXTERN_CVAR (Bool, am_showtotaltime)
+EXTERN_CVAR (Bool, noisedebug)
+EXTERN_CVAR(Bool, vid_fps)
+EXTERN_CVAR(Bool, inter_subtitles)
+
+extern DStatusBarCore *StatusBar;
+
+extern int setblocks;
+
+
+//---------------------------------------------------------------------------
+// ST_Clear
+//
+//---------------------------------------------------------------------------
+
+void ST_Clear()
+{
+	if (StatusBar != NULL)
+	{
+		delete StatusBar;
+		StatusBar = NULL;
+	}
+}
+
+//---------------------------------------------------------------------------
+//
+// Constructor
+//
+//---------------------------------------------------------------------------
+DStatusBarCore::DStatusBarCore ()
+{
+	CompleteBorder = false;
+	Centering = false;
+	FixedOrigin = false;
+	CrosshairSize = 1.;
+	Displacement = 0;
+	ShowLog = false;
+	SetSize(0);
+}
+
+void ValidateResolution(int& hres, int& vres);
+
+void DStatusBarCore::SetSize(int reltop, int hres, int vres, int hhres, int hvres)
+{
+	ValidateResolution(hres, vres);
+
+	BaseRelTop = reltop;
+	BaseSBarHorizontalResolution = hres;
+	BaseSBarVerticalResolution = vres;
+	BaseHUDHorizontalResolution = hhres < 0? hres : hhres;
+	BaseHUDVerticalResolution = hvres < 0? vres : hvres;
+	SetDrawSize(reltop, hres, vres);
+}
+
+void DStatusBarCore::SetDrawSize(int reltop, int hres, int vres)
+{
+	ValidateResolution(hres, vres);
+
+	RelTop = reltop;
+	HorizontalResolution = hres;
+	VerticalResolution = vres;
+	SetScale();	// recalculate positioning info.
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC SetScaled
+//
+//---------------------------------------------------------------------------
+
+void DStatusBarCore::SetScale ()
+{
+	ValidateResolution(HorizontalResolution, VerticalResolution);
+
+	double w = SCREENWIDTH;
+	double h = SCREENHEIGHT;
+	double refw, refh;
+
+	int horz = HorizontalResolution;
+	int vert = VerticalResolution;
+	double refaspect = horz / double(vert);
+	double screenaspect = w / double(h);
+
+	if ((horz == 320 && vert == 200) || (horz == 640 && vert == 400))
+	{
+		refaspect = 1.333;
+	}
+	
+	if (screenaspect < refaspect)
+	{
+		refw = w;
+		refh = w / refaspect;
+	}
+	else
+	{
+		refh = h;
+		refw = h * refaspect;
+	}
+	refw *= hud_scale;
+	refh *= hud_scale;
+
+	int sby = VerticalResolution - RelTop;
+	// Use full pixels for destination size.
+
+	ST_X = xs_CRoundToInt((w - refw) / 2);
+	ST_Y = xs_CRoundToInt(h - refh);
+	SBarTop = Scale(sby, h, VerticalResolution);
+	SBarScale.X = refw / horz;
+	SBarScale.Y = refh / vert;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC GetHUDScale
+//
+//---------------------------------------------------------------------------
+
+DVector2 DStatusBarCore::GetHUDScale() const
+{
+	return SBarScale;
+}
+
+//---------------------------------------------------------------------------
+//
+//  
+//
+//---------------------------------------------------------------------------
+
+void DStatusBarCore::BeginStatusBar(int resW, int resH, int relTop)
+{
+	SetDrawSize(relTop < 0? BaseRelTop : relTop, resW < 0? BaseSBarHorizontalResolution : resW, resH < 0? BaseSBarVerticalResolution : resH);
+	fullscreenOffsets = false;
+}
+
+//---------------------------------------------------------------------------
+//
+//  
+//
+//---------------------------------------------------------------------------
+
+void DStatusBarCore::BeginHUD(int resW, int resH, double Alpha)
+{
+	SetDrawSize(RelTop, resW < 0? BaseHUDHorizontalResolution : resW, resH < 0? BaseHUDVerticalResolution : resH);	
+	this->Alpha = Alpha;
+	CompleteBorder = false;
+	fullscreenOffsets = true;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC Tick
+//
+//---------------------------------------------------------------------------
+
+void DStatusBarCore::Tick ()
+{
+}
+
+
+static DObject *InitObject(PClass *type, int paramnum, VM_ARGS)
+{
+	auto obj =  type->CreateNew();
+	// Todo: init
+	return obj;
+}
+
+//============================================================================
+//
+//
+//
+//============================================================================
+
+void DStatusBarCore::PrintLevelStats(FLevelStats &stats)
+{
+	double y;
+	double scale = stats.fontscale * hud_statscale;
+	if (stats.spacing <= 0) stats.spacing = stats.font->GetHeight() * stats.fontscale;
+	double spacing = stats.spacing * hud_statscale;
+	if (stats.screenbottomspace < 0)
+	{
+		y = 200 - RelTop - spacing;
+	}
+	else
+	{
+		y = 200 - stats.screenbottomspace - spacing;
+	}
+
+	double y1, y2, y3;
+
+	if (stats.maxsecrets > 0)	// don't bother if there are no secrets.
+	{
+		y1 = y;
+		y -= spacing;
+	}
+	if (stats.frags >= 0 || stats.maxkills != -1)
+	{
+		y2 = y;
+		y -= spacing;
+	}
+	y3 = y;
+
+
+	FString text;
+	int black = 0x80000000;
+
+	text.Format(TEXTCOLOR_ESCAPESTR "%cT: " TEXTCOLOR_ESCAPESTR "%c%d:%02d", stats.letterColor + 'A', stats.standardColor + 'A', stats.time / 60000, (stats.time % 60000) / 1000);
+	DrawText(twod, stats.font, CR_UNTRANSLATED, 2 * hud_statscale + scale, y3 + scale, text, DTA_FullscreenScale, FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200,
+		DTA_KeepRatio, true, DTA_ScaleX, scale, DTA_ScaleY, scale, DTA_LegacyRenderStyle, STYLE_TranslucentStencil, DTA_Color, black, TAG_DONE);
+	DrawText(twod, stats.font, CR_UNTRANSLATED, 2 * hud_statscale, y3, text, DTA_FullscreenScale, FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200,
+		DTA_KeepRatio, true, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE);
+
+	text = "";
+	if (stats.frags > -1) text.Format(TEXTCOLOR_ESCAPESTR "%cF: " TEXTCOLOR_ESCAPESTR "%c%d", stats.letterColor + 'A', stats.standardColor + 'A', stats.frags);
+	else if (stats.maxkills == -2) text.Format(TEXTCOLOR_ESCAPESTR "%cK: " TEXTCOLOR_ESCAPESTR "%c%d", stats.letterColor + 'A', stats.standardColor + 'A', stats.kills);
+	else if (stats.maxkills != -1) text.Format(TEXTCOLOR_ESCAPESTR "%cK: " TEXTCOLOR_ESCAPESTR "%c%d/%d",
+		stats.letterColor + 'A', stats.kills == stats.maxkills ? stats.completeColor + 'A' : stats.standardColor + 'A', stats.kills, stats.maxkills);
+
+	if (text.IsNotEmpty())
+	{
+		DrawText(twod, stats.font, CR_UNTRANSLATED, 2 * hud_statscale+scale, y2+scale, text, DTA_FullscreenScale, FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200,
+			DTA_KeepRatio, true, DTA_ScaleX, scale, DTA_ScaleY, scale, DTA_LegacyRenderStyle, STYLE_TranslucentStencil, DTA_Color, black, TAG_DONE);
+
+		DrawText(twod, stats.font, CR_UNTRANSLATED, 2 * hud_statscale, y2, text, DTA_FullscreenScale, FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200,
+			DTA_KeepRatio, true, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE);
+	}
+
+	if (stats.maxsecrets > 0)	// don't bother if there are no secrets.
+	{
+		text.Format(TEXTCOLOR_ESCAPESTR "%cS: " TEXTCOLOR_ESCAPESTR "%c%d/%d",
+			stats.letterColor + 'A', stats.secrets == stats.maxsecrets ? stats.completeColor + 'A' : stats.standardColor + 'A', stats.secrets, stats.maxsecrets);
+
+		DrawText(twod, stats.font, CR_UNTRANSLATED, 2 * hud_statscale + scale, y1 + scale, text, DTA_FullscreenScale, FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200,
+			DTA_KeepRatio, true, DTA_ScaleX, scale, DTA_ScaleY, scale, DTA_LegacyRenderStyle, STYLE_TranslucentStencil, DTA_Color, black, TAG_DONE);
+
+		DrawText(twod, stats.font, CR_UNTRANSLATED, 2 * hud_statscale, y1, text, DTA_FullscreenScale, FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200,
+			DTA_KeepRatio, true, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE);
+	}
+}
+
+//============================================================================
+//
+//
+//
+//============================================================================
+
+void DStatusBarCore::PrintAutomapInfo(FLevelStats& stats, bool forcetextfont)
+{
+	auto lev = currentLevel;
+	FString mapname;
+	if (am_showlabel) 
+		mapname.Format(TEXTCOLOR_ESCAPESTR "%c%s: " TEXTCOLOR_ESCAPESTR "%c%s", stats.letterColor+'A', lev->LabelName(), stats.standardColor+'A', lev->DisplayName());
+	else 
+		mapname = lev->DisplayName();
+
+	forcetextfont |= am_textfont;
+	double y;
+	double scale = stats.fontscale * (forcetextfont ? *hud_statscale : 1);	// the tiny default font used by all games here cannot be scaled for readability purposes.
+	if (stats.spacing <= 0) stats.spacing = stats.font->GetHeight() * stats.fontscale;
+	double spacing = stats.spacing * (forcetextfont ? *hud_statscale : 1);
+	if (am_nameontop)
+	{
+		y = spacing + 1;
+	}
+	else if (stats.screenbottomspace < 0)
+	{
+		y = 200 - RelTop - spacing;
+	}
+	else
+	{
+		y = 200 - stats.screenbottomspace - spacing;
+	}
+	const auto &volname = gVolumeNames[volfromlevelnum(lev->levelNumber)];
+	if (volname.IsEmpty() && am_nameontop) y = 1;
+
+	DrawText(twod, stats.font, stats.standardColor, 2 * hud_statscale, y, mapname, DTA_FullscreenScale, FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200,
+		DTA_ScaleX, scale, DTA_ScaleY, scale, DTA_KeepRatio, true, TAG_DONE);
+	y -= spacing;
+	if (!(lev->flags & MI_USERMAP) && !(g_gameType & GAMEFLAG_PSEXHUMED) && volname.IsNotEmpty())
+		DrawText(twod, stats.font, stats.standardColor, 2 * hud_statscale, y, GStrings.localize(volname),
+			DTA_FullscreenScale, FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200,
+			DTA_ScaleX, scale, DTA_ScaleY, scale, DTA_KeepRatio, true, TAG_DONE);
+}
+
+//============================================================================
+//
+// 
+//
+//============================================================================
+
+short DStatusBarCore::CalcMagazineAmount(short ammo_remaining, short clip_capacity, bool reloading)
+{
+	// Determine amount in clip.
+	short clip_amount = ammo_remaining % clip_capacity;
+
+	// Set current clip value to clip capacity if wrapped around to zero, otherwise use determined value.
+	short clip_current = ammo_remaining != 0 && clip_amount == 0 ? clip_capacity : clip_amount;
+
+	// Return current clip value if weapon has rounds or is not on a reload cycle.
+	return ammo_remaining == 0 || (reloading && clip_amount == 0) ? 0 : clip_current;
+}
+
+//============================================================================
+//
+// 
+//
+//============================================================================
+
+void DStatusBarCore::Set43ClipRect()
+{
+	auto GetWidth = [=]() { return twod->GetWidth(); };
+	auto GetHeight = [=]() {return twod->GetHeight(); };
+
+	auto screenratio = ActiveRatio(GetWidth(), GetHeight());
+	if (screenratio < 1.34) return;
+
+	int width = xs_CRoundToInt(GetWidth() * 1.333 / screenratio);
+	int left = (GetWidth() - width) / 2;
+	twod->SetClipRect(left, 0, width, GetHeight());
+}
+
+//============================================================================
+//
+// 
+//
+//============================================================================
+
+void setViewport(int viewSize)
+{
+	int x0, y0, x1, y1;
+	if (screen == nullptr) return;
+	int xdim = screen->GetWidth();
+	int ydim = screen->GetHeight();
+	if (xdim == 0 || ydim == 0) return;
+	auto reserved = gi->GetReservedScreenSpace(viewSize);
+	reserved.top = xs_CRoundToInt((reserved.top * hud_scale * ydim) / 200);
+	reserved.statusbar = xs_CRoundToInt((reserved.statusbar * hud_scale * ydim) / 200);
+
+	int xdimcorrect = std::min(Scale(ydim, 4, 3), xdim);
+	if (viewSize > Hud_Stbar)
+	{
+		x0 = 0;
+		x1 = xdim - 1;
+		y0 = reserved.top;
+		y1 = ydim - 1;
+	}
+	else
+	{
+		x0 = 0;
+		y0 = reserved.top;
+		x1 = xdim - 1;
+		y1 = ydim - 1 - reserved.statusbar;
+
+		int height = y1 - y0;
+		int frameheight = (height * (5 - viewSize) / 20);
+		int framewidth = Scale(frameheight, xdim, y1+1);
+		x0 += framewidth;
+		x1 -= framewidth;
+		y0 += frameheight;
+		y1 -= frameheight;
+	}
+	videoSetViewableArea(x0, y0, x1, y1);
+}
+
+//============================================================================
+//
+//
+//
+//============================================================================
+
+int levelTextTime;
+
+void SerializeHud(FSerializer &arc)
+{
+	if (arc.BeginObject("hud"))
+	{
+		arc("texttimer", levelTextTime)
+		.EndObject();
+	}
+}
+
+void setLevelStarted(MapRecord *mi)
+{
+	levelTextTime = 85;
+	Printf(PRINT_NONOTIFY, TEXTCOLOR_GOLD "%s: %s\n", mi->LabelName(), mi->DisplayName());
+}
+
+void drawMapTitle()
+{
+    if (!hud_showmapname || levelTextTime <= 0 || M_Active())
+        return;
+	
+	double alpha = levelTextTime > 16? 1.0 : levelTextTime / 16.;
+    if (alpha > 0)
+    {
+		double scale = (g_gameType & GAMEFLAG_RRALL)? 0.4 : (g_gameType & GAMEFLAG_SW)? 0.7 : 1.0;
+		auto text = currentLevel->DisplayName();
+		double x = 160 - BigFont->StringWidth(text) * scale / 2.;
+		double y = (g_gameType & GAMEFLAG_BLOOD)? 50 : 100 - BigFont->GetHeight()/2.;
+		bool shadow = true;
+
+		if (shadow)
+		{
+			DrawText(twod, BigFont, CR_UNDEFINED, x+1, y+1, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_Color, 0xff000000, DTA_Alpha, alpha / 2., DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE);
+		}
+		DrawText(twod, BigFont, CR_UNDEFINED, x, y, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_Alpha, alpha, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE);
+    }
+}
+
+
+
diff --git a/source/exhumed/src/status.cpp b/source/exhumed/src/status.cpp
index 3e145b754..91fc3c7fd 100644
--- a/source/exhumed/src/status.cpp
+++ b/source/exhumed/src/status.cpp
@@ -512,7 +512,7 @@ void MoveStatus()
 }
 
 
-class DExhumedStatusBar : public DBaseStatusBar
+class DExhumedStatusBar : public DStatusBarCore
 {
     DHUDFont textfont, numberFont;
 
@@ -936,7 +936,7 @@ private:
 
         if (automapMode == am_full)
         {
-            DBaseStatusBar::PrintAutomapInfo(stats, true);
+            DStatusBarCore::PrintAutomapInfo(stats, true);
         }
         else if (hud_stats)
         {
@@ -948,7 +948,7 @@ private:
             stats.secrets = 0;
             stats.maxsecrets = 0;
 
-            DBaseStatusBar::PrintLevelStats(stats);
+            DStatusBarCore::PrintLevelStats(stats);
         }
     }
 
diff --git a/source/games/duke/src/sbar.cpp b/source/games/duke/src/sbar.cpp
index 5a7b1ff28..e512174e2 100644
--- a/source/games/duke/src/sbar.cpp
+++ b/source/games/duke/src/sbar.cpp
@@ -211,7 +211,7 @@ void DDukeCommonStatusBar::PrintLevelStats(int bottomy)
 		else stats.spacing = stats.font->GetHeight() + 1;
 		stats.standardColor = (isNamWW2GI() && am_textfont)? CR_ORANGE : CR_UNTRANSLATED;
 		stats.letterColor = CR_GOLD;
-		DBaseStatusBar::PrintAutomapInfo(stats, textfont);
+		DStatusBarCore::PrintAutomapInfo(stats, textfont);
 	}
 	else if (hud_stats)
 	{
@@ -242,7 +242,7 @@ void DDukeCommonStatusBar::PrintLevelStats(int bottomy)
 			stats.standardColor =
 				stats.completeColor = CR_UNTRANSLATED;
 		}
-		DBaseStatusBar::PrintLevelStats(stats);
+		DStatusBarCore::PrintLevelStats(stats);
 	}
 }
 
diff --git a/source/games/duke/src/sbar.h b/source/games/duke/src/sbar.h
index f1423d76f..c9bffd089 100644
--- a/source/games/duke/src/sbar.h
+++ b/source/games/duke/src/sbar.h
@@ -7,7 +7,7 @@
 BEGIN_DUKE_NS
 
 
-class DDukeCommonStatusBar : public DBaseStatusBar
+class DDukeCommonStatusBar : public DStatusBarCore
 {
 protected:
 	DHUDFont numberFont;
diff --git a/source/sw/src/sbar.cpp b/source/sw/src/sbar.cpp
index 9b3d78524..6e24fe6dc 100644
--- a/source/sw/src/sbar.cpp
+++ b/source/sw/src/sbar.cpp
@@ -67,7 +67,7 @@ static const short icons[] = {
     ID_PanelCaltrops,
 };
 
-class DSWStatusBar : public DBaseStatusBar
+class DSWStatusBar : public DStatusBarCore
 {
     DHUDFont miniFont, numberFont;
 
@@ -967,7 +967,7 @@ private:
                 stats.spacing = 6;
             }
             else stats.spacing = SmallFont->GetHeight() + 1;
-            DBaseStatusBar::PrintAutomapInfo(stats, textfont);
+            DStatusBarCore::PrintAutomapInfo(stats, textfont);
 		}
         // JBF 20040124: display level stats in screen corner
         else if (hud_stats && !(CommEnabled || numplayers > 1))
@@ -984,7 +984,7 @@ private:
             stats.standardColor = CR_TAN;
             stats.completeColor = CR_FIRE;
 
-            DBaseStatusBar::PrintLevelStats(stats);
+            DStatusBarCore::PrintLevelStats(stats);
         }
     }