diff --git a/src/g_skill.cpp b/src/g_skill.cpp
index a2c75376c..04ac2f290 100644
--- a/src/g_skill.cpp
+++ b/src/g_skill.cpp
@@ -488,6 +488,11 @@ const char * G_SkillName()
 	return name;
 }
 
+DEFINE_ACTION_FUNCTION(DObject, G_SkillName)
+{
+	PARAM_PROLOGUE;
+	ACTION_RETURN_STRING(G_SkillName());
+}
 
 //==========================================================================
 //
diff --git a/src/g_statusbar/sbar.h b/src/g_statusbar/sbar.h
index 5e12a695b..fe83d616d 100644
--- a/src/g_statusbar/sbar.h
+++ b/src/g_statusbar/sbar.h
@@ -394,7 +394,7 @@ public:
 	};
 
 	void DBaseStatusBar::DrawGraphic(FTextureID texture, bool animate, double x, double y, double Alpha = 1., bool translatable = false, bool dim = false,
-		int imgAlign = TOP | LEFT, int screenalign = TOP | LEFT, bool alphamap = false, double width = -1, double height = -1, double scaleX = 1, double scaleY = 1, bool fullscreenoffsets = false);
+		int imgAlign = TOP | LEFT, int screenalign = TOP | LEFT, bool alphamap = false, double width = -1, double height = -1);
 
 	void GetCoords(int &x, int &y)
 	{
@@ -428,6 +428,12 @@ public:
 
 	player_t *CPlayer;
 
+	double Alpha = 1.;
+	DVector2 drawOffset = { 0,0 };			// can be set by subclasses to offset drawing operations
+	double drawClip[4] = { 0,0,0,0 };		// defines a clipping rectangle (not used yet)
+	bool fullscreenOffsets = false;			// current screen is displayed with fullscreen behavior.
+	DVector2 cleanScale;			// factor for scaled fullscreen display.
+
 private:
 	bool RepositionCoords (int &x, int &y, int xo, int yo, const int w, const int h) const;
 	void DrawMessages (int layer, int bottom);
diff --git a/src/g_statusbar/shared_sbar.cpp b/src/g_statusbar/shared_sbar.cpp
index c41732c14..66eeca193 100644
--- a/src/g_statusbar/shared_sbar.cpp
+++ b/src/g_statusbar/shared_sbar.cpp
@@ -57,6 +57,7 @@
 #include "cmdlib.h"
 #include "g_levellocals.h"
 #include "virtual.h"
+#include "p_acs.h"
 #include "r_data/r_translate.h"
 
 #include "../version.h"
@@ -80,6 +81,8 @@ EXTERN_CVAR (Bool, am_showtime)
 EXTERN_CVAR (Bool, am_showtotaltime)
 EXTERN_CVAR (Bool, noisedebug)
 EXTERN_CVAR (Int, con_scaletext)
+EXTERN_CVAR(Bool, hud_scale)
+EXTERN_CVAR(Bool, vid_fps)
 
 int active_con_scaletext();
 
@@ -235,6 +238,8 @@ DBaseStatusBar::DBaseStatusBar ()
 	Displacement = 0;
 	CPlayer = NULL;
 	ShowLog = false;
+	cleanScale = { (double)CleanXfac, (double)CleanYfac };
+
 }
 
 void DBaseStatusBar::SetSize(int reltop, int hres, int vres)
@@ -1242,6 +1247,10 @@ void DBaseStatusBar::ScreenSizeChanged ()
 {
 	st_scale.Callback ();
 
+	int x, y;
+	V_CalcCleanFacs(HorizontalResolution, VerticalResolution, SCREENWIDTH, SCREENHEIGHT, &x, &y);
+	cleanScale = { (double)x, (double)y };
+
 	for (size_t i = 0; i < countof(Messages); ++i)
 	{
 		DHUDMessage *message = Messages[i];
@@ -1381,15 +1390,18 @@ uint32_t DBaseStatusBar::GetTranslation() const
 // draw stuff
 //
 //============================================================================
-EXTERN_CVAR(Bool, hud_scale)
-EXTERN_CVAR(Bool, vid_fps)
 
 void DBaseStatusBar::DrawGraphic(FTextureID texture, bool animate, double x, double y, double Alpha, bool translatable, bool dim,
-	int imgAlign, int screenalign, bool alphamap, double width, double height, double scaleX, double scaleY, bool fullscreenoffsets)
+	int imgAlign, int screenalign, bool alphamap, double width, double height)
 {
 	if (!texture.isValid())
 		return;
 
+	Alpha *= this->Alpha;
+	if (Alpha <= 0) return;
+	x += drawOffset.X;
+	y += drawOffset.Y;
+
 	FTexture *tex = animate ? TexMan(texture) : TexMan[texture];
 
 	switch (imgAlign & HMASK)
@@ -1406,7 +1418,7 @@ void DBaseStatusBar::DrawGraphic(FTextureID texture, bool animate, double x, dou
 	case VOFFSET: y -= tex->GetScaledTopOffsetDouble() * height / tex->GetScaledHeightDouble(); break;
 	}
 
-	if (!fullscreenoffsets)
+	if (!fullscreenOffsets)
 	{
 		x += ST_X;
 		y += ST_Y;
@@ -1440,10 +1452,10 @@ void DBaseStatusBar::DrawGraphic(FTextureID texture, bool animate, double x, dou
 
 		if (hud_scale)
 		{
-			x *= scaleX;
-			y *= scaleY;
-			width *= scaleX;
-			height *= scaleY;
+			x *= cleanScale.X;
+			y *= cleanScale.Y;
+			width *= cleanScale.X;
+			height *= cleanScale.Y;
 		}
 		x += orgx;
 		y += orgy;
@@ -1476,10 +1488,7 @@ DEFINE_ACTION_FUNCTION(DBaseStatusBar, DrawGraphic)
 	PARAM_BOOL(alphamap);
 	PARAM_FLOAT(w);
 	PARAM_FLOAT(h);
-	PARAM_FLOAT(sx);
-	PARAM_FLOAT(sy);
-	PARAM_BOOL(fso);
-	self->DrawGraphic(FSetTextureID(texid), animate, x, y, alpha, translatable, dim, ialign, salign, alphamap, w, h, sx, sy, fso);
+	self->DrawGraphic(FSetTextureID(texid), animate, x, y, alpha, translatable, dim, ialign, salign, alphamap, w, h);
 	return 0;
 }
 
@@ -1521,6 +1530,11 @@ DEFINE_FIELD(DBaseStatusBar, CrosshairSize);
 DEFINE_FIELD(DBaseStatusBar, Displacement);
 DEFINE_FIELD(DBaseStatusBar, CPlayer);
 DEFINE_FIELD(DBaseStatusBar, ShowLog);
+DEFINE_FIELD(DBaseStatusBar, Alpha);
+DEFINE_FIELD(DBaseStatusBar, drawOffset);
+DEFINE_FIELD(DBaseStatusBar, drawClip);
+DEFINE_FIELD(DBaseStatusBar, fullscreenOffsets);
+DEFINE_FIELD(DBaseStatusBar, cleanScale);
 
 DEFINE_GLOBAL(StatusBar);
 
@@ -1544,3 +1558,18 @@ static DObject *InitObject(PClass *type, int paramnum, VM_ARGS)
 	return obj;
 }
 
+
+DEFINE_ACTION_FUNCTION(DBaseStatusBar, GetGlobalACSString)
+{
+	PARAM_PROLOGUE;
+	PARAM_INT(index);
+	ACTION_RETURN_STRING(FBehavior::StaticLookupString(ACS_GlobalVars[index]));
+}
+
+DEFINE_ACTION_FUNCTION(DBaseStatusBar, GetGlobalACSArrayString)
+{
+	PARAM_PROLOGUE;
+	PARAM_INT(arrayno);
+	PARAM_INT(index);
+	ACTION_RETURN_STRING(FBehavior::StaticLookupString(ACS_GlobalArrays[arrayno][index]));
+}
diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt
index 825e1ee41..282310bac 100644
--- a/wadsrc/static/zscript/base.txt
+++ b/wadsrc/static/zscript/base.txt
@@ -316,6 +316,7 @@ class Object native
 	native bool bDestroyed;
 
 	// These really should be global functions...
+	native static String G_SkillName();
 	native static int G_SkillPropertyInt(int p);
 	native static double G_SkillPropertyFloat(int p);
 	native static vector3, int G_PickDeathmatchStart();
@@ -514,6 +515,12 @@ struct LevelLocals native
 	native static void WorldDone();
 	native static void RemoveAllBots(bool fromlist);
 	native void SetInterMusic(String nextmap);
+	
+	String TimeFormatted()
+	{
+		int sec = Thinker.Tics2Seconds(time); 
+		return String.Format("%02d:%02d:%02d", sec / 3600, (sec % 3600) / 60, sec % 60);
+	}
 }
 
 struct StringTable native
diff --git a/wadsrc/static/zscript/statusbar/statusbar.txt b/wadsrc/static/zscript/statusbar/statusbar.txt
index 2499cfc31..12fc7d0f1 100644
--- a/wadsrc/static/zscript/statusbar/statusbar.txt
+++ b/wadsrc/static/zscript/statusbar/statusbar.txt
@@ -38,95 +38,6 @@ class BaseStatusBar native ui
 		HUD_AltHud // Used for passing through popups to the alt hud
 	}
 	
-
-	enum EHudDraw
-	{
-		HUD_Normal,
-		HUD_HorizCenter
-	}
-	
-	const XHAIRSHRINKSIZE =(1./18);
-	const XHAIRPICKUPSIZE = (2+XHAIRSHRINKSIZE);
-	
-
-	native int ST_X, ST_Y;
-	native int RelTop;
-	native int HorizontalResolution, VerticalResolution;
-	native bool Scaled;
-	native bool Centering;
-	native bool FixedOrigin;
-	native bool CompleteBorder;
-	native double CrosshairSize;
-	native double Displacement;
-	native PlayerInfo CPlayer;
-	native bool ShowLog;
-	
-	native void SetSize(int height, int vwidth, int vheight);
-	virtual void Init() {}
-
-	native virtual void SetScaled(bool scale, bool force = false);
-	native virtual void Tick ();
-	native virtual void Draw (int state, double TicFrac);
-	native virtual void ScreenSizeChanged ();
-
-	virtual void FlashItem (class<Inventory> itemtype) {}
-	virtual void AttachToPlayer (PlayerInfo player) { CPlayer = player; }
-	virtual void FlashCrosshair () { CrosshairSize = XHAIRPICKUPSIZE; }
-	virtual void NewGame () {}
-	virtual void ShowPop (int popnum) { ShowLog = (popnum == POP_Log && !ShowLog); }
-	virtual clearscope void ReceivedWeapon (Weapon weapn) {}
-	virtual bool MustDrawLog(int state) { return true; }
-	virtual void SetMugShotState (String state_name, bool wait_till_done=false, bool reset=false) {}
-	
-	native void RefreshBackground () const;
-	native Inventory ValidateInvFirst (int numVisible) const;
-	native static TextureID, bool GetInventoryIcon(Inventory item, int flags);
-
-	native void DrawGraphic(TextureID texture, bool animate, Vector2 pos, double Alpha,  bool translatable, bool dim, 
-		int imgAlign, int screenalign, bool alphamap, Vector2 box, Vector2 scale, bool fso);
-	
-	
-	//============================================================================
-	//
-	// DBaseStatusBar :: GetCurrentAmmo
-	//
-	// Returns the types and amounts of ammo used by the current weapon. If the
-	// weapon only uses one type of ammo, it is always returned as ammo1.
-	//
-	//============================================================================
-
-	Inventory, Inventory, int, int GetCurrentAmmo () const
-	{
-		Ammo ammo1, ammo2;
-		if (CPlayer.ReadyWeapon != NULL)
-		{
-			ammo1 = CPlayer.ReadyWeapon.Ammo1;
-			ammo2 = CPlayer.ReadyWeapon.Ammo2;
-			if (ammo1 == NULL)
-			{
-				ammo1 = ammo2;
-				ammo2 = NULL;
-			}
-		}
-		else
-		{
-			ammo1 = ammo2 = NULL;
-		}
-		let ammocount1 = ammo1 != NULL ? ammo1.Amount : 0;
-		let ammocount2 = ammo2 != NULL ? ammo2.Amount : 0;
-		return ammo1, ammo2, ammocount1, ammocount2;
-	}
-}
-
-//============================================================================
-//
-// This is only needed to shadow the native base class because a native class
-// cannot have scripted variables added.
-//
-//============================================================================
-
-class CustomStatusBar : BaseStatusBar
-{
 	enum DI_Flags
 	{
 		DI_SKIPICON = 0x1,
@@ -188,28 +99,91 @@ class CustomStatusBar : BaseStatusBar
 		AMMO_BOTH
 	};
 	
-
-	
-	double Alpha;
-	Vector2 drawOffset;		// can be set by subclasses to offset drawing operations
-	double drawClip[4];		// defines a clipping rectangle (not used yet)
-	bool fullscreenOffsets;	// current screen is displayed with fullscreen behavior.
-	
-	Vector2 cleanScale;		// factor for scaled fullscreen display.
-
-	override void Init()
+	enum EHudDraw
 	{
-		Super.Init();
-		Alpha = 1;
-		DrawOffset = (0, 0);
-		drawClip[0] = 0;
-		drawClip[1] = 0;
-		drawClip[2] = 0;
-		drawClip[3] = 0;
-		cleanScale = (CleanXfac, CleanYfac);
-		fullscreenOffsets = false;
+		HUD_Normal,
+		HUD_HorizCenter
 	}
 	
+	const XHAIRSHRINKSIZE =(1./18);
+	const XHAIRPICKUPSIZE = (2+XHAIRSHRINKSIZE);
+	
+
+	native int ST_X, ST_Y;
+	native int RelTop;
+	native int HorizontalResolution, VerticalResolution;
+	native bool Scaled;
+	native bool Centering;
+	native bool FixedOrigin;
+	native bool CompleteBorder;
+	native double CrosshairSize;
+	native double Displacement;
+	native PlayerInfo CPlayer;
+	native bool ShowLog;
+
+	// These are block properties for the drawers. A child class can set them to have a block of items use the same settings.
+	native double Alpha;
+	native Vector2 drawOffset;		// can be set by subclasses to offset drawing operations
+	native double drawClip[4];		// defines a clipping rectangle (not used yet)
+	native bool fullscreenOffsets;	// current screen is displayed with fullscreen behavior.
+	native Vector2 cleanScale;		// factor for scaled fullscreen display.
+	
+	
+	native void SetSize(int height, int vwidth, int vheight);
+	virtual void Init() {}
+
+	native virtual void SetScaled(bool scale, bool force = false);
+	native virtual void Tick ();
+	native virtual void Draw (int state, double TicFrac);
+	native virtual void ScreenSizeChanged ();
+
+	virtual void FlashItem (class<Inventory> itemtype) {}
+	virtual void AttachToPlayer (PlayerInfo player) { CPlayer = player; }
+	virtual void FlashCrosshair () { CrosshairSize = XHAIRPICKUPSIZE; }
+	virtual void NewGame () {}
+	virtual void ShowPop (int popnum) { ShowLog = (popnum == POP_Log && !ShowLog); }
+	virtual clearscope void ReceivedWeapon (Weapon weapn) {}
+	virtual bool MustDrawLog(int state) { return true; }
+	virtual void SetMugShotState (String state_name, bool wait_till_done=false, bool reset=false) {}
+	
+	native void RefreshBackground () const;
+	native Inventory ValidateInvFirst (int numVisible) const;
+	native static TextureID, bool GetInventoryIcon(Inventory item, int flags);
+
+	native void DrawGraphic(TextureID texture, bool animate, Vector2 pos, double Alpha,  bool translatable, bool dim, int imgAlign, int screenalign, bool alphamap, Vector2 box);
+	
+	
+	//============================================================================
+	//
+	// DBaseStatusBar :: GetCurrentAmmo
+	//
+	// Returns the types and amounts of ammo used by the current weapon. If the
+	// weapon only uses one type of ammo, it is always returned as ammo1.
+	//
+	//============================================================================
+
+	Inventory, Inventory, int, int GetCurrentAmmo () const
+	{
+		Ammo ammo1, ammo2;
+		if (CPlayer.ReadyWeapon != NULL)
+		{
+			ammo1 = CPlayer.ReadyWeapon.Ammo1;
+			ammo2 = CPlayer.ReadyWeapon.Ammo2;
+			if (ammo1 == NULL)
+			{
+				ammo1 = ammo2;
+				ammo2 = NULL;
+			}
+		}
+		else
+		{
+			ammo1 = ammo2 = NULL;
+		}
+		let ammocount1 = ammo1 != NULL ? ammo1.Amount : 0;
+		let ammocount2 = ammo2 != NULL ? ammo2.Amount : 0;
+		return ammo1, ammo2, ammocount1, ammocount2;
+	}
+
 	//============================================================================
 	//
 	// Get an icon
@@ -234,6 +208,37 @@ class CustomStatusBar : BaseStatusBar
 		return icon;
 	}
 	
+	//============================================================================
+	//
+	// Convenience functions to retrieve item tags
+	//
+	//============================================================================
+
+	String GetAmmoTag(bool secondary = false)
+	{
+		let w = CPlayer.ReadyWeapon;
+		if (w == null) return "";
+		let ammo = secondary? w.ammo2 : w.ammo1;
+		return ammo.GetTag();
+	}	
+
+	String GetWeaponTag()
+	{
+		let w = CPlayer.ReadyWeapon;
+		if (w == null) return "";
+		return w.GetTag();
+	}	
+
+	String GetSelectedInventoryTag()
+	{
+		let w = CPlayer.mo.InvSel;
+		if (w == null) return "";
+		return w.GetTag();
+	}	
+	
+	// These cannot be done in ZScript.
+	native String GetGlobalACSString(int index);
+	native String GetGlobalACSArrayString(int arrayno, int index);
 	//============================================================================
 	//
 	// various checker functions, based on SBARINFOs condition nodes.
@@ -460,7 +465,7 @@ class CustomStatusBar : BaseStatusBar
 		{
 			boxsize = texsize;
 		}
-		DrawGraphic(texture, animated, pos + drawOffset, Alpha * self.Alpha, !!(flags & DI_TRANSLATABLE), false, itemAlign, screenAlign, false, boxsize, cleanscale, fullscreenoffsets);
+		DrawGraphic(texture, animated, pos, Alpha, !!(flags & DI_TRANSLATABLE), false, itemAlign, screenAlign, false, boxsize);
 	}
 	
 	//============================================================================
diff --git a/wadsrc/static/zscript/statusbar/strife_sbar.txt b/wadsrc/static/zscript/statusbar/strife_sbar.txt
index e19f9b4a1..f75bfffb5 100644
--- a/wadsrc/static/zscript/statusbar/strife_sbar.txt
+++ b/wadsrc/static/zscript/statusbar/strife_sbar.txt
@@ -1,5 +1,5 @@
 
-class StrifeStatusBar : CustomStatusBar
+class StrifeStatusBar : BaseStatusBar
 {
 	
 	// Number of tics to move the popscreen up and down.