diff --git a/src/dobjgc.cpp b/src/dobjgc.cpp
index 230dbeac7..0a44599d0 100644
--- a/src/dobjgc.cpp
+++ b/src/dobjgc.cpp
@@ -379,7 +379,7 @@ static void MarkRoot()
 		Mark(PClass::AllClasses[i]);
 	}
 	// Mark global symbols
-	GlobalSymbols.MarkSymbols();
+	Namespaces.MarkSymbols();
 	// Mark bot stuff.
 	Mark(bglobal.firstthing);
 	Mark(bglobal.body1);
diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp
index c3ab87e11..dbd3e6d6f 100644
--- a/src/dobjtype.cpp
+++ b/src/dobjtype.cpp
@@ -65,8 +65,9 @@ EXTERN_CVAR(Bool, strictdecorate);
 
 // PUBLIC DATA DEFINITIONS -------------------------------------------------
 
+FNamespaceManager Namespaces;
+
 FTypeTable TypeTable;
-PSymbolTable GlobalSymbols;
 TArray<PClass *> PClass::AllClasses;
 bool PClass::bShutdown;
 bool PClass::bVMOperational;
@@ -117,7 +118,7 @@ void DumpTypeTable()
 		Printf("%4zu:", i);
 		for (PType *ty = TypeTable.TypeHash[i]; ty != NULL; ty = ty->HashNext)
 		{
-			Printf(" -> %s", ty->IsKindOf(RUNTIME_CLASS(PNamedType)) ? static_cast<PNamedType*>(ty)->TypeName.GetChars(): ty->GetClass()->TypeName.GetChars());
+			Printf(" -> %s", ty->DescriptiveName());
 			len++;
 			all++;
 		}
@@ -434,7 +435,7 @@ void PType::StaticInit()
 
 	TypeVoidPtr = NewPointer(TypeVoid, false);
 	TypeColorStruct = NewStruct("@ColorStruct", nullptr);	//This name is intentionally obfuscated so that it cannot be used explicitly. The point of this type is to gain access to the single channels of a color value.
-	TypeStringStruct = NewNativeStruct(NAME_String, nullptr);
+	TypeStringStruct = NewNativeStruct("Stringstruct", nullptr);
 #ifdef __BIG_ENDIAN__
 	TypeColorStruct->AddField(NAME_a, TypeUInt8);
 	TypeColorStruct->AddField(NAME_r, TypeUInt8);
@@ -472,24 +473,24 @@ void PType::StaticInit()
 
 
 
-	GlobalSymbols.AddSymbol(new PSymbolType(NAME_sByte, TypeSInt8));
-	GlobalSymbols.AddSymbol(new PSymbolType(NAME_Byte, TypeUInt8));
-	GlobalSymbols.AddSymbol(new PSymbolType(NAME_Short, TypeSInt16));
-	GlobalSymbols.AddSymbol(new PSymbolType(NAME_uShort, TypeUInt16));
-	GlobalSymbols.AddSymbol(new PSymbolType(NAME_Int, TypeSInt32));
-	GlobalSymbols.AddSymbol(new PSymbolType(NAME_uInt, TypeUInt32));
-	GlobalSymbols.AddSymbol(new PSymbolType(NAME_Bool, TypeBool));
-	GlobalSymbols.AddSymbol(new PSymbolType(NAME_Float, TypeFloat64));
-	GlobalSymbols.AddSymbol(new PSymbolType(NAME_Double, TypeFloat64));
-	GlobalSymbols.AddSymbol(new PSymbolType(NAME_Float32, TypeFloat32));
-	GlobalSymbols.AddSymbol(new PSymbolType(NAME_Float64, TypeFloat64));
-	GlobalSymbols.AddSymbol(new PSymbolType(NAME_String, TypeString));
-	GlobalSymbols.AddSymbol(new PSymbolType(NAME_Name, TypeName));
-	GlobalSymbols.AddSymbol(new PSymbolType(NAME_Sound, TypeSound));
-	GlobalSymbols.AddSymbol(new PSymbolType(NAME_Color, TypeColor));
-	GlobalSymbols.AddSymbol(new PSymbolType(NAME_State, TypeState));
-	GlobalSymbols.AddSymbol(new PSymbolType(NAME_Vector2, TypeVector2));
-	GlobalSymbols.AddSymbol(new PSymbolType(NAME_Vector3, TypeVector3));
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_sByte, TypeSInt8));
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Byte, TypeUInt8));
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Short, TypeSInt16));
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_uShort, TypeUInt16));
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Int, TypeSInt32));
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_uInt, TypeUInt32));
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Bool, TypeBool));
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Float, TypeFloat64));
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Double, TypeFloat64));
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Float32, TypeFloat32));
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Float64, TypeFloat64));
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_String, TypeString));
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Name, TypeName));
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Sound, TypeSound));
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Color, TypeColor));
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_State, TypeState));
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Vector2, TypeVector2));
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Vector3, TypeVector3));
 }
 
 
@@ -1692,7 +1693,7 @@ PClassPointer *NewClassPointer(PClass *restrict)
 IMPLEMENT_CLASS(PEnum, false, true)
 
 IMPLEMENT_POINTERS_START(PEnum)
-	IMPLEMENT_POINTER(ValueType)
+	IMPLEMENT_POINTER(Outer)
 IMPLEMENT_POINTERS_END
 
 //==========================================================================
@@ -1702,7 +1703,7 @@ IMPLEMENT_POINTERS_END
 //==========================================================================
 
 PEnum::PEnum()
-: ValueType(NULL)
+: PInt(4, false)
 {
 	mDescriptiveName = "Enum";
 }
@@ -1714,8 +1715,10 @@ PEnum::PEnum()
 //==========================================================================
 
 PEnum::PEnum(FName name, PTypeBase *outer)
-: PNamedType(name, outer), ValueType(NULL)
+: PInt(4, false)
 {
+	EnumName = name;
+	Outer = outer;
 	mDescriptiveName.Format("Enum<%s>", name.GetChars());
 }
 
@@ -1731,6 +1734,7 @@ PEnum::PEnum(FName name, PTypeBase *outer)
 PEnum *NewEnum(FName name, PTypeBase *outer)
 {
 	size_t bucket;
+	if (outer == nullptr) outer = Namespaces.GlobalNamespace;
 	PType *etype = TypeTable.FindType(RUNTIME_CLASS(PEnum), (intptr_t)outer, (intptr_t)name, &bucket);
 	if (etype == NULL)
 	{
@@ -2373,6 +2377,7 @@ size_t PStruct::PropagateMark()
 PStruct *NewStruct(FName name, PTypeBase *outer)
 {
 	size_t bucket;
+	if (outer == nullptr) outer = Namespaces.GlobalNamespace;
 	PType *stype = TypeTable.FindType(RUNTIME_CLASS(PStruct), (intptr_t)outer, (intptr_t)name, &bucket);
 	if (stype == NULL)
 	{
@@ -2392,8 +2397,8 @@ IMPLEMENT_CLASS(PNativeStruct, false, false)
 //
 //==========================================================================
 
-PNativeStruct::PNativeStruct(FName name)
-	: PStruct(name, nullptr)
+PNativeStruct::PNativeStruct(FName name, PTypeBase *outer)
+	: PStruct(name, outer)
 {
 	mDescriptiveName.Format("NativeStruct<%s>", name.GetChars());
 	Size = 0;
@@ -2411,10 +2416,11 @@ PNativeStruct::PNativeStruct(FName name)
 PNativeStruct *NewNativeStruct(FName name, PTypeBase *outer)
 {
 	size_t bucket;
+	if (outer == nullptr) outer = Namespaces.GlobalNamespace;
 	PType *stype = TypeTable.FindType(RUNTIME_CLASS(PNativeStruct), (intptr_t)outer, (intptr_t)name, &bucket);
 	if (stype == NULL)
 	{
-		stype = new PNativeStruct(name);
+		stype = new PNativeStruct(name, outer);
 		TypeTable.AddType(stype, RUNTIME_CLASS(PNativeStruct), (intptr_t)outer, (intptr_t)name, bucket);
 	}
 	return static_cast<PNativeStruct *>(stype);
@@ -2796,6 +2802,7 @@ void PClass::StaticInit ()
 	atterm (StaticShutdown);
 
 	StaticBootstrap();
+	Namespaces.GlobalNamespace = Namespaces.NewNamespace(0);
 
 	FAutoSegIterator probe(CRegHead, CRegTail);
 
@@ -2843,7 +2850,7 @@ void PClass::StaticShutdown ()
 
 	// Unless something went wrong, anything left here should be class and type objects only, which do not own any scripts.
 	TypeTable.Clear();
-	GlobalSymbols.ReleaseSymbols();
+	Namespaces.ReleaseSymbols();
 
 	for (i = 0; i < PClass::AllClasses.Size(); ++i)
 	{
@@ -3039,14 +3046,14 @@ void PClass::InsertIntoHash ()
 	size_t bucket;
 	PType *found;
 
-	found = TypeTable.FindType(RUNTIME_CLASS(PClass), (intptr_t)Outer, TypeName, &bucket);
+	found = TypeTable.FindType(RUNTIME_CLASS(PClass), 0, TypeName, &bucket);
 	if (found != NULL)
 	{ // This type has already been inserted
 		I_Error("Tried to register class '%s' more than once.\n", TypeName.GetChars());
 	}
 	else
 	{
-		TypeTable.AddType(this, RUNTIME_CLASS(PClass), (intptr_t)Outer, TypeName, bucket);
+		TypeTable.AddType(this, RUNTIME_CLASS(PClass), 0, TypeName, bucket);
 	}
 }
 
@@ -3084,8 +3091,7 @@ PClass *PClass::FindClass (FName zaname)
 	{
 		return NULL;
 	}
-	return static_cast<PClass *>(TypeTable.FindType(RUNTIME_CLASS(PClass),
-		/*FIXME:Outer*/0, zaname, NULL));
+	return static_cast<PClass *>(TypeTable.FindType(RUNTIME_CLASS(PClass), 0, zaname, NULL));
 }
 
 //==========================================================================
@@ -3376,7 +3382,7 @@ PClass *PClass::FindClassTentative(FName name)
 
 	Derive(type, name);
 	type->Size = TentativeClass;
-	TypeTable.AddType(type, RUNTIME_CLASS(PClass), (intptr_t)type->Outer, name, bucket);
+	TypeTable.AddType(type, RUNTIME_CLASS(PClass), 0, name, bucket);
 	return type;
 }
 
@@ -3835,3 +3841,60 @@ PSymbol *PSymbolTable::ReplaceSymbol(PSymbol *newsym)
 	Symbols.Insert(newsym->SymbolName, newsym);
 	return NULL;
 }
+
+IMPLEMENT_CLASS(PNamespace, false, true)
+
+IMPLEMENT_POINTERS_START(PNamespace)
+IMPLEMENT_POINTER(Parent)
+IMPLEMENT_POINTERS_END
+
+PNamespace::PNamespace(int filenum, PNamespace *parent)
+{
+	Parent = parent;
+	if (parent) Symbols.SetParentTable(&parent->Symbols);
+	FileNum = filenum;
+}
+
+size_t PNamespace::PropagateMark()
+{
+	GC::Mark(Parent);
+	return Symbols.MarkSymbols() + 1;
+}
+
+FNamespaceManager::FNamespaceManager()
+{
+	GlobalNamespace = nullptr;
+}
+
+PNamespace *FNamespaceManager::NewNamespace(int filenum)
+{
+	PNamespace *parent = nullptr;
+	// The parent will be the last namespace with this or a lower filenum.
+	// This ensures that DECORATE won't see the symbols of later files.
+	for (int i = AllNamespaces.Size() - 1; i >= 0; i--)
+	{
+		if (AllNamespaces[i]->FileNum <= filenum)
+		{
+			parent = AllNamespaces[i];
+			break;
+		}
+	}
+	auto newns = new PNamespace(filenum, parent);
+	AllNamespaces.Push(newns);
+	return newns;
+}
+
+size_t FNamespaceManager::MarkSymbols()
+{
+	for (auto ns : AllNamespaces)
+	{
+		GC::Mark(ns);
+	}
+	return AllNamespaces.Size();
+}
+
+void FNamespaceManager::ReleaseSymbols()
+{
+	GlobalNamespace = nullptr;
+	AllNamespaces.Clear();
+}
diff --git a/src/dobjtype.h b/src/dobjtype.h
index 7054095bd..61747134c 100644
--- a/src/dobjtype.h
+++ b/src/dobjtype.h
@@ -171,8 +171,6 @@ public:
 	PSymbolTreeNode() : PSymbol(NAME_None) {}
 };
 
-extern PSymbolTable		 GlobalSymbols;
-
 // Basic information shared by all types ------------------------------------
 
 // Only one copy of a type is ever instantiated at one time.
@@ -593,15 +591,15 @@ protected:
 
 // Compound types -----------------------------------------------------------
 
-class PEnum : public PNamedType
+class PEnum : public PInt
 {
-	DECLARE_CLASS(PEnum, PNamedType);
+	DECLARE_CLASS(PEnum, PInt);
 	HAS_OBJECT_POINTERS;
 public:
 	PEnum(FName name, PTypeBase *outer);
 
-	PType *ValueType;
-	TMap<FName, int> Values;
+	PTypeBase *Outer;
+	FName EnumName;
 protected:
 	PEnum();
 };
@@ -710,7 +708,7 @@ class PNativeStruct : public PStruct
 {
 	DECLARE_CLASS(PNativeStruct, PStruct);
 public:
-	PNativeStruct(FName name = NAME_None);
+	PNativeStruct(FName name = NAME_None, PTypeBase *outer = nullptr);
 };
 
 class PPrototype : public PCompoundType
@@ -986,31 +984,39 @@ public:
 	PSymbolConstString() {}
 };
 
-// Enumerations for serializing types in an archive -------------------------
+// Namespaces --------------------------------------------------
 
-enum ETypeVal : BYTE
+class PNamespace : public PTypeBase
 {
-	VAL_Int8,
-	VAL_UInt8,
-	VAL_Int16,
-	VAL_UInt16,
-	VAL_Int32,
-	VAL_UInt32,
-	VAL_Int64,
-	VAL_UInt64,
-	VAL_Zero,
-	VAL_One,
-	VAL_Float32,
-	VAL_Float64,
-	VAL_String,
-	VAL_Name,
-	VAL_Struct,
-	VAL_Array,
-	VAL_Object,
-	VAL_State,
-	VAL_Class,
+	DECLARE_CLASS(PNamespace, PTypeBase)
+	HAS_OBJECT_POINTERS;
+
+public:
+	PSymbolTable Symbols;
+	PNamespace *Parent;
+	int FileNum;	// This is for blocking DECORATE access to later files.
+
+	PNamespace() {}
+	PNamespace(int filenum, PNamespace *parent);
+	size_t PropagateMark();
 };
 
+struct FNamespaceManager
+{
+	PNamespace *GlobalNamespace;
+	TArray<PNamespace *> AllNamespaces;
+
+	FNamespaceManager();
+	PNamespace *NewNamespace(int filenum);
+	size_t MarkSymbols();
+	void ReleaseSymbols();
+};
+
+extern FNamespaceManager Namespaces;
+
+
+// Enumerations for serializing types in an archive -------------------------
+
 inline bool &DObject::BoolVar(FName field)
 {
 	return *(bool*)ScriptVar(field, TypeBool);
diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp
index 6065050a9..1d16b182f 100644
--- a/src/scripting/codegeneration/codegen.cpp
+++ b/src/scripting/codegeneration/codegen.cpp
@@ -95,14 +95,14 @@ static const FLOP FxFlops[] =
 //
 //==========================================================================
 
-FCompileContext::FCompileContext(PFunction *fnc, PPrototype *ret, bool fromdecorate, int stateindex, int statecount, int lump) 
-	: ReturnProto(ret), Function(fnc), Class(nullptr), FromDecorate(fromdecorate), StateIndex(stateindex), StateCount(statecount), Lump(lump)
+FCompileContext::FCompileContext(PNamespace *cg, PFunction *fnc, PPrototype *ret, bool fromdecorate, int stateindex, int statecount, int lump) 
+	: ReturnProto(ret), Function(fnc), Class(nullptr), FromDecorate(fromdecorate), StateIndex(stateindex), StateCount(statecount), Lump(lump), CurGlobals(cg)
 {
 	if (fnc != nullptr) Class = fnc->OwningClass;
 }
 
-FCompileContext::FCompileContext(PStruct *cls, bool fromdecorate) 
-	: ReturnProto(nullptr), Function(nullptr), Class(cls), FromDecorate(fromdecorate), StateIndex(-1), StateCount(0), Lump(-1)
+FCompileContext::FCompileContext(PNamespace *cg, PStruct *cls, bool fromdecorate) 
+	: ReturnProto(nullptr), Function(nullptr), Class(cls), FromDecorate(fromdecorate), StateIndex(-1), StateCount(0), Lump(-1), CurGlobals(cg)
 {
 }
 
@@ -119,7 +119,7 @@ PSymbol *FCompileContext::FindInSelfClass(FName identifier, PSymbolTable *&symt)
 }
 PSymbol *FCompileContext::FindGlobal(FName identifier)
 {
-	return GlobalSymbols.FindSymbol(identifier, true);
+	return CurGlobals->Symbols.FindSymbol(identifier, true);
 }
 
 void FCompileContext::CheckReturn(PPrototype *proto, FScriptPosition &pos)
@@ -185,16 +185,30 @@ FxLocalVariableDeclaration *FCompileContext::FindLocalVariable(FName name)
 	}
 }
 
-static PStruct *FindStructType(FName name)
+static PStruct *FindStructType(FName name, FCompileContext &ctx)
 {
-	PStruct *ccls = PClass::FindClass(name);
-	if (ccls == nullptr)
+	auto sym = ctx.Class->Symbols.FindSymbol(name, true);
+	if (sym == nullptr) sym = ctx.CurGlobals->Symbols.FindSymbol(name, true);
+	if (sym && sym->IsKindOf(RUNTIME_CLASS(PSymbolType)))
 	{
-		ccls = dyn_cast<PStruct>(TypeTable.FindType(RUNTIME_CLASS(PStruct), 0, (intptr_t)name, nullptr));
-		if (ccls == nullptr) ccls = dyn_cast<PStruct>(TypeTable.FindType(RUNTIME_CLASS(PNativeStruct), 0, (intptr_t)name, nullptr));
+		auto type = static_cast<PSymbolType*>(sym);
+		return dyn_cast<PStruct>(type->Type);
 	}
-	return ccls;
+	return nullptr;
 }
+
+// This is for resolving class identifiers which need to be context aware, unlike class names.
+static PClass *FindClassType(FName name, FCompileContext &ctx)
+{
+	auto sym = ctx.CurGlobals->Symbols.FindSymbol(name, true);
+	if (sym && sym->IsKindOf(RUNTIME_CLASS(PSymbolType)))
+	{
+		auto type = static_cast<PSymbolType*>(sym);
+		return dyn_cast<PClass>(type->Type);
+	}
+	return nullptr;
+}
+
 //==========================================================================
 //
 // ExpEmit
@@ -235,7 +249,7 @@ void ExpEmit::Reuse(VMFunctionBuilder *build)
 
 static PSymbol *FindBuiltinFunction(FName funcname, VMNativeFunction::NativeCallType func)
 {
-	PSymbol *sym = GlobalSymbols.FindSymbol(funcname, false);
+	PSymbol *sym = Namespaces.GlobalNamespace->Symbols.FindSymbol(funcname, false);
 	if (sym == nullptr)
 	{
 		PSymbolVMFunction *symfunc = new PSymbolVMFunction(funcname);
@@ -243,7 +257,7 @@ static PSymbol *FindBuiltinFunction(FName funcname, VMNativeFunction::NativeCall
 		calldec->PrintableName = funcname.GetChars();
 		symfunc->Function = calldec;
 		sym = symfunc;
-		GlobalSymbols.AddSymbol(sym);
+		Namespaces.GlobalNamespace->Symbols.AddSymbol(sym);
 	}
 	return sym;
 }
@@ -5918,7 +5932,7 @@ FxExpression *FxMemberIdentifier::Resolve(FCompileContext& ctx)
 	{
 		// If the left side is a class name for a static member function call it needs to be resolved manually 
 		// because the resulting value type would cause problems in nearly every other place where identifiers are being used.
-		ccls = FindStructType(static_cast<FxIdentifier *>(Object)->Identifier);
+		ccls = FindStructType(static_cast<FxIdentifier *>(Object)->Identifier, ctx);
 		if (ccls != nullptr) static_cast<FxIdentifier *>(Object)->noglobal = true;
 	}
 
@@ -7256,7 +7270,7 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
 		return x->Resolve(ctx);
 	}
 
-	PClass *cls = PClass::FindClass(MethodName);
+	PClass *cls = FindClassType(MethodName, ctx);
 	if (cls != nullptr && cls->bExported)
 	{
 		if (CheckArgSize(MethodName, ArgList, 1, 1, ScriptPosition))
@@ -7487,9 +7501,11 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
 
 	if (Self->ExprType == EFX_Identifier)
 	{
+		auto id = static_cast<FxIdentifier *>(Self)->Identifier;
 		// If the left side is a class name for a static member function call it needs to be resolved manually 
 		// because the resulting value type would cause problems in nearly every other place where identifiers are being used.
-		ccls = FindStructType(static_cast<FxIdentifier *>(Self)->Identifier);
+		if (id == NAME_String) ccls = TypeStringStruct;
+		else ccls = FindStructType(id, ctx);
 		if (ccls != nullptr) static_cast<FxIdentifier *>(Self)->noglobal = true;
 	}
 
@@ -7500,8 +7516,6 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
 		if (ccls != nullptr)
 		{
 			// [ZZ] substitute ccls for String internal type.
-			if (ccls->TypeName == NAME_String)
-				ccls = TypeStringStruct;
 			if (!ccls->IsKindOf(RUNTIME_CLASS(PClass)) || static_cast<PClass *>(ccls)->bExported)
 			{
 				cls = ccls;
@@ -9977,7 +9991,7 @@ FxExpression *FxClassTypeCast::Resolve(FCompileContext &ctx)
 
 		if (clsname != NAME_None)
 		{
-			cls = PClass::FindClass(clsname);
+			cls = FindClassType(clsname, ctx);
 			if (cls == nullptr)
 			{
 				/* lax */
diff --git a/src/scripting/codegeneration/codegen.h b/src/scripting/codegeneration/codegen.h
index 3dac463eb..7c18589c5 100644
--- a/src/scripting/codegeneration/codegen.h
+++ b/src/scripting/codegeneration/codegen.h
@@ -84,9 +84,10 @@ struct FCompileContext
 	int Lump;
 	bool Unsafe = false;
 	TDeletingArray<FxLocalVariableDeclaration *> FunctionArgs;
+	PNamespace *CurGlobals;
 
-	FCompileContext(PFunction *func, PPrototype *ret, bool fromdecorate, int stateindex, int statecount, int lump);
-	FCompileContext(PStruct *cls, bool fromdecorate);	// only to be used to resolve constants!
+	FCompileContext(PNamespace *spc, PFunction *func, PPrototype *ret, bool fromdecorate, int stateindex, int statecount, int lump);
+	FCompileContext(PNamespace *spc, PStruct *cls, bool fromdecorate);	// only to be used to resolve constants!
 
 	PSymbol *FindInClass(FName identifier, PSymbolTable *&symt);
 	PSymbol *FindInSelfClass(FName identifier, PSymbolTable *&symt);
diff --git a/src/scripting/decorate/olddecorations.cpp b/src/scripting/decorate/olddecorations.cpp
index 077a75ad6..1eeed5796 100644
--- a/src/scripting/decorate/olddecorations.cpp
+++ b/src/scripting/decorate/olddecorations.cpp
@@ -100,7 +100,7 @@ static const char *RenderStyles[] =
 //==========================================================================
 PClassActor *DecoDerivedClass(const FScriptPosition &sc, PClassActor *parent, FName typeName);
 
-void ParseOldDecoration(FScanner &sc, EDefinitionType def)
+void ParseOldDecoration(FScanner &sc, EDefinitionType def, PNamespace *ns)
 {
 	Baggage bag;
 	TArray<FState> StateArray;
@@ -116,6 +116,7 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def)
 	typeName = FName(sc.String);
 	type = DecoDerivedClass(FScriptPosition(sc), parent, typeName);
 	ResetBaggage(&bag, parent);
+	bag.Namespace = ns;
 	bag.Info = type;
 	bag.fromDecorate = true;
 #ifdef _DEBUG
diff --git a/src/scripting/decorate/thingdef_exp.cpp b/src/scripting/decorate/thingdef_exp.cpp
index a0ab09510..2b0866dd8 100644
--- a/src/scripting/decorate/thingdef_exp.cpp
+++ b/src/scripting/decorate/thingdef_exp.cpp
@@ -82,13 +82,13 @@ static FxExpression *ParseExpressionB (FScanner &sc, PClassActor *cls);
 static FxExpression *ParseExpressionA (FScanner &sc, PClassActor *cls);
 static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls);
 
-FxExpression *ParseExpression (FScanner &sc, PClassActor *cls, bool mustresolve)
+FxExpression *ParseExpression (FScanner &sc, PClassActor *cls, PNamespace *spc)
 {
 	FxExpression *data = ParseExpressionM (sc, cls);
 
-	if (mustresolve)
+	if (spc)
 	{
-		FCompileContext ctx(cls, true);
+		FCompileContext ctx(spc, cls, true);
 		data = data->Resolve(ctx);
 	}
 
diff --git a/src/scripting/decorate/thingdef_parse.cpp b/src/scripting/decorate/thingdef_parse.cpp
index 837a03a32..81ab765f5 100644
--- a/src/scripting/decorate/thingdef_parse.cpp
+++ b/src/scripting/decorate/thingdef_parse.cpp
@@ -56,7 +56,7 @@
 #include "v_text.h"
 #include "m_argv.h"
 
-void ParseOldDecoration(FScanner &sc, EDefinitionType def);
+void ParseOldDecoration(FScanner &sc, EDefinitionType def, PNamespace *ns);
 EXTERN_CVAR(Bool, strictdecorate);
 
 
@@ -101,12 +101,11 @@ PClassActor *DecoDerivedClass(const FScriptPosition &sc, PClassActor *parent, FN
 //
 // ParseParameter
 //
-// Parses a parameter - either a default in a function declaration 
-// or an argument in a function call.
+// Parses an argument in a function call.
 //
 //==========================================================================
 
-FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, PType *type, bool constant)
+FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, PType *type)
 {
 	FxExpression *x = NULL;
 	int v;
@@ -118,12 +117,7 @@ FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, PType *type, bool c
 	}
 	else if (type == TypeBool || type == TypeSInt32 || type == TypeFloat64)
 	{
-		x = ParseExpression (sc, cls, constant);
-		if (constant && !x->isConstant())
-		{
-			sc.ScriptMessage("Default parameter must be constant.");
-			FScriptPosition::ErrorCounter++;
-		}
+		x = ParseExpression (sc, cls, nullptr);
 		// Do automatic coercion between bools, ints and floats.
 		if (type == TypeBool)
 		{
@@ -193,11 +187,10 @@ FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, PType *type, bool c
 				x = new FxMultiNameState(sc.String, sc);
 			}
 		}
-		else if (!constant)
+		else
 		{
 			x = new FxRuntimeStateIndex(ParseExpression(sc, cls));
 		}
-		else sc.MustGetToken(TK_StringConst); // This is for the error.
 	}
 	else if (type->GetClass() == RUNTIME_CLASS(PClassPointer))
 	{	// Actor name
@@ -222,7 +215,7 @@ FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, PType *type, bool c
 //
 //==========================================================================
 
-static void ParseConstant (FScanner &sc, PSymbolTable *symt, PClassActor *cls)
+static void ParseConstant (FScanner &sc, PSymbolTable *symt, PClassActor *cls, PNamespace *ns)
 {
 	// Read the type and make sure it's int or float.
 	if (sc.CheckToken(TK_Int) || sc.CheckToken(TK_Float))
@@ -231,7 +224,7 @@ static void ParseConstant (FScanner &sc, PSymbolTable *symt, PClassActor *cls)
 		sc.MustGetToken(TK_Identifier);
 		FName symname = sc.String;
 		sc.MustGetToken('=');
-		FxExpression *expr = ParseExpression (sc, cls, true);
+		FxExpression *expr = ParseExpression (sc, cls, ns);
 		sc.MustGetToken(';');
 
 		if (expr == nullptr)
@@ -283,7 +276,7 @@ static void ParseConstant (FScanner &sc, PSymbolTable *symt, PClassActor *cls)
 //
 //==========================================================================
 
-static void ParseEnum (FScanner &sc, PSymbolTable *symt, PClassActor *cls)
+static void ParseEnum (FScanner &sc, PSymbolTable *symt, PClassActor *cls, PNamespace *ns)
 {
 	int currvalue = 0;
 
@@ -294,7 +287,7 @@ static void ParseEnum (FScanner &sc, PSymbolTable *symt, PClassActor *cls)
 		FName symname = sc.String;
 		if (sc.CheckToken('='))
 		{
-			FxExpression *expr = ParseExpression (sc, cls, true);
+			FxExpression *expr = ParseExpression (sc, cls, ns);
 			if (expr != nullptr)
 			{
 				if (!expr->isConstant())
@@ -339,7 +332,7 @@ static void ParseEnum (FScanner &sc, PSymbolTable *symt, PClassActor *cls)
 //
 //==========================================================================
 
-static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClassActor *cls)
+static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClassActor *cls, PNamespace *ns)
 {
 	PType *type;
 	int maxelems = 1;
@@ -381,7 +374,7 @@ static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClassActor *cl
 
 	if (sc.CheckToken('['))
 	{
-		FxExpression *expr = ParseExpression(sc, cls, true);
+		FxExpression *expr = ParseExpression(sc, cls, ns);
 		if (expr == nullptr)
 		{
 			sc.ScriptMessage("Error while resolving array size");
@@ -1112,11 +1105,12 @@ static PClassActor *ParseActorHeader(FScanner &sc, Baggage *bag)
 // Reads an actor definition
 //
 //==========================================================================
-static void ParseActor(FScanner &sc)
+static void ParseActor(FScanner &sc, PNamespace *ns)
 {
 	PClassActor *info = NULL;
 	Baggage bag;
 
+	bag.Namespace = ns;
 	bag.fromDecorate = true;
 	info = ParseActorHeader(sc, &bag);
 	sc.MustGetToken('{');
@@ -1125,15 +1119,15 @@ static void ParseActor(FScanner &sc)
 		switch (sc.TokenType)
 		{
 		case TK_Const:
-			ParseConstant (sc, &info->Symbols, info);
+			ParseConstant (sc, &info->Symbols, info, ns);
 			break;
 
 		case TK_Enum:
-			ParseEnum (sc, &info->Symbols, info);
+			ParseEnum (sc, &info->Symbols, info, ns);
 			break;
 
 		case TK_Var:
-			ParseUserVariable (sc, &info->Symbols, info);
+			ParseUserVariable (sc, &info->Symbols, info, ns);
 			break;
 
 		case TK_Identifier:
@@ -1225,6 +1219,7 @@ static void ParseDamageDefinition(FScanner &sc)
 
 void ParseDecorate (FScanner &sc)
 {
+	auto ns = Namespaces.NewNamespace(sc.LumpNum);
 	// Get actor class name.
 	for(;;)
 	{
@@ -1255,11 +1250,11 @@ void ParseDecorate (FScanner &sc)
 		}
 
 		case TK_Const:
-			ParseConstant (sc, &GlobalSymbols, NULL);
+			ParseConstant (sc, &ns->Symbols, NULL, ns);
 			break;
 
 		case TK_Enum:
-			ParseEnum (sc, &GlobalSymbols, NULL);
+			ParseEnum (sc, &ns->Symbols, NULL, ns);
 			break;
 
 		case ';':
@@ -1275,22 +1270,22 @@ void ParseDecorate (FScanner &sc)
 			// so let's do a special case for this.
 			if (sc.Compare("ACTOR"))
 			{
-				ParseActor (sc);
+				ParseActor (sc, ns);
 				break;
 			}
 			else if (sc.Compare("PICKUP"))
 			{
-				ParseOldDecoration (sc, DEF_Pickup);
+				ParseOldDecoration (sc, DEF_Pickup, ns);
 				break;
 			}
 			else if (sc.Compare("BREAKABLE"))
 			{
-				ParseOldDecoration (sc, DEF_BreakableDecoration);
+				ParseOldDecoration (sc, DEF_BreakableDecoration, ns);
 				break;
 			}
 			else if (sc.Compare("PROJECTILE"))
 			{
-				ParseOldDecoration (sc, DEF_Projectile);
+				ParseOldDecoration (sc, DEF_Projectile, ns);
 				break;
 			}
 			else if (sc.Compare("DAMAGETYPE"))
@@ -1300,7 +1295,7 @@ void ParseDecorate (FScanner &sc)
 			}
 		default:
 			sc.RestorePos(pos);
-			ParseOldDecoration(sc, DEF_Decoration);
+			ParseOldDecoration(sc, DEF_Decoration, ns);
 			break;
 		}
 	}
diff --git a/src/scripting/decorate/thingdef_states.cpp b/src/scripting/decorate/thingdef_states.cpp
index 671481de1..8e08fc038 100644
--- a/src/scripting/decorate/thingdef_states.cpp
+++ b/src/scripting/decorate/thingdef_states.cpp
@@ -343,7 +343,7 @@ endofstate:
 			if (ScriptCode != nullptr)
 			{
 				auto funcsym = CreateAnonymousFunction(actor, nullptr, state.UseFlags);
-				state.ActionFunc = FunctionBuildList.AddFunction(funcsym, ScriptCode, FStringf("%s.StateFunction.%d", actor->TypeName.GetChars(), bag.statedef.GetStateCount()), true, bag.statedef.GetStateCount(), int(statestring.Len()), sc.LumpNum);
+				state.ActionFunc = FunctionBuildList.AddFunction(bag.Namespace, funcsym, ScriptCode, FStringf("%s.StateFunction.%d", actor->TypeName.GetChars(), bag.statedef.GetStateCount()), true, bag.statedef.GetStateCount(), int(statestring.Len()), sc.LumpNum);
 			}
 			int count = bag.statedef.AddStates(&state, statestring, scp);
 			if (count < 0)
@@ -671,7 +671,7 @@ void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray<FxExpression
 		else
 		{
 			// Use the generic parameter parser for everything else
-			x = ParseParameter(sc, cls, params[pnum], false);
+			x = ParseParameter(sc, cls, params[pnum]);
 		}
 		out_params.Push(x);
 		pnum++;
diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp
index 9edb705bf..6776e70e8 100644
--- a/src/scripting/thingdef.cpp
+++ b/src/scripting/thingdef.cpp
@@ -216,7 +216,7 @@ PFunction *FindClassMemberFunction(PStruct *selfcls, PStruct *funccls, FName nam
 //
 //==========================================================================
 
-void CreateDamageFunction(PClassActor *info, AActor *defaults, FxExpression *id, bool fromDecorate, int lumpnum)
+void CreateDamageFunction(PNamespace *OutNamespace, PClassActor *info, AActor *defaults, FxExpression *id, bool fromDecorate, int lumpnum)
 {
 	if (id == nullptr)
 	{
@@ -226,7 +226,7 @@ void CreateDamageFunction(PClassActor *info, AActor *defaults, FxExpression *id,
 	{
 		auto dmg = new FxReturnStatement(new FxIntCast(id, true), id->ScriptPosition);
 		auto funcsym = CreateAnonymousFunction(info, TypeSInt32, 0);
-		defaults->DamageFunc = FunctionBuildList.AddFunction(funcsym, dmg, FStringf("%s.DamageFunction", info->TypeName.GetChars()), fromDecorate, -1, 0, lumpnum);
+		defaults->DamageFunc = FunctionBuildList.AddFunction(OutNamespace, funcsym, dmg, FStringf("%s.DamageFunction", info->TypeName.GetChars()), fromDecorate, -1, 0, lumpnum);
 	}
 }
 
diff --git a/src/scripting/thingdef.h b/src/scripting/thingdef.h
index 76a209722..37c247cbe 100644
--- a/src/scripting/thingdef.h
+++ b/src/scripting/thingdef.h
@@ -117,6 +117,7 @@ struct Baggage
 #ifdef _DEBUG
 	FString ClassName;	// This is here so that during debugging the class name can be seen
 #endif
+	PNamespace *Namespace;
 	PClassActor *Info;
 	bool DropItemSet;
 	bool StateSet;
@@ -132,7 +133,7 @@ struct Baggage
 
 inline void ResetBaggage (Baggage *bag, PClassActor *stateclass)
 {
-	bag->DropItemList = NULL;
+	bag->DropItemList = nullptr;
 	bag->DropItemSet = false;
 	bag->CurrentState = 0;
 	bag->fromDecorate = true;
@@ -150,7 +151,7 @@ AFuncDesc *FindFunction(PStruct *cls, const char * string);
 FieldDesc *FindField(PStruct *cls, const char * string);
 
 
-FxExpression *ParseExpression(FScanner &sc, PClassActor *cls, bool mustresolve = false);
+FxExpression *ParseExpression(FScanner &sc, PClassActor *cls, PNamespace *resolvenspc = nullptr);
 void ParseStates(FScanner &sc, PClassActor *actor, AActor *defaults, Baggage &bag);
 void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray<FxExpression *> &out_params,
 	PFunction *afd, FString statestring, FStateDefinitions *statedef);
@@ -160,7 +161,7 @@ FName CheckCastKludges(FName in);
 void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FName> *argnames, PStruct *cls, DWORD funcflags, int useflags);
 PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, int flags);
 PFunction *FindClassMemberFunction(PStruct *cls, PStruct *funccls, FName name, FScriptPosition &sc, bool *error);
-void CreateDamageFunction(PClassActor *info, AActor *defaults, FxExpression *id, bool fromDecorate, int lumpnum);
+void CreateDamageFunction(PNamespace *ns, PClassActor *info, AActor *defaults, FxExpression *id, bool fromDecorate, int lumpnum);
 
 //==========================================================================
 //
@@ -169,7 +170,7 @@ void CreateDamageFunction(PClassActor *info, AActor *defaults, FxExpression *id,
 //==========================================================================
 
 void HandleActorFlag(FScanner &sc, Baggage &bag, const char *part1, const char *part2, int mod);
-FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, PType *type, bool constant);
+FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, PType *type);
 
 
 enum 
diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp
index 1dd8d6263..9fa33314e 100644
--- a/src/scripting/thingdef_data.cpp
+++ b/src/scripting/thingdef_data.cpp
@@ -762,16 +762,16 @@ void InitThingdef()
 
 	// expose the global validcount variable.
 	PField *vcf = new PField("validcount", TypeSInt32, VARF_Native | VARF_Static, (intptr_t)&validcount);
-	GlobalSymbols.AddSymbol(vcf);
+	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);
-	GlobalSymbols.AddSymbol(multif);
+	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);
-	GlobalSymbols.AddSymbol(levelf);
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(levelf);
 
 	// Add the game data arrays to LevelLocals.
 	lstruct->AddNativeField("sectors", NewPointer(NewResizableArray(sectorstruct), false), myoffsetof(FLevelLocals, sectors), VARF_Native);
@@ -783,17 +783,17 @@ void InitThingdef()
 
 	auto aact = NewPointer(NewResizableArray(NewClassPointer(RUNTIME_CLASS(AActor))), true);
 	PField *aacf = new PField("AllActorClasses", aact, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&PClassActor::AllActorClasses);
-	GlobalSymbols.AddSymbol(aacf);
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(aacf);
 
 	// 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);
-	GlobalSymbols.AddSymbol(dehf);
+	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);
-	GlobalSymbols.AddSymbol(gi);
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(gi);
 
 	// set up a variable for the global players array.
 	PStruct *pstruct = NewNativeStruct("PlayerInfo", nullptr);
@@ -801,33 +801,33 @@ void InitThingdef()
 	pstruct->Align = alignof(player_t);
 	PArray *parray = NewArray(pstruct, MAXPLAYERS);
 	PField *playerf = new PField("players", parray, VARF_Native | VARF_Static, (intptr_t)&players);
-	GlobalSymbols.AddSymbol(playerf);
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(playerf);
 
 	pstruct->AddNativeField("weapons", NewNativeStruct("WeaponSlots", nullptr), myoffsetof(player_t, weapons), VARF_Native);
 
 
 	parray = NewArray(TypeBool, MAXPLAYERS);
 	playerf = new PField("playeringame", parray, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&playeringame);
-	GlobalSymbols.AddSymbol(playerf);
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(playerf);
 
 	playerf = new PField("gameaction", TypeUInt8, VARF_Native | VARF_Static, (intptr_t)&gameaction);
-	GlobalSymbols.AddSymbol(playerf);
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(playerf);
 
 	playerf = new PField("skyflatnum", TypeTextureID, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&skyflatnum);
-	GlobalSymbols.AddSymbol(playerf);
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(playerf);
 
 	playerf = new PField("globalfreeze", TypeUInt8, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&bglobal.freeze);
-	GlobalSymbols.AddSymbol(playerf);
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(playerf);
 
 	playerf = new PField("consoleplayer", TypeSInt32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&consoleplayer);
-	GlobalSymbols.AddSymbol(playerf);
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(playerf);
 
 	// 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!!!
 	static AWeapon *wpnochg = WP_NOCHANGE;
 	playerf = new PField("WP_NOCHANGE", NewPointer(RUNTIME_CLASS(AWeapon), false), VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&wpnochg);
-	GlobalSymbols.AddSymbol(playerf);
+	Namespaces.GlobalNamespace->Symbols.AddSymbol(playerf);
 
 	// synthesize a symbol for each flag from the flag name tables to avoid redundant declaration of them.
 	for (auto &fl : FlagLists)
@@ -936,7 +936,7 @@ DEFINE_ACTION_FUNCTION(FStringTable, Localize)
 	ACTION_RETURN_STRING(GStrings(&label[1]));
 }
 
-DEFINE_ACTION_FUNCTION(FString, Replace)
+DEFINE_ACTION_FUNCTION(FStringStruct, Replace)
 {
 	PARAM_SELF_STRUCT_PROLOGUE(FString);
 	PARAM_STRING(s1);
@@ -1103,14 +1103,14 @@ static FString FStringFormat(VM_ARGS)
 	return output;
 }
 
-DEFINE_ACTION_FUNCTION(FString, Format)
+DEFINE_ACTION_FUNCTION(FStringStruct, Format)
 {
 	PARAM_PROLOGUE;
 	FString s = FStringFormat(param, defaultparam, numparam, ret, numret);
 	ACTION_RETURN_STRING(s);
 }
 
-DEFINE_ACTION_FUNCTION(FString, AppendFormat)
+DEFINE_ACTION_FUNCTION(FStringStruct, AppendFormat)
 {
 	PARAM_SELF_STRUCT_PROLOGUE(FString);
 	// first parameter is the self pointer
diff --git a/src/scripting/thingdef_properties.cpp b/src/scripting/thingdef_properties.cpp
index 807bd2a93..682300147 100644
--- a/src/scripting/thingdef_properties.cpp
+++ b/src/scripting/thingdef_properties.cpp
@@ -689,7 +689,7 @@ DEFINE_PROPERTY(damage, X, Actor)
 
 	defaults->DamageVal = dmgval;
 	// Only DECORATE can get here with a valid expression.
-	CreateDamageFunction(bag.Info, defaults, id, true, bag.Lumpnum);
+	CreateDamageFunction(bag.Namespace, bag.Info, defaults, id, true, bag.Lumpnum);
 }
 
 //==========================================================================
diff --git a/src/scripting/vm/vmbuilder.cpp b/src/scripting/vm/vmbuilder.cpp
index 4886dba8c..46553ab72 100644
--- a/src/scripting/vm/vmbuilder.cpp
+++ b/src/scripting/vm/vmbuilder.cpp
@@ -802,7 +802,7 @@ void VMFunctionBuilder::BackpatchListToHere(TArray<size_t> &locs)
 //==========================================================================
 FFunctionBuildList FunctionBuildList;
 
-VMFunction *FFunctionBuildList::AddFunction(PFunction *functype, FxExpression *code, const FString &name, bool fromdecorate, int stateindex, int statecount, int lumpnum)
+VMFunction *FFunctionBuildList::AddFunction(PNamespace *gnspc, PFunction *functype, FxExpression *code, const FString &name, bool fromdecorate, int stateindex, int statecount, int lumpnum)
 {
 	auto func = code->GetDirectFunction();
 	if (func != nullptr)
@@ -814,6 +814,8 @@ VMFunction *FFunctionBuildList::AddFunction(PFunction *functype, FxExpression *c
 	//Printf("Adding %s\n", name.GetChars());
 
 	Item it;
+	assert(gnspc != nullptr);
+	it.CurGlobals = gnspc;
 	it.Func = functype;
 	it.Code = code;
 	it.PrintableName = name;
@@ -853,7 +855,7 @@ void FFunctionBuildList::Build()
 		assert(item.Code != NULL);
 
 		// We don't know the return type in advance for anonymous functions.
-		FCompileContext ctx(item.Func, item.Func->SymbolName == NAME_None ? nullptr : item.Func->Variants[0].Proto, item.FromDecorate, item.StateIndex, item.StateCount, item.Lump);
+		FCompileContext ctx(item.CurGlobals, item.Func, item.Func->SymbolName == NAME_None ? nullptr : item.Func->Variants[0].Proto, item.FromDecorate, item.StateIndex, item.StateCount, item.Lump);
 
 		// Allocate registers for the function's arguments and create local variable nodes before starting to resolve it.
 		VMFunctionBuilder buildit(item.Func->GetImplicitArgs());
diff --git a/src/scripting/vm/vmbuilder.h b/src/scripting/vm/vmbuilder.h
index b7746f1ce..300cfd843 100644
--- a/src/scripting/vm/vmbuilder.h
+++ b/src/scripting/vm/vmbuilder.h
@@ -141,6 +141,7 @@ class FFunctionBuildList
 		FxExpression *Code = nullptr;
 		PPrototype *Proto = nullptr;
 		VMScriptFunction *Function = nullptr;
+		PNamespace *CurGlobals = nullptr;
 		FString PrintableName;
 		int StateIndex;
 		int StateCount;
@@ -151,7 +152,7 @@ class FFunctionBuildList
 	TArray<Item> mItems;
 
 public:
-	VMFunction *AddFunction(PFunction *func, FxExpression *code, const FString &name, bool fromdecorate, int currentstate, int statecnt, int lumpnum);
+	VMFunction *AddFunction(PNamespace *curglobals, PFunction *func, FxExpression *code, const FString &name, bool fromdecorate, int currentstate, int statecnt, int lumpnum);
 	void Build();
 };
 
diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp
index 714b56714..57bfbd0ec 100644
--- a/src/scripting/zscript/zcc_compile.cpp
+++ b/src/scripting/zscript/zcc_compile.cpp
@@ -74,7 +74,7 @@ static FString GetStringConst(FxExpression *ex, FCompileContext &ctx)
 
 int ZCCCompiler::IntConstFromNode(ZCC_TreeNode *node, PStruct *cls)
 {
-	FCompileContext ctx(cls, false);
+	FCompileContext ctx(OutNamespace, cls, false);
 	FxExpression *ex = new FxIntCast(ConvertNode(node), false);
 	ex = ex->Resolve(ctx);
 	if (ex == nullptr) return 0;
@@ -88,7 +88,7 @@ int ZCCCompiler::IntConstFromNode(ZCC_TreeNode *node, PStruct *cls)
 
 FString ZCCCompiler::StringConstFromNode(ZCC_TreeNode *node, PStruct *cls)
 {
-	FCompileContext ctx(cls, false);
+	FCompileContext ctx(OutNamespace, cls, false);
 	FxExpression *ex = new FxStringCast(ConvertNode(node));
 	ex = ex->Resolve(ctx);
 	if (ex == nullptr) return "";
@@ -154,7 +154,12 @@ void ZCCCompiler::ProcessClass(ZCC_Class *cnode, PSymbolTreeNode *treenode)
 					cls->Enums.Push(enumType);
 					break;
 
-				case AST_Struct:		
+				case AST_Struct:	
+					if (static_cast<ZCC_Struct *>(node)->Flags & VARF_Native)
+					{
+						Error(node, "Cannot define native structs inside classes");
+						static_cast<ZCC_Struct *>(node)->Flags &= ~VARF_Native;
+					}
 					ProcessStruct(static_cast<ZCC_Struct *>(node), childnode, cls->cls);	
 					break;
 
@@ -271,8 +276,8 @@ void ZCCCompiler::ProcessStruct(ZCC_Struct *cnode, PSymbolTreeNode *treenode, ZC
 //
 //==========================================================================
 
-ZCCCompiler::ZCCCompiler(ZCC_AST &ast, DObject *_outer, PSymbolTable &_symbols, PSymbolTable &_outsymbols, int lumpnum)
-	: Outer(_outer), GlobalTreeNodes(&_symbols), OutputSymbols(&_outsymbols), AST(ast), Lump(lumpnum)
+ZCCCompiler::ZCCCompiler(ZCC_AST &ast, DObject *_outer, PSymbolTable &_symbols, PNamespace *_outnamespc, int lumpnum)
+	: Outer(_outer), GlobalTreeNodes(&_symbols), OutNamespace(_outnamespc), AST(ast), Lump(lumpnum)
 {
 	FScriptPosition::ResetErrorCounter();
 	// Group top-level nodes by type
@@ -303,8 +308,8 @@ ZCCCompiler::ZCCCompiler(ZCC_AST &ast, DObject *_outer, PSymbolTable &_symbols,
 					{
 					case AST_Enum:
 						zenumType = static_cast<ZCC_Enum *>(node);
-						enumType = NewEnum(zenumType->NodeName, nullptr);
-						GlobalSymbols.AddSymbol(new PSymbolType(zenumType->NodeName, enumType));
+						enumType = NewEnum(zenumType->NodeName, OutNamespace);
+						OutNamespace->Symbols.AddSymbol(new PSymbolType(zenumType->NodeName, enumType));
 						break;
 
 					case AST_Class:
@@ -468,13 +473,31 @@ void ZCCCompiler::CreateStructTypes()
 {
 	for(auto s : Structs)
 	{
+		PTypeBase *outer;
+		PSymbolTable *syms;
+
 		s->Outer = s->OuterDef == nullptr? nullptr : s->OuterDef->CType();
-		if (s->strct->Flags & ZCC_Native)
-			s->strct->Type = NewNativeStruct(s->NodeName(), nullptr);
+		if (s->Outer)
+		{
+			outer = s->Outer;
+			syms = &s->Outer->Symbols;
+		}
 		else
-			s->strct->Type = NewStruct(s->NodeName(), s->Outer);
+		{
+			outer = OutNamespace;
+			syms = &OutNamespace->Symbols;
+		}
+
+		if (s->strct->Flags & ZCC_Native)
+		{
+			s->strct->Type = NewNativeStruct(s->NodeName(), outer);
+		}
+		else
+		{
+			s->strct->Type = NewStruct(s->NodeName(), outer);
+		}
 		s->strct->Symbol = new PSymbolType(s->NodeName(), s->Type());
-		GlobalSymbols.AddSymbol(s->strct->Symbol);
+		syms->AddSymbol(s->strct->Symbol);
 
 		for (auto e : s->Enums)
 		{
@@ -577,7 +600,7 @@ void ZCCCompiler::CreateClassTypes()
 				if (c->Type() == nullptr) c->cls->Type = parent->FindClassTentative(c->NodeName());
 				c->Type()->bExported = true;	// this class is accessible to script side type casts. (The reason for this flag is that types like PInt need to be skipped.)
 				c->cls->Symbol = new PSymbolType(c->NodeName(), c->Type());
-				GlobalSymbols.AddSymbol(c->cls->Symbol);
+				OutNamespace->Symbols.AddSymbol(c->cls->Symbol);
 				Classes.Push(c);
 				OrigClasses.Delete(i--);
 				donesomething = true;
@@ -601,7 +624,7 @@ void ZCCCompiler::CreateClassTypes()
 					// create a placeholder so that the compiler can continue looking for errors.
 					c->cls->Type = RUNTIME_CLASS(DObject)->FindClassTentative(c->NodeName());
 					c->cls->Symbol = new PSymbolType(c->NodeName(), c->Type());
-					GlobalSymbols.AddSymbol(c->cls->Symbol);
+					OutNamespace->Symbols.AddSymbol(c->cls->Symbol);
 					Classes.Push(c);
 					OrigClasses.Delete(i--);
 					donesomething = true;
@@ -617,7 +640,7 @@ void ZCCCompiler::CreateClassTypes()
 		Error(c->cls, "Class %s has circular inheritance", FName(c->NodeName()).GetChars());
 		c->cls->Type = RUNTIME_CLASS(DObject)->FindClassTentative(c->NodeName());
 		c->cls->Symbol = new PSymbolType(c->NodeName(), c->Type());
-		GlobalSymbols.AddSymbol(c->cls->Symbol);
+		OutNamespace->Symbols.AddSymbol(c->cls->Symbol);
 		Classes.Push(c);
 	}
 
@@ -678,7 +701,7 @@ void ZCCCompiler::CompileAllConstants()
 	// put all constants in one list to make resolving this easier.
 	TArray<ZCC_ConstantWork> constantwork;
 
-	CopyConstants(constantwork, Constants, nullptr, OutputSymbols);
+	CopyConstants(constantwork, Constants, nullptr, &OutNamespace->Symbols);
 	for (auto c : Classes)
 	{
 		CopyConstants(constantwork, c->Constants, c->Type(), &c->Type()->Symbols);
@@ -810,7 +833,7 @@ void ZCCCompiler::AddConstant(ZCC_ConstantWork &constant)
 
 bool ZCCCompiler::CompileConstant(ZCC_ConstantWork *work)
 {
-	FCompileContext ctx(work->cls, false);
+	FCompileContext ctx(OutNamespace, work->cls, false);
 	FxExpression *exp = ConvertNode(work->node->Value);
 	try
 	{
@@ -836,49 +859,6 @@ bool ZCCCompiler::CompileConstant(ZCC_ConstantWork *work)
 }
 
 
-//==========================================================================
-//
-// ZCCCompiler :: IdentifyIdentifier
-//
-// Returns a node that represents what the identifer stands for.
-//
-//==========================================================================
-
-ZCC_Expression *ZCCCompiler::IdentifyIdentifier(ZCC_ExprID *idnode, PSymbolTable *symt)
-{
-	// Check the symbol table for the identifier.
-	PSymbolTable *table;
-	PSymbol *sym = symt->FindSymbolInTable(idnode->Identifier, table);
-	// GlobalSymbols cannot be the parent of a class's symbol table so we have to look for global symbols explicitly.
-	if (sym == nullptr && symt != &GlobalSymbols) sym = GlobalSymbols.FindSymbolInTable(idnode->Identifier, table);
-	if (sym != nullptr)
-	{
-		ZCC_Expression *node = NodeFromSymbol(sym, idnode, table);
-		if (node != NULL)
-		{
-			return node;
-		}
-	}
-	else if (SimplifyingConstant)	// leave unknown identifiers alone when simplifying non-constants. It is impossible to know what they are here.
-	{
-		// Also handle line specials.
-		// To call this like a function this needs to be done differently, but for resolving constants this is ok.
-		int spec = P_FindLineSpecial(FName(idnode->Identifier).GetChars());
-		if (spec != 0)
-		{
-			ZCC_ExprConstant *val = static_cast<ZCC_ExprConstant *>(AST.InitNode(sizeof(*val), AST_ExprConstant, idnode));
-			val->Operation = PEX_ConstValue;
-			val->Type = TypeSInt32;
-			val->IntVal = spec;
-			return val;
-		}
-
-		Error(idnode, "Unknown identifier '%s'", FName(idnode->Identifier).GetChars());
-		idnode->ToErrorNode();
-	}
-	return idnode;
-}
-
 //==========================================================================
 //
 // ZCCCompiler :: NodeFromSymbol
@@ -1056,6 +1036,7 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
 	{
 		auto field = Fields[0];
 		FieldDesc *fd = nullptr;
+		FString str = FName(field->Names[0].Name).GetChars();
 
 		PType *fieldtype = DetermineType(type, field, field->Names->Name, field->Type, true, true);
 
@@ -1394,10 +1375,15 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n
 		}
 		else
 		{
+			// 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);
-			if (sym == nullptr) sym = GlobalSymbols.FindSymbol(ctype->Restriction->Id, false);
+			if (sym == nullptr) sym = OutNamespace->Symbols.FindSymbol(ctype->Restriction->Id, true);
 			if (sym == nullptr)
 			{
+				// A symbol with a given name cannot be reached from this definition point, so
+				// even if a class with the given name exists, it is not accessible.
 				Error(field, "%s: Unknown identifier", FName(ctype->Restriction->Id).GetChars());
 				return TypeError;
 			}
@@ -1434,10 +1420,9 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n
 PType *ZCCCompiler::ResolveUserType(ZCC_BasicType *type, PSymbolTable *symt)
 {
 	// Check the symbol table for the identifier.
-	PSymbolTable *table;
-	PSymbol *sym = symt->FindSymbolInTable(type->UserType->Id, table);
-	// GlobalSymbols cannot be the parent of a class's symbol table so we have to look for global symbols explicitly.
-	if (sym == nullptr && symt != &GlobalSymbols) sym = GlobalSymbols.FindSymbolInTable(type->UserType->Id, table);
+	PSymbol *sym = symt->FindSymbol(type->UserType->Id, true);
+	// We first look in the current class and its parents, and then in the current namespace and its parents.
+	if (sym == nullptr) sym = OutNamespace->Symbols.FindSymbol(type->UserType->Id, true);
 	if (sym != nullptr && sym->IsKindOf(RUNTIME_CLASS(PSymbolType)))
 	{
 		auto ptype = static_cast<PSymbolType *>(sym)->Type;
@@ -1477,7 +1462,7 @@ PType *ZCCCompiler::ResolveArraySize(PType *baseType, ZCC_Expression *arraysize,
 	} while (node != arraysize);
 
 
-	FCompileContext ctx(cls, false);
+	FCompileContext ctx(OutNamespace, cls, false);
 	for (auto node : indices)
 	{
 		// There is no float->int casting here.
@@ -1526,7 +1511,7 @@ void ZCCCompiler::DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *proper
 		const char * p = prop->params;
 		auto exp = property->Values;
 
-		FCompileContext ctx(bag.Info, false);
+		FCompileContext ctx(OutNamespace, bag.Info, false);
 		while (true)
 		{
 			FPropParam conv;
@@ -1693,7 +1678,7 @@ void ZCCCompiler::DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *prop
 	}
 
 	auto exp = property->Values;
-	FCompileContext ctx(bag.Info, false);
+	FCompileContext ctx(OutNamespace, bag.Info, false);
 	for (auto f : prop->Variables)
 	{
 		void *addr;
@@ -1775,7 +1760,7 @@ void ZCCCompiler::ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *pro
 		if (namenode->Id == NAME_DamageFunction)
 		{
 			auto x = ConvertNode(prop->Values);
-			CreateDamageFunction(cls, (AActor *)bag.Info->Defaults, x, false, Lump);
+			CreateDamageFunction(OutNamespace, cls, (AActor *)bag.Info->Defaults, x, false, Lump);
 			((AActor *)bag.Info->Defaults)->DamageVal = -1;
 			return;
 		}
@@ -1923,6 +1908,7 @@ void ZCCCompiler::InitDefaults()
 			#ifdef _DEBUG
 				bag.ClassName = c->Type()->TypeName;
 			#endif
+				bag.Namespace = OutNamespace;
 				bag.Info = ti;
 				bag.DropItemSet = false;
 				bag.StateSet = false;
@@ -2150,7 +2136,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
 						// It will also lose important type info about enums, once these get implemented
 						// The code generator can do this properly for us.
 						FxExpression *x = new FxTypeCast(ConvertNode(p->Default), type, false);
-						FCompileContext ctx(c->Type(), false);
+						FCompileContext ctx(OutNamespace, c->Type(), false);
 						x = x->Resolve(ctx);
 
 						if (x != nullptr)
@@ -2258,7 +2244,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
 				auto code = ConvertAST(c->Type(), f->Body);
 				if (code != nullptr)
 				{
-					FunctionBuildList.AddFunction(sym, code, FStringf("%s.%s", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()), false, -1, 0, Lump);
+					FunctionBuildList.AddFunction(OutNamespace, sym, code, FStringf("%s.%s", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()), false, -1, 0, Lump);
 				}
 			}
 		}
@@ -2527,7 +2513,7 @@ void ZCCCompiler::CompileStates()
 					{
 						state.sprite = GetSpriteIndex(sl->Sprite->GetChars());
 					}
-					FCompileContext ctx(c->Type(), false);
+					FCompileContext ctx(OutNamespace, c->Type(), false);
 					if (CheckRandom(sl->Duration))
 					{
 						auto func = static_cast<ZCC_ExprFuncCall *>(sl->Duration);
@@ -2581,7 +2567,7 @@ void ZCCCompiler::CompileStates()
 						if (code != nullptr)
 						{
 							auto funcsym = CreateAnonymousFunction(c->Type(), nullptr, state.UseFlags);
-							state.ActionFunc = FunctionBuildList.AddFunction(funcsym, code, FStringf("%s.StateFunction.%d", c->Type()->TypeName.GetChars(), statedef.GetStateCount()), false, statedef.GetStateCount(), (int)sl->Frames->Len(), Lump);
+							state.ActionFunc = FunctionBuildList.AddFunction(OutNamespace, funcsym, code, FStringf("%s.StateFunction.%d", c->Type()->TypeName.GetChars(), statedef.GetStateCount()), false, statedef.GetStateCount(), (int)sl->Frames->Len(), Lump);
 						}
 					}
 
diff --git a/src/scripting/zscript/zcc_compile.h b/src/scripting/zscript/zcc_compile.h
index 79dc3b135..a14443b7d 100644
--- a/src/scripting/zscript/zcc_compile.h
+++ b/src/scripting/zscript/zcc_compile.h
@@ -86,13 +86,13 @@ struct ZCC_ConstantWork
 class ZCCCompiler
 {
 public:
-	ZCCCompiler(ZCC_AST &tree, DObject *outer, PSymbolTable &symbols, PSymbolTable &outsymbols, int lumpnum);
+	ZCCCompiler(ZCC_AST &tree, DObject *outer, PSymbolTable &symbols, PNamespace *outnamespace, int lumpnum);
 	~ZCCCompiler();
 	int Compile();
 
 private:
 	int IntConstFromNode(ZCC_TreeNode *node, PStruct *cls);
-	FString ZCCCompiler::StringConstFromNode(ZCC_TreeNode *node, PStruct *cls);
+	FString StringConstFromNode(ZCC_TreeNode *node, PStruct *cls);
 	void ProcessClass(ZCC_Class *node, PSymbolTreeNode *tnode);
 	void ProcessStruct(ZCC_Struct *node, PSymbolTreeNode *tnode, ZCC_Class *outer);
 	void CreateStructTypes();
@@ -130,7 +130,6 @@ private:
 
 	PSymbolTreeNode *AddTreeNode(FName name, ZCC_TreeNode *node, PSymbolTable *treenodes, bool searchparents = false);
 
-	ZCC_Expression *IdentifyIdentifier(ZCC_ExprID *idnode, PSymbolTable *sym);
 	ZCC_Expression *NodeFromSymbol(PSymbol *sym, ZCC_Expression *source, PSymbolTable *table);
 	ZCC_ExprConstant *NodeFromSymbolConst(PSymbolConst *sym, ZCC_Expression *idnode);
 	ZCC_ExprTypeRef *NodeFromSymbolType(PSymbolType *sym, ZCC_Expression *idnode);
@@ -147,7 +146,7 @@ private:
 	DObject *Outer;
 	PStruct *ConvertClass;	// class type to be used when resoving symbols while converting an AST
 	PSymbolTable *GlobalTreeNodes;
-	PSymbolTable *OutputSymbols;
+	PNamespace *OutNamespace;
 	ZCC_AST &AST;
 	int Lump;
 };
diff --git a/src/scripting/zscript/zcc_parser.cpp b/src/scripting/zscript/zcc_parser.cpp
index 872ab2cc8..501a7b2ea 100644
--- a/src/scripting/zscript/zcc_parser.cpp
+++ b/src/scripting/zscript/zcc_parser.cpp
@@ -226,6 +226,8 @@ static void InitTokenMap()
 #undef TOKENDEF
 #undef TOKENDEF2
 
+//**--------------------------------------------------------------------------
+
 static void ParseSingleFile(const char *filename, int lump, void *parser, ZCCParseState &state)
 {
 	int tokentype;
@@ -311,6 +313,8 @@ parse_end:
 	state.sc = nullptr;
 }
 
+//**--------------------------------------------------------------------------
+
 static void DoParse(int lumpnum)
 {
 	FScanner sc;
@@ -395,7 +399,8 @@ static void DoParse(int lumpnum)
 	}
 
 	PSymbolTable symtable;
-	ZCCCompiler cc(state, NULL, symtable, GlobalSymbols, lumpnum);
+	auto newns = Wads.GetLumpFile(lumpnum) == 0 ? Namespaces.GlobalNamespace : Namespaces.NewNamespace(Wads.GetLumpFile(lumpnum));
+	ZCCCompiler cc(state, NULL, symtable, newns, lumpnum);
 	cc.Compile();
 
 	if (FScriptPosition::ErrorCounter > 0)
diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt
index eab046372..a1177289e 100644
--- a/wadsrc/static/zscript/base.txt
+++ b/wadsrc/static/zscript/base.txt
@@ -305,7 +305,7 @@ enum EPickStart
 }
 
 // Although String is a builtin type, this is a convenient way to attach methods to it.
-struct String native
+struct StringStruct native
 {
 	native void Replace(String pattern, String replacement);
 	native static vararg String Format(String fmt, ...);
diff --git a/wadsrc/static/zscript/shared/player_cheat.txt b/wadsrc/static/zscript/shared/player_cheat.txt
index 3dba4ac2d..2aaa01ba8 100644
--- a/wadsrc/static/zscript/shared/player_cheat.txt
+++ b/wadsrc/static/zscript/shared/player_cheat.txt
@@ -127,7 +127,7 @@ extend class PlayerPawn
 			{
 				let armoritem = BasicArmorPickup(Spawn("BasicArmorPickup"));
 				armoritem.SaveAmount = 100*deh.BlueAC;
-				armoritem.SavePercent = gameinfo.Armor2Percent > 0? gameinfo.Armor2Percent : 0.5;
+				armoritem.SavePercent = gameinfo.Armor2Percent > 0 ? gameinfo.Armor2Percent * 100 : 50;
 				if (!armoritem.CallTryPickup (self))
 				{
 					armoritem.Destroy ();