diff --git a/src/menu/colorpickermenu.cpp b/src/menu/colorpickermenu.cpp
index 7970382bb..6f73a709e 100644
--- a/src/menu/colorpickermenu.cpp
+++ b/src/menu/colorpickermenu.cpp
@@ -124,55 +124,6 @@ public:
 	//
 	//=============================================================================
 
-	bool MouseEvent(int type, int mx, int my)
-	{
-		int olditem = mDesc->mSelectedItem;
-		bool res = Super::MouseEvent(type, mx, my);
-
-		if (mDesc->mSelectedItem == -1 || mDesc->mSelectedItem == mStartItem+7)
-		{
-			int y = (-mDesc->mPosition + BigFont->GetHeight() + mDesc->mItems.Size() * OptionSettings.mLinespacing) * CleanYfac_1;
-			int h = (screen->GetHeight() - y) / 16;
-			int fh = OptionSettings.mLinespacing * CleanYfac_1;
-			int w = fh;
-			int yy = y + 2 * CleanYfac_1;
-			int indent = (screen->GetWidth() / 2);
-
-			if (h > fh) h = fh;
-			else if (h < 4) return res;	// no space to draw it.
-
-			int box_y = y - 2 * CleanYfac_1;
-			int box_x = indent - 16*w;
-
-			if (mx >= box_x && mx < box_x + 16*w && my >= box_y && my < box_y + 16*h)
-			{
-				int cell_x = (mx - box_x) / w;
-				int cell_y = (my - box_y) / h;
-
-				if (olditem != mStartItem+7 || cell_x != mGridPosX || cell_y != mGridPosY)
-				{
-					mGridPosX = cell_x;
-					mGridPosY = cell_y;
-					//S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
-				}
-				mDesc->mSelectedItem = mStartItem+7;
-				if (type == MOUSE_Release)
-				{
-					MenuEvent(MKEY_Enter, true);
-					if (m_use_mouse == 2) mDesc->mSelectedItem = -1;
-				}
-				res = true;
-			}
-		}
-		return res;
-	}
-
-	//=============================================================================
-	//
-	//
-	//
-	//=============================================================================
-
 	void Drawer()
 	{
 		Super::Drawer();
diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp
index 8b9094f72..adaf1990c 100644
--- a/src/menu/menu.cpp
+++ b/src/menu/menu.cpp
@@ -149,7 +149,7 @@ bool DMenu::Responder (event_t *ev)
 			res = MouseEventBack(MOUSE_Click, ev->data1, ev->data2);
 			// make the menu's mouse handler believe that the current coordinate is outside the valid range
 			if (res) ev->data2 = -1;	
-			res |= MouseEvent(MOUSE_Click, ev->data1, ev->data2);
+			res |= CallMouseEvent(MOUSE_Click, ev->data1, ev->data2);
 			if (res)
 			{
 				SetCapture();
@@ -163,7 +163,7 @@ bool DMenu::Responder (event_t *ev)
 			{
 				res = MouseEventBack(MOUSE_Move, ev->data1, ev->data2);
 				if (res) ev->data2 = -1;	
-				res |= MouseEvent(MOUSE_Move, ev->data1, ev->data2);
+				res |= CallMouseEvent(MOUSE_Move, ev->data1, ev->data2);
 			}
 		}
 		else if (ev->subtype == EV_GUI_LButtonUp)
@@ -173,7 +173,7 @@ bool DMenu::Responder (event_t *ev)
 				ReleaseCapture();
 				res = MouseEventBack(MOUSE_Release, ev->data1, ev->data2);
 				if (res) ev->data2 = -1;	
-				res |= MouseEvent(MOUSE_Release, ev->data1, ev->data2);
+				res |= CallMouseEvent(MOUSE_Release, ev->data1, ev->data2);
 			}
 		}
 	}
@@ -217,7 +217,7 @@ bool DMenu::CallMenuEvent(int mkey, bool fromcontroller)
 		int retval;
 		VMReturn ret(&retval);
 		GlobalVMStack.Call(func, params, 3, &ret, 1, nullptr);
-		return retval;
+		return !!retval;
 	}
 	else return MenuEvent(mkey, fromcontroller);
 }
@@ -253,6 +253,28 @@ bool DMenu::MouseEvent(int type, int x, int y)
 	return true;
 }
 
+DEFINE_ACTION_FUNCTION(DMenu, MouseEvent)
+{
+	PARAM_SELF_PROLOGUE(DMenu);
+	PARAM_INT(type);
+	PARAM_INT(x);
+	PARAM_INT(y);
+	ACTION_RETURN_BOOL(self->MouseEvent(type, x, y));
+}
+
+bool DMenu::CallMouseEvent(int type, int x, int y)
+{
+	IFVIRTUAL(DMenu, MouseEvent)
+	{
+		VMValue params[] = { (DObject*)this, type, x, y };
+		int retval;
+		VMReturn ret(&retval);
+		GlobalVMStack.Call(func, params, 4, &ret, 1, nullptr);
+		return !!retval;
+	}
+	else return MouseEvent (type, x, y);
+}
+
 //=============================================================================
 //
 //
@@ -1034,7 +1056,7 @@ CCMD(reset2saved)
 //native OptionMenuItem OptionMenuDescriptor.GetItem(Name iname);
 //native void OptionMenuItem.drawLabel(int indent, int y, EColorRange color, bool grayed = false);
 
-DEFINE_FIELD(DMenuDescriptor, mMenuName) 
+DEFINE_FIELD(DMenuDescriptor, mMenuName)
 DEFINE_FIELD(DMenuDescriptor, mNetgameMessage)
 DEFINE_FIELD(DMenuDescriptor, mClass)
 
@@ -1062,3 +1084,11 @@ DEFINE_FIELD(DOptionMenu, VisBottom)
 DEFINE_FIELD(DOptionMenu, mFocusControl)
 DEFINE_FIELD(DOptionMenu, mDesc)
 
+DEFINE_FIELD(FOptionMenuSettings, mTitleColor)
+DEFINE_FIELD(FOptionMenuSettings, mFontColor)
+DEFINE_FIELD(FOptionMenuSettings, mFontColorValue)
+DEFINE_FIELD(FOptionMenuSettings, mFontColorMore)
+DEFINE_FIELD(FOptionMenuSettings, mFontColorHeader)
+DEFINE_FIELD(FOptionMenuSettings, mFontColorHighlight)
+DEFINE_FIELD(FOptionMenuSettings, mFontColorSelection)
+DEFINE_FIELD(FOptionMenuSettings, mLinespacing)
diff --git a/src/menu/menu.h b/src/menu/menu.h
index a3c1f86ea..153c79226 100644
--- a/src/menu/menu.h
+++ b/src/menu/menu.h
@@ -244,6 +244,7 @@ public:
 	virtual bool MouseEvent(int type, int x, int y);
 
 	bool CallMenuEvent(int mkey, bool fromcontroller);
+	bool CallMouseEvent(int type, int x, int y);
 
 	bool MouseEventBack(int type, int x, int y);
 	void SetCapture();
diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp
index 71ecfc58c..1307693cf 100644
--- a/src/scripting/backend/codegen.cpp
+++ b/src/scripting/backend/codegen.cpp
@@ -7747,6 +7747,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
 		if (MethodName == NAME_Size)
 		{
 			FxExpression *x = new FxMemberIdentifier(Self, NAME_Size, ScriptPosition);	// todo: obfuscate the name to prevent direct access.
+			Self->ValueType = static_cast<PDynArray*>(Self->ValueType)->BackingType;
 			Self = nullptr;
 			delete this;
 			return x->Resolve(ctx);
diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp
index 97f6ae10b..8cd10b75d 100644
--- a/src/scripting/thingdef_data.cpp
+++ b/src/scripting/thingdef_data.cpp
@@ -54,6 +54,8 @@
 #include "p_checkposition.h"
 #include "r_sky.h"
 #include "v_font.h"
+#include "v_video.h"
+#include "menu/menu.h"
 
 static TArray<FPropertyInfo*> properties;
 static TArray<AFuncDesc> AFTable;
@@ -841,6 +843,34 @@ void InitThingdef()
 	fieldptr = new PField("intermissionfont", fontptr, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&IntermissionFont);
 	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
 
+	fieldptr = new PField("CleanXFac", TypeSInt32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&CleanXfac);
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
+
+	fieldptr = new PField("CleanYFac", TypeSInt32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&CleanYfac);
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
+
+	fieldptr = new PField("CleanWidth", TypeSInt32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&CleanWidth);
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
+
+	fieldptr = new PField("CleanHeight", TypeSInt32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&CleanHeight);
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
+
+	fieldptr = new PField("CleanXFac_1", TypeSInt32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&CleanXfac_1);
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
+
+	fieldptr = new PField("CleanYFac_1", TypeSInt32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&CleanYfac_1);
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
+
+	fieldptr = new PField("CleanWidth_1", TypeSInt32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&CleanWidth_1);
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
+
+	fieldptr = new PField("CleanHeight_1", TypeSInt32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&CleanHeight_1);
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
+
+	fieldptr = new PField("OptionMenuSettings", NewStruct("FOptionMenuSettings", nullptr), VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&OptionSettings);
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
+
+	
 	// Argh. It sucks when bad hacks need to be supported. WP_NOCHANGE is just a bogus pointer but it used everywhere as a special flag.
 	// It cannot be defined as constant because constants can either be numbers or strings but nothing else, so the only 'solution'
 	// is to create a static variable from it and reference that in the script. Yuck!!!
diff --git a/src/v_font.cpp b/src/v_font.cpp
index 00d3b10f7..669054001 100644
--- a/src/v_font.cpp
+++ b/src/v_font.cpp
@@ -853,6 +853,12 @@ DEFINE_ACTION_FUNCTION(FFont, GetCharWidth)
 	ACTION_RETURN_INT(self->GetCharWidth(code));
 }
 
+DEFINE_ACTION_FUNCTION(FFont, GetHeight)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FFont);
+	ACTION_RETURN_INT(self->GetHeight());
+}
+
 //==========================================================================
 //
 // Find string width using this font
diff --git a/src/v_font.h b/src/v_font.h
index 4c78b3007..21d773d9d 100644
--- a/src/v_font.h
+++ b/src/v_font.h
@@ -40,7 +40,7 @@ class DCanvas;
 struct FRemapTable;
 class FTexture;
 
-enum EColorRange
+enum EColorRange : int
 {
 	CR_UNDEFINED = -1,
 	CR_BRICK,
@@ -67,7 +67,6 @@ enum EColorRange
 	CR_DARKGRAY,
 	CR_CYAN,
 	NUM_TEXT_COLORS,
-	FORCE_DWORD = 0x7fffffff	// required for script access.
 };
 
 extern int NumTextColors;
diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt
index 269e32ef2..863e8d010 100644
--- a/wadsrc/static/zscript/base.txt
+++ b/wadsrc/static/zscript/base.txt
@@ -132,7 +132,12 @@ struct Screen native
 		CR_CYAN,
 		NUM_TEXT_COLORS
 	};
-
+	
+	int CleanWidth, CleanHeight;
+	int CleanXFac, CleanYFac;
+	int CleanWidth_1, CleanHeight_1;
+	int CleanXFac_1, CleanYFac_1;
+	
 	native static Color PaletteColor(int index);
 	native static int GetWidth();
 	native static int GetHeight();
@@ -154,6 +159,8 @@ struct Font native
 {
 	native int GetCharWidth(int code);
 	native int StringWidth(String code);
+	native int GetHeight();
+
 	native static int FindFontColor(Name color);
 	native static Font FindFont(Name fontname);
 	native static Font GetFont(Name fontname);
diff --git a/wadsrc/static/zscript/menu/colorpickermenu.txt b/wadsrc/static/zscript/menu/colorpickermenu.txt
index 017c89796..2ed3cf076 100644
--- a/wadsrc/static/zscript/menu/colorpickermenu.txt
+++ b/wadsrc/static/zscript/menu/colorpickermenu.txt
@@ -98,5 +98,54 @@ class ColorpickerMenu : Menu native
 		return Super.MenuEvent(mkey, fromcontroller);
 	}
 
+	//=============================================================================
+	//
+	//
+	//
+	//=============================================================================
+
+	override bool MouseEvent(int type, int mx, int my)
+	{
+		int olditem = mDesc.mSelectedItem;
+		bool res = Super.MouseEvent(type, mx, my);
+
+		if (mDesc.mSelectedItem == -1 || mDesc.mSelectedItem == mStartItem+7)
+		{
+			int y = (-mDesc.mPosition + BigFont.GetHeight() + mDesc.mItems.Size() * OptionMenuSettings.mLinespacing) * CleanYfac_1;
+			int h = (screen.GetHeight() - y) / 16;
+			int fh = OptionMenuSettings.mLinespacing * CleanYfac_1;
+			int w = fh;
+			int yy = y + 2 * CleanYfac_1;
+			int indent = (screen.GetWidth() / 2);
+
+			if (h > fh) h = fh;
+			else if (h < 4) return res;	// no space to draw it.
+
+			int box_y = y - 2 * CleanYfac_1;
+			int box_x = indent - 16*w;
+
+			if (mx >= box_x && mx < box_x + 16*w && my >= box_y && my < box_y + 16*h)
+			{
+				int cell_x = (mx - box_x) / w;
+				int cell_y = (my - box_y) / h;
+
+				if (olditem != mStartItem+7 || cell_x != mGridPosX || cell_y != mGridPosY)
+				{
+					mGridPosX = cell_x;
+					mGridPosY = cell_y;
+					//S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
+				}
+				mDesc.mSelectedItem = mStartItem+7;
+				if (type == MOUSE_Release)
+				{
+					MenuEvent(MKEY_Enter, true);
+					if (m_use_mouse == 2) mDesc.mSelectedItem = -1;
+				}
+				res = true;
+			}
+		}
+		return res;
+	}
+
 	
 }
\ No newline at end of file
diff --git a/wadsrc/static/zscript/menu/menu.txt b/wadsrc/static/zscript/menu/menu.txt
index 2dbfa6e79..14ff55fb9 100644
--- a/wadsrc/static/zscript/menu/menu.txt
+++ b/wadsrc/static/zscript/menu/menu.txt
@@ -1,30 +1,38 @@
 
-enum EMenuKey
-{
-	MKEY_Up,
-	MKEY_Down,
-	MKEY_Left,
-	MKEY_Right,
-	MKEY_PageUp,
-	MKEY_PageDown,
-	MKEY_Enter,
-	MKEY_Back,
-	MKEY_Clear,
-	NUM_MKEYS,
-
-	// These are not buttons but events sent from other menus 
-
-	MKEY_Input,
-	MKEY_Abort,
-	MKEY_MBYes,
-	MKEY_MBNo,
-}
-
 class Menu : Object native
 {
+	enum EMenuKey
+	{
+		MKEY_Up,
+		MKEY_Down,
+		MKEY_Left,
+		MKEY_Right,
+		MKEY_PageUp,
+		MKEY_PageDown,
+		MKEY_Enter,
+		MKEY_Back,
+		MKEY_Clear,
+		NUM_MKEYS,
+
+		// These are not buttons but events sent from other menus 
+
+		MKEY_Input,
+		MKEY_Abort,
+		MKEY_MBYes,
+		MKEY_MBNo,
+	}
+
+	enum EMenuMouse
+	{
+		MOUSE_Click,
+		MOUSE_Move,
+		MOUSE_Release
+	};
+
 	//native static int MenuTime();
 	
 	native virtual bool MenuEvent (int mkey, bool fromcontroller);
+	native virtual bool MouseEvent(int type, int mx, int my);
 	
 	void MenuSound(Sound snd)
 	{