diff --git a/src/c_bind.cpp b/src/c_bind.cpp
index 65cdc1757..540c77f7b 100644
--- a/src/c_bind.cpp
+++ b/src/c_bind.cpp
@@ -158,6 +158,9 @@ FKeyBindings Bindings;
 FKeyBindings DoubleBindings;
 FKeyBindings AutomapBindings;
 
+DEFINE_GLOBAL(Bindings)
+DEFINE_GLOBAL(AutomapBindings)
+
 static unsigned int DClickTime[NUM_KEYS];
 static uint8_t DClicked[(NUM_KEYS+7)/8];
 
diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp
index a4ff314a7..d2c8f3b25 100644
--- a/src/d_dehacked.cpp
+++ b/src/d_dehacked.cpp
@@ -212,6 +212,7 @@ DehInfo deh =
 	40,		// BFG cells per shot
 };
 
+DEFINE_GLOBAL(deh)
 DEFINE_FIELD_X(DehInfo, DehInfo, MaxSoulsphere)
 DEFINE_FIELD_X(DehInfo, DehInfo, ExplosionStyle)
 DEFINE_FIELD_X(DehInfo, DehInfo, ExplosionAlpha)
diff --git a/src/g_game.cpp b/src/g_game.cpp
index 11c5210e5..4bb55ad49 100644
--- a/src/g_game.cpp
+++ b/src/g_game.cpp
@@ -2982,3 +2982,18 @@ bool G_CheckDemoStatus (void)
 
 	return false; 
 }
+
+DEFINE_GLOBAL(players)
+DEFINE_GLOBAL(playeringame)
+DEFINE_GLOBAL(PlayerClasses)
+DEFINE_GLOBAL_NAMED(Skins, PlayerSkins)
+DEFINE_GLOBAL(consoleplayer)
+DEFINE_GLOBAL_NAMED(PClassActor::AllActorClasses, AllActorClasses)
+DEFINE_GLOBAL(validcount)
+DEFINE_GLOBAL(multiplayer)
+DEFINE_GLOBAL(gameaction)
+DEFINE_GLOBAL(gamestate)
+DEFINE_GLOBAL(skyflatnum)
+DEFINE_GLOBAL_NAMED(bglobal.freeze, globalfreeze)
+DEFINE_GLOBAL(gametic)
+DEFINE_GLOBAL(demoplayback)
diff --git a/src/g_level.cpp b/src/g_level.cpp
index e1f77b624..f8c92a271 100644
--- a/src/g_level.cpp
+++ b/src/g_level.cpp
@@ -1901,7 +1901,7 @@ void FLevelLocals::AddScroller (int secnum)
 //
 //
 //==========================================================================
-
+DEFINE_GLOBAL(level);
 DEFINE_FIELD(FLevelLocals, sectors)
 DEFINE_FIELD(FLevelLocals, lines)
 DEFINE_FIELD(FLevelLocals, sides)
diff --git a/src/gi.cpp b/src/gi.cpp
index d30902045..cf28e96be 100644
--- a/src/gi.cpp
+++ b/src/gi.cpp
@@ -45,6 +45,7 @@
 
 gameinfo_t gameinfo;
 
+DEFINE_GLOBAL(gameinfo)
 DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, backpacktype)
 DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, Armor2Percent)
 DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, ArmorIcon1)
diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp
index ca9659376..797bd5a50 100644
--- a/src/menu/menu.cpp
+++ b/src/menu/menu.cpp
@@ -1057,7 +1057,9 @@ CCMD(undocolorpic)
 }
 
 
-
+DEFINE_GLOBAL(menuactive)
+DEFINE_GLOBAL(BackbuttonTime)
+DEFINE_GLOBAL(BackbuttonAlpha)
 
 DEFINE_FIELD(DMenu, mParentMenu)
 DEFINE_FIELD(DMenu, mMouseCapture);
diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp
index a9fd42773..04015fc5b 100644
--- a/src/menu/menudef.cpp
+++ b/src/menu/menudef.cpp
@@ -67,6 +67,8 @@ PClass *DefaultOptionMenuClass;
 
 void I_BuildALDeviceList(FOptionValues *opt);
 
+DEFINE_GLOBAL_NAMED(OptionSettings, OptionMenuSettings)
+
 DEFINE_ACTION_FUNCTION(FOptionValues, GetCount)
 {
 	PARAM_PROLOGUE;
diff --git a/src/namedef.h b/src/namedef.h
index 91fd28626..28e2ff8c4 100644
--- a/src/namedef.h
+++ b/src/namedef.h
@@ -1,6 +1,7 @@
 // 'None' must always be the first name.
 xx(None)
 xx(Null)
+xx(_)
 
 xx(Super)
 xx(Object)
diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp
index db2abf597..6ff75cc7b 100644
--- a/src/scripting/thingdef_data.cpp
+++ b/src/scripting/thingdef_data.cpp
@@ -664,11 +664,12 @@ AFuncDesc *FindFunction(PStruct *cls, const char * string)
 FieldDesc *FindField(PStruct *cls, const char * string)
 {
 	int min = 0, max = FieldTable.Size() - 1;
+	const char * cname = cls ? cls->TypeName.GetChars() : "";
 
 	while (min <= max)
 	{
 		int mid = (min + max) / 2;
-		int lexval = stricmp(cls->TypeName.GetChars(), FieldTable[mid].ClassName + 1);
+		int lexval = stricmp(cname, FieldTable[mid].ClassName + 1);
 		if (lexval == 0) lexval = stricmp(string, FieldTable[mid].FieldName);
 		if (lexval == 0)
 		{
@@ -740,9 +741,7 @@ static int fieldcmp(const void * a, const void * b)
 
 void InitThingdef()
 {
-	// Create all global variables here because this cannot be done on the script side and really isn't worth adding support for.
-	// Also create all special fields here that cannot be declared by script syntax plus the pointer serializers. Doing all these with class overrides would be a bit messy.
-
+	// Some native types need size and serialization information added before the scripts get compiled.
 	auto secplanestruct = NewNativeStruct("Secplane", nullptr);
 	secplanestruct->Size = sizeof(secplane_t);
 	secplanestruct->Align = alignof(secplane_t);
@@ -853,140 +852,11 @@ void InitThingdef()
 		}
 	);
 
-	// expose the global validcount variable.
-	PField *vcf = new PField("validcount", TypeSInt32, VARF_Native | VARF_Static, (intptr_t)&validcount);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(vcf);
-
-	// expose the global Multiplayer variable.
-	PField *multif = new PField("multiplayer", TypeBool, VARF_Native | VARF_ReadOnly | VARF_Static, (intptr_t)&multiplayer);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(multif);
-
-	// set up a variable for the global level data structure
-	PStruct *lstruct = NewNativeStruct("LevelLocals", nullptr);
-	PField *levelf = new PField("level", lstruct, VARF_Native | VARF_Static, (intptr_t)&level);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(levelf);
-
-	auto aact = NewPointer(NewStaticArray(NewClassPointer(RUNTIME_CLASS(AActor))), true);
-	PField *aacf = new PField("AllActorClasses", aact, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&PClassActor::AllActorClasses);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(aacf);
-
-	auto plrcls = NewPointer(NewStaticArray(playerclassstruct), false);
-	PField *plrclsf = new PField("PlayerClasses", plrcls, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&PlayerClasses);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(plrclsf);
-
-	auto plrskn = NewPointer(NewStaticArray(playerskinstruct), false);
-	PField *plrsknf = new PField("PlayerSkins", plrskn, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&Skins);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(plrsknf);
-
-	auto teamst = NewPointer(NewStaticArray(teamstruct), false);
-	PField *teamf = new PField("Teams", teamst, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&Teams);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(teamf);
-
-	auto bindcls = NewNativeStruct("KeyBindings", nullptr);
-	PField *binding = new PField("Bindings", bindcls, VARF_Native | VARF_Static, (intptr_t)&Bindings);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(binding);
-	binding = new PField("AutomapBindings", bindcls, VARF_Native | VARF_Static, (intptr_t)&AutomapBindings);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(binding);
-
-	// set up a variable for the DEH data
-	PStruct *dstruct = NewNativeStruct("DehInfo", nullptr);
-	PField *dehf = new PField("deh", dstruct, VARF_Native | VARF_Static, (intptr_t)&deh);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(dehf);
-
-	// set up a variable for the global gameinfo data
-	PStruct *gistruct = NewNativeStruct("GameInfoStruct", nullptr);
-	PField *gi = new PField("gameinfo", gistruct, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&gameinfo);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(gi);
-
-	// set up a variable for the global players array.
-	PArray *parray = NewArray(pstruct, MAXPLAYERS);
-	PField *fieldptr = new PField("players", parray, VARF_Native | VARF_Static, (intptr_t)&players);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
-
-	parray = NewArray(TypeBool, MAXPLAYERS);
-	fieldptr = new PField("playeringame", parray, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&playeringame);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
-
-	fieldptr = new PField("gameaction", TypeUInt32, VARF_Native | VARF_Static, (intptr_t)&gameaction);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
-
-	fieldptr = new PField("gamestate", TypeSInt32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&gamestate);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
-
-	fieldptr = new PField("skyflatnum", TypeTextureID, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&skyflatnum);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
-
-	fieldptr = new PField("globalfreeze", TypeUInt8, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&bglobal.freeze);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
-
-	fieldptr = new PField("consoleplayer", TypeSInt32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&consoleplayer);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
-
-	auto fontptr = NewPointer(NewNativeStruct("Font", nullptr));
-
-	fieldptr = new PField("smallfont", fontptr, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&SmallFont);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
-
-	fieldptr = new PField("smallfont2", fontptr, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&SmallFont2);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
-
-	fieldptr = new PField("bigfont", fontptr, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&BigFont);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
-
-	fieldptr = new PField("confont", fontptr, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&ConFont);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
-
-	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("menuactive", TypeSInt32, VARF_Native | VARF_Static, (intptr_t)&menuactive);
-	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);
-
-	fieldptr = new PField("gametic", TypeSInt32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&gametic);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
-
-	fieldptr = new PField("demoplayback", TypeBool, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&demoplayback);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
-
-	fieldptr = new PField("BackbuttonTime", TypeSInt32, VARF_Native | VARF_Static, (intptr_t)&BackbuttonTime);
-	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
-
-	fieldptr = new PField("BackbuttonAlpha", TypeFloat32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&BackbuttonAlpha);
-	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!!!
 	wpnochg = WP_NOCHANGE;
-	fieldptr = new PField("WP_NOCHANGE", NewPointer(RUNTIME_CLASS(AWeapon), false), VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&wpnochg);
+	PField *fieldptr = new PField("WP_NOCHANGE", NewPointer(RUNTIME_CLASS(AWeapon), false), VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&wpnochg);
 	Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
 
 	// synthesize a symbol for each flag from the flag name tables to avoid redundant declaration of them.
diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h
index df5d8a398..4b1f406b1 100644
--- a/src/scripting/vm/vm.h
+++ b/src/scripting/vm/vm.h
@@ -1170,7 +1170,12 @@ struct AFuncDesc
 	MSVC_FSEG FieldDesc const *const VMField_##cls##_##scriptname##_HookPtr GCC_FSEG = &VMField_##cls##_##scriptname;
 
 #define DEFINE_GLOBAL(name) \
-	static const FieldDesc VMGlobal_##name = { nullptr, #name, (intptr_t)&name, (unsigned)sizeof(name), 0 }; \
+	static const FieldDesc VMGlobal_##name = { "", #name, (intptr_t)&name, (unsigned)sizeof(name), 0 }; \
+	extern FieldDesc const *const VMGlobal_##name##_HookPtr; \
+	MSVC_FSEG FieldDesc const *const VMGlobal_##name##_HookPtr GCC_FSEG = &VMGlobal_##name;
+
+#define DEFINE_GLOBAL_NAMED(iname, name) \
+	static const FieldDesc VMGlobal_##name = { "", #name, (intptr_t)&iname, (unsigned)sizeof(iname), 0 }; \
 	extern FieldDesc const *const VMGlobal_##name##_HookPtr; \
 	MSVC_FSEG FieldDesc const *const VMGlobal_##name##_HookPtr GCC_FSEG = &VMGlobal_##name;
 
diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp
index 6202be534..7dc916be2 100644
--- a/src/scripting/zscript/zcc_compile.cpp
+++ b/src/scripting/zscript/zcc_compile.cpp
@@ -487,7 +487,13 @@ void ZCCCompiler::CreateStructTypes()
 			syms = &OutNamespace->Symbols;
 		}
 
-		if (s->strct->Flags & ZCC_Native)
+		if (s->NodeName() == NAME__ && Wads.GetLumpFile(Lump) == 0)
+		{
+			// This is just a container for syntactic purposes.
+			s->strct->Type = nullptr;
+			continue;
+		}
+		else if (s->strct->Flags & ZCC_Native)
 		{
 			s->strct->Type = NewNativeStruct(s->NodeName(), outer);
 		}
@@ -776,7 +782,8 @@ void ZCCCompiler::CompileAllConstants()
 	}
 	for (auto s : Structs)
 	{
-		CopyConstants(constantwork, s->Constants, s->Type(), &s->Type()->Symbols);
+		if (s->Type() != nullptr)
+			CopyConstants(constantwork, s->Constants, s->Type(), &s->Type()->Symbols);
 	}
 
 	// Before starting to resolve the list, let's create symbols for all already resolved ones first (i.e. all literal constants), to reduce work.
@@ -1048,7 +1055,7 @@ void ZCCCompiler::CompileAllFields()
 		donesomething = false;
 		for (unsigned i = 0; i < Structs.Size(); i++)
 		{
-			if (CompileFields(Structs[i]->Type(), Structs[i]->Fields, Structs[i]->Outer, &Structs[i]->TreeNodes, true))
+			if (CompileFields(Structs[i]->Type(), Structs[i]->Fields, Structs[i]->Outer, Structs[i]->Type() == 0? GlobalTreeNodes : &Structs[i]->TreeNodes, true))
 			{
 				// Remove from the list if all fields got compiled.
 				Structs.Delete(i--);
@@ -1132,10 +1139,13 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
 		if (field->Flags & ZCC_Transient) varflags |= VARF_Transient;
 		if (mVersion >= MakeVersion(2, 4, 0))
 		{
-			if (type->ObjectFlags & OF_UI)
-				varflags |= VARF_UI;
-			if (type->ObjectFlags & OF_Play)
-				varflags |= VARF_Play;
+			if (type != nullptr)
+			{
+				if (type->ObjectFlags & OF_UI)
+					varflags |= VARF_UI;
+				if (type->ObjectFlags & OF_Play)
+					varflags |= VARF_Play;
+			}
 			if (field->Flags & ZCC_UIFlag)
 				varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_UI);
 			if (field->Flags & ZCC_Play)
@@ -1202,21 +1212,33 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
 						fd = FindField(type, FName(name->Name).GetChars());
 						if (fd == nullptr)
 						{
-							Error(field, "The member variable '%s.%s' has not been exported from the executable.", type->TypeName.GetChars(), FName(name->Name).GetChars());
+							Error(field, "The member variable '%s.%s' has not been exported from the executable.", type == nullptr? "" : type->TypeName.GetChars(), FName(name->Name).GetChars());
 						}
 						// For native structs a size check cannot be done because they normally have no size. But for a native reference they are still fine.
 						else if (thisfieldtype->Size != ~0u && thisfieldtype->Size != fd->FieldSize && fd->BitValue == 0 && !thisfieldtype->IsA(RUNTIME_CLASS(PNativeStruct)))
 						{
-							Error(field, "The member variable '%s.%s' has mismatching sizes in internal and external declaration. (Internal = %d, External = %d)", type->TypeName.GetChars(), FName(name->Name).GetChars(), fd->FieldSize, thisfieldtype->Size);
+							Error(field, "The member variable '%s.%s' has mismatching sizes in internal and external declaration. (Internal = %d, External = %d)", type == nullptr ? "" : type->TypeName.GetChars(), FName(name->Name).GetChars(), fd->FieldSize, thisfieldtype->Size);
 						}
 						// Q: Should we check alignment, too? A mismatch may be an indicator for bad assumptions.
-						else
+						else if (type != nullptr)
 						{
 							// for bit fields the type must point to the source variable.
 							if (fd->BitValue != 0) thisfieldtype = fd->FieldSize == 1 ? TypeUInt8 : fd->FieldSize == 2 ? TypeUInt16 : TypeUInt32;
 							auto f = type->AddNativeField(name->Name, thisfieldtype, fd->FieldOffset, varflags, fd->BitValue);
 							if (field->Flags & (ZCC_Version | ZCC_Deprecated)) f->mVersion = field->Version;
 						}
+						else
+						{
+							// This is a global variable.
+							if (fd->BitValue != 0) thisfieldtype = fd->FieldSize == 1 ? TypeUInt8 : fd->FieldSize == 2 ? TypeUInt16 : TypeUInt32;
+							PField *field = new PField(name->Name, thisfieldtype, varflags | VARF_Native | VARF_Static, fd->FieldOffset, fd->BitValue);
+
+							if (OutNamespace->Symbols.AddSymbol(field) == nullptr)
+							{ // name is already in use
+								field->Destroy();
+								return nullptr;
+							}
+						}
 					}
 				}
 				else if (hasnativechildren)
@@ -1432,7 +1454,7 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n
 			{
 				Error(field, "%s: @ not allowed for user scripts", name.GetChars());
 			}
-			retval = ResolveUserType(btype, &outertype->Symbols, true);
+			retval = ResolveUserType(btype, outertype? &outertype->Symbols : nullptr, true);
 			break;
 
 		case ZCC_UserType:
@@ -1456,7 +1478,7 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n
 				break;
 
 			default:
-				retval = ResolveUserType(btype, &outertype->Symbols, false);
+				retval = ResolveUserType(btype, outertype ? &outertype->Symbols : nullptr, false);
 				break;
 			}
 			break;
@@ -1515,7 +1537,7 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n
 			// This doesn't check the class list directly but the current symbol table to ensure that
 			// this does not reference a type that got shadowed by a more local definition.
 			// We first look in the current class and its parents, and then in the current namespace and its parents.
-			auto sym = outertype->Symbols.FindSymbol(ctype->Restriction->Id, true);
+			auto sym = outertype ? outertype->Symbols.FindSymbol(ctype->Restriction->Id, true) : nullptr;
 			if (sym == nullptr) sym = OutNamespace->Symbols.FindSymbol(ctype->Restriction->Id, true);
 			if (sym == nullptr)
 			{
@@ -1562,8 +1584,10 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n
 PType *ZCCCompiler::ResolveUserType(ZCC_BasicType *type, PSymbolTable *symt, bool nativetype)
 {
 	// Check the symbol table for the identifier.
-	PSymbol *sym = symt->FindSymbol(type->UserType->Id, true);
+	PSymbol *sym = nullptr;
+	
 	// We first look in the current class and its parents, and then in the current namespace and its parents.
+	if (symt != nullptr) sym = symt->FindSymbol(type->UserType->Id, true);
 	if (sym == nullptr) sym = OutNamespace->Symbols.FindSymbol(type->UserType->Id, true);
 	if (sym != nullptr && sym->IsKindOf(RUNTIME_CLASS(PSymbolType)))
 	{
diff --git a/src/teaminfo.cpp b/src/teaminfo.cpp
index b9d5d24aa..0658545b3 100644
--- a/src/teaminfo.cpp
+++ b/src/teaminfo.cpp
@@ -335,4 +335,5 @@ CCMD (teamlist)
 }
 
 
-DEFINE_FIELD_NAMED(FTeam, m_Name, mName)
\ No newline at end of file
+DEFINE_GLOBAL(Teams)
+DEFINE_FIELD_NAMED(FTeam, m_Name, mName)
diff --git a/src/v_video.cpp b/src/v_video.cpp
index 4fcce944c..c942cb9fe 100644
--- a/src/v_video.cpp
+++ b/src/v_video.cpp
@@ -1783,3 +1783,17 @@ CCMD(vid_listadapters)
 	if (Video != NULL)
 		Video->DumpAdapters();
 }
+
+DEFINE_GLOBAL(SmallFont)
+DEFINE_GLOBAL(SmallFont2)
+DEFINE_GLOBAL(BigFont)
+DEFINE_GLOBAL(ConFont)
+DEFINE_GLOBAL(IntermissionFont)
+DEFINE_GLOBAL(CleanXfac)
+DEFINE_GLOBAL(CleanYfac)
+DEFINE_GLOBAL(CleanWidth)
+DEFINE_GLOBAL(CleanHeight)
+DEFINE_GLOBAL(CleanXfac_1)
+DEFINE_GLOBAL(CleanYfac_1)
+DEFINE_GLOBAL(CleanWidth_1)
+DEFINE_GLOBAL(CleanHeight_1)
diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt
index 3b08b8170..e509702e6 100644
--- a/wadsrc/static/zscript/base.txt
+++ b/wadsrc/static/zscript/base.txt
@@ -1,3 +1,46 @@
+struct _ native
+{
+	native readonly Array<class<Actor> > AllActorClasses;
+	native readonly Array<@PlayerClass> PlayerClasses;
+	native readonly Array<@PlayerSkin> PlayerSkins;
+	native readonly Array<@Team> Teams;
+	native int validcount;
+	native readonly bool multiplayer;
+	native play @LevelLocals level;
+	native @KeyBindings Bindings;
+	native @KeyBindings AutomapBindings;
+	native play @DehInfo deh;
+	native readonly @GameInfoStruct gameinfo;
+	native play @PlayerInfo players[MAXPLAYERS];
+	native readonly bool playeringame[MAXPLAYERS];
+
+	native play uint gameaction;
+	native readonly int gamestate;
+	native readonly TextureID skyflatnum;
+	native readonly uint8 globalfreeze;
+	native readonly int consoleplayer;
+	native readonly Font smallfont;
+	native readonly Font smallfont2;
+	native readonly Font bigfont;
+	native readonly Font confont;
+	native readonly Font intermissionfont;
+	native readonly int CleanXFac;
+	native readonly int CleanYFac;
+	native readonly int CleanWidth;
+	native readonly int CleanHeight;
+	native readonly int CleanXFac_1;
+	native readonly int CleanYFac_1;
+	native readonly int CleanWidth_1;
+	native readonly int CleanHeight_1;
+	native ui int menuactive;
+	native readonly @FOptionMenuSettings OptionMenuSettings;
+	native readonly int gametic;
+	native readonly bool demoplayback;
+	native ui int BackbuttonTime;
+	native ui float BackbuttonAlpha;
+	
+}
+
 struct TexMan
 {
 	enum EUseTypes
diff --git a/wadsrc/static/zscript/menu/optionmenu.txt b/wadsrc/static/zscript/menu/optionmenu.txt
index 99405cd88..dc428d1de 100644
--- a/wadsrc/static/zscript/menu/optionmenu.txt
+++ b/wadsrc/static/zscript/menu/optionmenu.txt
@@ -32,7 +32,7 @@
 **
 */
 
-struct FOptionMenuSettings version("2.4")
+struct FOptionMenuSettings native version("2.4")
 {
 	int mTitleColor;
 	int mFontColor;