diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ffeba6d49..b5c2cc949 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1280,7 +1280,6 @@ set (PCH_SOURCES
 	scripting/vm/vmframe.cpp
 	scripting/zscript/ast.cpp
 	scripting/zscript/zcc_compile.cpp
-	scripting/zscript/zcc_expr.cpp
 	scripting/zscript/zcc_parser.cpp
 	sfmt/SFMT.cpp
 )
diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp
index 2d41a57e3..c3ab87e11 100644
--- a/src/dobjtype.cpp
+++ b/src/dobjtype.cpp
@@ -240,159 +240,6 @@ size_t PType::PropagateMark()
 	return marked + Super::PropagateMark();
 }
 
-//==========================================================================
-//
-// PType :: AddConversion
-//
-//==========================================================================
-
-bool PType::AddConversion(PType *target, void (*convertconst)(ZCC_ExprConstant *, class FSharedStringArena &))
-{
-	// Make sure a conversion hasn't already been registered
-	for (unsigned i = 0; i < Conversions.Size(); ++i)
-	{
-		if (Conversions[i].TargetType == target)
-			return false;
-	}
-	Conversions.Push(Conversion(target, convertconst));
-	return true;
-}
-
-//==========================================================================
-//
-// PType :: FindConversion
-//
-// Returns <0 if there is no path to target. Otherwise, returns the distance
-// to target and fills slots (if non-NULL) with the necessary conversions
-// to get there. A result of 0 means this is the target.
-//
-//==========================================================================
-
-int PType::FindConversion(PType *target, const PType::Conversion **slots, int numslots)
-{
-	if (this == target)
-	{
-		return 0;
-	}
-	// The queue is implemented as a ring buffer
-	VisitQueue queue;
-	VisitedNodeSet visited;
-
-	// Use a breadth-first search to find the shortest path to the target.
-	MarkPred(NULL, -1, -1);
-	queue.Push(this);
-	visited.Insert(this);
-	while (!queue.IsEmpty())
-	{
-		PType *t = queue.Pop();
-		if (t == target)
-		{ // found it
-			if (slots != NULL)
-			{
-				if (t->Distance >= numslots)
-				{ // Distance is too far for the output
-					return -2;
-				}
-				t->FillConversionPath(slots);
-			}
-			return t->Distance + 1;
-		}
-		for (unsigned i = 0; i < t->Conversions.Size(); ++i)
-		{
-			PType *succ = t->Conversions[i].TargetType;
-			if (!visited.Check(succ))
-			{
-				succ->MarkPred(t, i, t->Distance + 1);
-				visited.Insert(succ);
-				queue.Push(succ);
-			}
-		}
-	}
-	return -1;
-}
-
-//==========================================================================
-//
-// PType :: FillConversionPath
-//
-// Traces backwards from the target type to the original type and fills in
-// the conversions necessary to get between them. slots must point to an
-// array large enough to contain the entire path.
-//
-//==========================================================================
-
-void PType::FillConversionPath(const PType::Conversion **slots)
-{
-	for (PType *node = this; node->Distance >= 0; node = node->PredType)
-	{
-		assert(node->PredType != NULL);
-		slots[node->Distance] = &node->PredType->Conversions[node->PredConv];
-	}
-}
-
-//==========================================================================
-//
-// PType :: VisitQueue :: Push
-//
-//==========================================================================
-
-void PType::VisitQueue::Push(PType *type)
-{
-	Queue[In] = type;
-	Advance(In);
-	assert(!IsEmpty() && "Queue overflowed");
-}
-
-//==========================================================================
-//
-// PType :: VisitQueue :: Pop
-//
-//==========================================================================
-
-PType *PType::VisitQueue::Pop()
-{
-	if (IsEmpty())
-	{
-		return NULL;
-	}
-	PType *node = Queue[Out];
-	Advance(Out);
-	return node;
-}
-
-//==========================================================================
-//
-// PType :: VisitedNodeSet :: Insert
-//
-//==========================================================================
-
-void PType::VisitedNodeSet::Insert(PType *node)
-{
-	assert(!Check(node) && "Node was already inserted");
-	size_t buck = Hash(node) & (countof(Buckets) - 1);
-	node->VisitNext = Buckets[buck];
-	Buckets[buck] = node;
-}
-
-//==========================================================================
-//
-// PType :: VisitedNodeSet :: Check
-//
-//==========================================================================
-
-bool PType::VisitedNodeSet::Check(const PType *node)
-{
-	size_t buck = Hash(node) & (countof(Buckets) - 1);
-	for (const PType *probe = Buckets[buck]; probe != NULL; probe = probe->VisitNext)
-	{
-		if (probe == node)
-		{
-			return true;
-		}
-	}
-	return false;
-}
-
 //==========================================================================
 //
 // PType :: WriteValue
diff --git a/src/dobjtype.h b/src/dobjtype.h
index d38c42c58..7054095bd 100644
--- a/src/dobjtype.h
+++ b/src/dobjtype.h
@@ -214,15 +214,6 @@ public:
 	typedef PClassType MetaClass;
 	MetaClass *GetClass() const;
 
-	struct Conversion
-	{
-		Conversion(PType *target, void (*convert)(ZCC_ExprConstant *, class FSharedStringArena &))
-			: TargetType(target), ConvertConstant(convert) {}
-
-		PType *TargetType;
-		void (*ConvertConstant)(ZCC_ExprConstant *val, class FSharedStringArena &strdump);
-	};
-
 	unsigned int	Size;			// this type's size
 	unsigned int	Align;			// this type's preferred alignment
 	PType			*HashNext;		// next type in this type table
@@ -235,10 +226,6 @@ public:
 	virtual ~PType();
 	virtual bool isNumeric() { return false; }
 
-	bool AddConversion(PType *target, void (*convertconst)(ZCC_ExprConstant *, class FSharedStringArena &));
-
-	int FindConversion(PType *target, const Conversion **slots, int numslots);
-
 	// Writes the value of a variable of this type at (addr) to an archive, preceded by
 	// a tag indicating its type. The tag is there so that variable types can be changed
 	// without completely breaking savegames, provided that the change isn't between
@@ -318,54 +305,6 @@ public:
 	size_t PropagateMark();
 
 	static void StaticInit();
-
-private:
-	// Stuff for type conversion searches
-	class VisitQueue
-	{
-	public:
-		VisitQueue() : In(0), Out(0) {}
-		void Push(PType *type);
-		PType *Pop();
-		bool IsEmpty() { return In == Out; }
-
-	private:
-		// This is a fixed-sized ring buffer.
-		PType *Queue[64];
-		int In, Out;
-
-		void Advance(int &ptr)
-		{
-			ptr = (ptr + 1) & (countof(Queue) - 1);
-		}
-	};
-
-	class VisitedNodeSet
-	{
-	public:
-		VisitedNodeSet() { memset(Buckets, 0, sizeof(Buckets)); }
-		void Insert(PType *node);
-		bool Check(const PType *node);
-
-	private:
-		PType *Buckets[32];
-
-		size_t Hash(const PType *type) { return size_t(type) >> 4; }
-	};
-
-	TArray<Conversion> Conversions;
-	PType *PredType;
-	PType *VisitNext;
-	short PredConv;
-	short Distance;
-
-	void MarkPred(PType *pred, int conv, int dist)
-	{
-		PredType = pred;
-		PredConv = conv;
-		Distance = dist;
-	}
-	void FillConversionPath(const Conversion **slots);
 };
 
 // Not-really-a-type types --------------------------------------------------
@@ -1043,7 +982,7 @@ class PSymbolConstString : public PSymbolConst
 public:
 	FString Str;
 
-	PSymbolConstString(FName name, FString &str) : PSymbolConst(name, TypeString), Str(str) {}
+	PSymbolConstString(FName name, const FString &str) : PSymbolConst(name, TypeString), Str(str) {}
 	PSymbolConstString() {}
 };
 
diff --git a/src/g_game.cpp b/src/g_game.cpp
index 936caea18..e5c327472 100644
--- a/src/g_game.cpp
+++ b/src/g_game.cpp
@@ -1891,168 +1891,150 @@ void G_DoLoadGame ()
 	hidecon = gameaction == ga_loadgamehidecon;
 	gameaction = ga_nothing;
 
-	FResourceFile *resfile = FResourceFile::OpenResourceFile(savename.GetChars(), nullptr, true, true);
+	std::unique_ptr<FResourceFile> resfile(FResourceFile::OpenResourceFile(savename.GetChars(), nullptr, true, true));
 	if (resfile == nullptr)
 	{
 		Printf ("Could not read savegame '%s'\n", savename.GetChars());
 		return;
 	}
-	try
+	FResourceLump *info = resfile->FindLump("info.json");
+	if (info == nullptr)
 	{
-		FResourceLump *info = resfile->FindLump("info.json");
-		if (info == nullptr)
-		{
-			delete resfile;
-			Printf("'%s' is not a valid savegame: Missing 'info.json'.\n", savename.GetChars());
-			return;
-		}
-
-		SaveVersion = 0;
-
-		void *data = info->CacheLump();
-		FSerializer arc;
-		if (!arc.OpenReader((const char *)data, info->LumpSize))
-		{
-			Printf("Failed to access savegame info\n");
-			delete resfile;
-			return;
-		}
-
-		// Check whether this savegame actually has been created by a compatible engine.
-		// Since there are ZDoom derivates using the exact same savegame format but
-		// with mutual incompatibilities this check simplifies things significantly.
-		FString savever, engine, map;
-		arc("Save Version", SaveVersion);
-		arc("Engine", engine);
-		arc("Current Map", map);
-
-		if (engine.CompareNoCase(GAMESIG) != 0)
-		{
-			// Make a special case for the message printed for old savegames that don't
-			// have this information.
-			if (engine.IsEmpty())
-			{
-				Printf("Savegame is from an incompatible version\n");
-			}
-			else
-			{
-				Printf("Savegame is from another ZDoom-based engine: %s\n", engine.GetChars());
-			}
-			delete resfile;
-			return;
-		}
-
-		if (SaveVersion < MINSAVEVER || SaveVersion > SAVEVER)
-		{
-			delete resfile;
-			Printf("Savegame is from an incompatible version");
-			if (SaveVersion < MINSAVEVER)
-			{
-				Printf(": %d (%d is the oldest supported)", SaveVersion, MINSAVEVER);
-			}
-			else
-			{
-				Printf(": %d (%d is the highest supported)", SaveVersion, SAVEVER);
-			}
-			Printf("\n");
-			return;
-		}
-
-		if (!G_CheckSaveGameWads(arc, true))
-		{
-			delete resfile;
-			return;
-		}
-
-		if (map.IsEmpty())
-		{
-			Printf("Savegame is missing the current map\n");
-			delete resfile;
-			return;
-		}
-
-		// Now that it looks like we can load this save, hide the fullscreen console if it was up
-		// when the game was selected from the menu.
-		if (hidecon && gamestate == GS_FULLCONSOLE)
-		{
-			gamestate = GS_HIDECONSOLE;
-		}
-		// we are done with info.json.
-		arc.Close();
-
-		info = resfile->FindLump("globals.json");
-		if (info == nullptr)
-		{
-			delete resfile;
-			Printf("'%s' is not a valid savegame: Missing 'globals.json'.\n", savename.GetChars());
-			return;
-		}
-
-		data = info->CacheLump();
-		if (!arc.OpenReader((const char *)data, info->LumpSize))
-		{
-			Printf("Failed to access savegame info\n");
-			delete resfile;
-			return;
-		}
-
-
-		// Read intermission data for hubs
-		G_SerializeHub(arc);
-
-		bglobal.RemoveAllBots(true);
-
-		FString cvar;
-		arc("importantcvars", cvar);
-		if (!cvar.IsEmpty())
-		{
-			BYTE *vars_p = (BYTE *)cvar.GetChars();
-			C_ReadCVars(&vars_p);
-		}
-
-		DWORD time[2] = { 1,0 };
-
-		arc("ticrate", time[0])
-			("leveltime", time[1]);
-		// dearchive all the modifications
-		level.time = Scale(time[1], TICRATE, time[0]);
-
-		G_ReadSnapshots(resfile);
-		delete resfile;	// we no longer need the resource file below this point
-		resfile = nullptr;
-		G_ReadVisited(arc);
-
-		// load a base level
-		savegamerestore = true;		// Use the player actors in the savegame
-		bool demoplaybacksave = demoplayback;
-		G_InitNew(map, false);
-		demoplayback = demoplaybacksave;
-		savegamerestore = false;
-
-		STAT_Serialize(arc);
-		FRandom::StaticReadRNGState(arc);
-		P_ReadACSDefereds(arc);
-		P_ReadACSVars(arc);
-
-		NextSkill = -1;
-		arc("nextskill", NextSkill);
-
-		if (level.info != nullptr)
-			level.info->Snapshot.Clean();
-
-		BackupSaveName = savename;
-
-		// At this point, the GC threshold is likely a lot higher than the
-		// amount of memory in use, so bring it down now by starting a
-		// collection.
-		GC::StartCollection();
+		Printf("'%s' is not a valid savegame: Missing 'info.json'.\n", savename.GetChars());
+		return;
 	}
-	catch (...)
+
+	SaveVersion = 0;
+
+	void *data = info->CacheLump();
+	FSerializer arc;
+	if (!arc.OpenReader((const char *)data, info->LumpSize))
 	{
-		// delete the resource file if anything goes wrong in here.
-		if (resfile != nullptr) delete resfile;
-		throw;
+		Printf("Failed to access savegame info\n");
+		return;
 	}
+
+	// Check whether this savegame actually has been created by a compatible engine.
+	// Since there are ZDoom derivates using the exact same savegame format but
+	// with mutual incompatibilities this check simplifies things significantly.
+	FString savever, engine, map;
+	arc("Save Version", SaveVersion);
+	arc("Engine", engine);
+	arc("Current Map", map);
+
+	if (engine.CompareNoCase(GAMESIG) != 0)
+	{
+		// Make a special case for the message printed for old savegames that don't
+		// have this information.
+		if (engine.IsEmpty())
+		{
+			Printf("Savegame is from an incompatible version\n");
+		}
+		else
+		{
+			Printf("Savegame is from another ZDoom-based engine: %s\n", engine.GetChars());
+		}
+		return;
+	}
+
+	if (SaveVersion < MINSAVEVER || SaveVersion > SAVEVER)
+	{
+		Printf("Savegame is from an incompatible version");
+		if (SaveVersion < MINSAVEVER)
+		{
+			Printf(": %d (%d is the oldest supported)", SaveVersion, MINSAVEVER);
+		}
+		else
+		{
+			Printf(": %d (%d is the highest supported)", SaveVersion, SAVEVER);
+		}
+		Printf("\n");
+		return;
+	}
+
+	if (!G_CheckSaveGameWads(arc, true))
+	{
+		return;
+	}
+
+	if (map.IsEmpty())
+	{
+		Printf("Savegame is missing the current map\n");
+		return;
+	}
+
+	// Now that it looks like we can load this save, hide the fullscreen console if it was up
+	// when the game was selected from the menu.
+	if (hidecon && gamestate == GS_FULLCONSOLE)
+	{
+		gamestate = GS_HIDECONSOLE;
+	}
+	// we are done with info.json.
+	arc.Close();
+
+	info = resfile->FindLump("globals.json");
+	if (info == nullptr)
+	{
+		Printf("'%s' is not a valid savegame: Missing 'globals.json'.\n", savename.GetChars());
+		return;
+	}
+
+	data = info->CacheLump();
+	if (!arc.OpenReader((const char *)data, info->LumpSize))
+	{
+		Printf("Failed to access savegame info\n");
+		return;
+	}
+
+
+	// Read intermission data for hubs
+	G_SerializeHub(arc);
+
+	bglobal.RemoveAllBots(true);
+
+	FString cvar;
+	arc("importantcvars", cvar);
+	if (!cvar.IsEmpty())
+	{
+		BYTE *vars_p = (BYTE *)cvar.GetChars();
+		C_ReadCVars(&vars_p);
+	}
+
+	DWORD time[2] = { 1,0 };
+
+	arc("ticrate", time[0])
+		("leveltime", time[1]);
+	// dearchive all the modifications
+	level.time = Scale(time[1], TICRATE, time[0]);
+
+	G_ReadSnapshots(resfile.get());
+	resfile.reset(nullptr);	// we no longer need the resource file below this point
+	G_ReadVisited(arc);
+
+	// load a base level
+	savegamerestore = true;		// Use the player actors in the savegame
+	bool demoplaybacksave = demoplayback;
+	G_InitNew(map, false);
+	demoplayback = demoplaybacksave;
+	savegamerestore = false;
+
+	STAT_Serialize(arc);
+	FRandom::StaticReadRNGState(arc);
+	P_ReadACSDefereds(arc);
+	P_ReadACSVars(arc);
+
+	NextSkill = -1;
+	arc("nextskill", NextSkill);
+
+	if (level.info != nullptr)
+		level.info->Snapshot.Clean();
+
+	BackupSaveName = savename;
+
+	// At this point, the GC threshold is likely a lot higher than the
+	// amount of memory in use, so bring it down now by starting a
+	// collection.
+	GC::StartCollection();
 }
 
 
diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp
index b7d8335f8..327ac5fd4 100644
--- a/src/gl/shaders/gl_shader.cpp
+++ b/src/gl/shaders/gl_shader.cpp
@@ -60,15 +60,15 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
 	static char buffer[10000];
 	FString error;
 
-	int i_lump = Wads.CheckNumForFullName("shaders/glsl/shaderdefs.i");
+	int i_lump = Wads.CheckNumForFullName("shaders/glsl/shaderdefs.i", 0);
 	if (i_lump == -1) I_Error("Unable to load 'shaders/glsl/shaderdefs.i'");
 	FMemLump i_data = Wads.ReadLump(i_lump);
 
-	int vp_lump = Wads.CheckNumForFullName(vert_prog_lump);
+	int vp_lump = Wads.CheckNumForFullName(vert_prog_lump, 0);
 	if (vp_lump == -1) I_Error("Unable to load '%s'", vert_prog_lump);
 	FMemLump vp_data = Wads.ReadLump(vp_lump);
 
-	int fp_lump = Wads.CheckNumForFullName(frag_prog_lump);
+	int fp_lump = Wads.CheckNumForFullName(frag_prog_lump, 0);
 	if (fp_lump == -1) I_Error("Unable to load '%s'", frag_prog_lump);
 	FMemLump fp_data = Wads.ReadLump(fp_lump);
 
diff --git a/src/gl/shaders/gl_shaderprogram.cpp b/src/gl/shaders/gl_shaderprogram.cpp
index 1ed19913a..7a4690874 100644
--- a/src/gl/shaders/gl_shaderprogram.cpp
+++ b/src/gl/shaders/gl_shaderprogram.cpp
@@ -89,7 +89,7 @@ void FShaderProgram::CreateShader(ShaderType type)
 
 void FShaderProgram::Compile(ShaderType type, const char *lumpName, const char *defines, int maxGlslVersion)
 {
-	int lump = Wads.CheckNumForFullName(lumpName);
+	int lump = Wads.CheckNumForFullName(lumpName, 0);
 	if (lump == -1) I_FatalError("Unable to load '%s'", lumpName);
 	FString code = Wads.ReadLump(lump).GetString().GetChars();
 	Compile(type, lumpName, code, defines, maxGlslVersion);
diff --git a/src/gl/system/gl_menu.cpp b/src/gl/system/gl_menu.cpp
index 11534436b..8dffbc4f2 100644
--- a/src/gl/system/gl_menu.cpp
+++ b/src/gl/system/gl_menu.cpp
@@ -84,8 +84,11 @@ void gl_SetupMenu()
 	{
 		for(int i = (*opt)->mValues.Size()-1; i>=0; i--)
 		{
-			// Delete HQnX resize modes for non MSVC targets
-			if ((*opt)->mValues[i].Value >= 7.0)
+			// Delete hqNx MMX resize modes for targets
+			// without support of this instruction set
+			const auto index = llround((*opt)->mValues[i].Value);
+
+			if (index > 6 && index < 10)
 			{
 				(*opt)->mValues.Delete(i);
 			}
diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp
index 210c0cd5b..807326d78 100644
--- a/src/p_mobj.cpp
+++ b/src/p_mobj.cpp
@@ -924,8 +924,7 @@ DEFINE_ACTION_FUNCTION(AActor, TakeInventory)
 	PARAM_INT(amount);
 	PARAM_BOOL_DEF(fromdecorate);
 	PARAM_BOOL_DEF(notakeinfinite);
-	self->TakeInventory(item, amount, fromdecorate, notakeinfinite);
-	return 0;
+	ACTION_RETURN_BOOL(self->TakeInventory(item, amount, fromdecorate, notakeinfinite));
 }
 
 //============================================================================
diff --git a/src/posix/sdl/sdlglvideo.cpp b/src/posix/sdl/sdlglvideo.cpp
index 7858fdad9..e8b61ef02 100644
--- a/src/posix/sdl/sdlglvideo.cpp
+++ b/src/posix/sdl/sdlglvideo.cpp
@@ -133,10 +133,6 @@ SDLGLVideo::SDLGLVideo (int parm)
         fprintf( stderr, "Video initialization failed: %s\n",
              SDL_GetError( ) );
     }
-#ifndef	_WIN32
-	// mouse cursor is visible by default on linux systems, we disable it by default
-	SDL_ShowCursor (0);
-#endif
 }
 
 SDLGLVideo::~SDLGLVideo ()
diff --git a/src/sc_man.cpp b/src/sc_man.cpp
index ccfb01ac5..5f944735a 100644
--- a/src/sc_man.cpp
+++ b/src/sc_man.cpp
@@ -1008,6 +1008,7 @@ void FScanner::CheckOpen()
 int FScriptPosition::ErrorCounter;
 int FScriptPosition::WarnCounter;
 bool FScriptPosition::StrictErrors;	// makes all OPTERROR messages real errors.
+bool FScriptPosition::errorout;		// call I_Error instead of printing the error itself.
 
 FScriptPosition::FScriptPosition(const FScriptPosition &other)
 {
@@ -1054,6 +1055,8 @@ void FScriptPosition::Message (int severity, const char *message, ...) const
 	{
 		severity = StrictErrors || strictdecorate ? MSG_ERROR : MSG_WARNING;
 	}
+	// This is mainly for catching the error with an exception handler.
+	if (severity == MSG_ERROR && errorout) severity = MSG_FATAL;
 
 	if (message == NULL)
 	{
diff --git a/src/sc_man.h b/src/sc_man.h
index 362d57faf..3347fe26a 100644
--- a/src/sc_man.h
+++ b/src/sc_man.h
@@ -144,6 +144,7 @@ struct FScriptPosition
 	static int WarnCounter;
 	static int ErrorCounter;
 	static bool StrictErrors;
+	static bool errorout;
 	FString FileName;
 	int ScriptLine;
 
diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp
index cb5944ade..6065050a9 100644
--- a/src/scripting/codegeneration/codegen.cpp
+++ b/src/scripting/codegeneration/codegen.cpp
@@ -4078,7 +4078,7 @@ ExpEmit FxConcat::Emit(VMFunctionBuilder *build)
 	}
 	else
 	{
-		int cast;
+		int cast = 0;
 		strng2 = ExpEmit(build, REGT_STRING);
 		if (op2.Konst)
 		{
@@ -5653,6 +5653,12 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
 
 	if (Identifier == NAME_Default)
 	{
+		if (ctx.Function == nullptr)
+		{
+			ScriptPosition.Message(MSG_ERROR, "Unable to access class defaults from constant declaration");
+			delete this;
+			return nullptr;
+		}
 		if (ctx.Function->Variants[0].SelfClass == nullptr)
 		{
 			ScriptPosition.Message(MSG_ERROR, "Unable to access class defaults from static function");
@@ -5680,6 +5686,13 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
 	{
 		if (sym->IsKindOf(RUNTIME_CLASS(PField)))
 		{
+			if (ctx.Function == nullptr)
+			{
+				ScriptPosition.Message(MSG_ERROR, "Unable to access class member %s from constant declaration", sym->SymbolName.GetChars());
+				delete this;
+				return nullptr;
+			}
+
 			FxExpression *self = new FxSelf(ScriptPosition);
 			self = self->Resolve(ctx);
 			newex = ResolveMember(ctx, ctx.Function->Variants[0].SelfClass, self, ctx.Function->Variants[0].SelfClass);
@@ -5697,6 +5710,12 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
 			newex = FxConstant::MakeConstant(sym, ScriptPosition);
 			goto foundit;
 		}
+		else if (ctx.Function == nullptr)
+		{
+			ScriptPosition.Message(MSG_ERROR, "Unable to access class member %s from constant declaration", sym->SymbolName.GetChars());
+			delete this;
+			return nullptr;
+		}
 		// Do this check for ZScript as well, so that a clearer error message can be printed. MSG_OPTERROR will default to MSG_ERROR there.
 		else if (ctx.Function->Variants[0].SelfClass != ctx.Class && sym->IsKindOf(RUNTIME_CLASS(PField)))
 		{
@@ -5810,7 +5829,7 @@ FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PStruct *classct
 		return x->Resolve(ctx);
 	}
 
-	if ((sym = objtype->Symbols.FindSymbolInTable(Identifier, symtbl)) != nullptr)
+	if (objtype != nullptr && (sym = objtype->Symbols.FindSymbolInTable(Identifier, symtbl)) != nullptr)
 	{
 		if (sym->IsKindOf(RUNTIME_CLASS(PSymbolConst)))
 		{
@@ -7149,7 +7168,6 @@ static bool CheckArgSize(FName fname, FArgumentList &args, int min, int max, FSc
 
 FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
 {
-	ABORT(ctx.Class);
 	bool error = false;
 
 	for (auto a : ArgList)
@@ -7162,20 +7180,30 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
 		}
 	}
 
-	PFunction *afd = FindClassMemberFunction(ctx.Class, ctx.Class, MethodName, ScriptPosition, &error);
-
-	if (afd != nullptr)
+	if (ctx.Class != nullptr)
 	{
-		if (!CheckFunctionCompatiblity(ScriptPosition, ctx.Function, afd))
-		{
-			delete this;
-			return nullptr;
-		}
+		PFunction *afd = FindClassMemberFunction(ctx.Class, ctx.Class, MethodName, ScriptPosition, &error);
 
-		auto self = (afd->Variants[0].Flags & VARF_Method)? new FxSelf(ScriptPosition) : nullptr;
-		auto x = new FxVMFunctionCall(self, afd, ArgList, ScriptPosition, false);
-		delete this;
-		return x->Resolve(ctx);
+		if (afd != nullptr)
+		{
+			if (ctx.Function == nullptr)
+			{
+				ScriptPosition.Message(MSG_ERROR, "Unable to call function %s from constant declaration", MethodName.GetChars());
+				delete this;
+				return nullptr;
+			}
+
+			if (!CheckFunctionCompatiblity(ScriptPosition, ctx.Function, afd))
+			{
+				delete this;
+				return nullptr;
+			}
+
+			auto self = (afd->Variants[0].Flags & VARF_Method) ? new FxSelf(ScriptPosition) : nullptr;
+			auto x = new FxVMFunctionCall(self, afd, ArgList, ScriptPosition, false);
+			delete this;
+			return x->Resolve(ctx);
+		}
 	}
 
 	for (size_t i = 0; i < countof(FxFlops); ++i)
@@ -7202,7 +7230,13 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
 	if (special != 0 && min >= 0)
 	{
 		int paramcount = ArgList.Size();
-		if (paramcount < min)
+		if (ctx.Function == nullptr || ctx.Class == nullptr)
+		{
+			ScriptPosition.Message(MSG_ERROR, "Unable to call action special %s from constant declaration", MethodName.GetChars());
+			delete this;
+			return nullptr;
+		}
+		else if (paramcount < min)
 		{
 			ScriptPosition.Message(MSG_ERROR, "Not enough parameters for '%s' (expected %d, got %d)", 
 				MethodName.GetChars(), min, paramcount);
@@ -7427,13 +7461,20 @@ FxMemberFunctionCall::~FxMemberFunctionCall()
 
 FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
 {
-	ABORT(ctx.Class);
 	PStruct *cls;
 	bool staticonly = false;
 	bool novirtual = false;
 
 	PStruct *ccls = nullptr;
 
+	if (ctx.Class == nullptr)
+	{
+		// There's no way that a member function call can resolve to a constant so abort right away.
+		ScriptPosition.Message(MSG_ERROR, "Expression is not constant.");
+		delete this;
+		return nullptr;
+	}
+
 	for (auto a : ArgList)
 	{
 		if (a == nullptr)
@@ -7467,6 +7508,12 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
 				staticonly = true;
 				if (ccls->IsKindOf(RUNTIME_CLASS(PClass)))
 				{
+					if (ctx.Function == nullptr)
+					{
+						ScriptPosition.Message(MSG_ERROR, "Unable to call %s from constant declaration", MethodName.GetChars());
+						delete this;
+						return nullptr;
+					}
 					auto clstype = dyn_cast<PClass>(ctx.Function->Variants[0].SelfClass);
 					if (clstype != nullptr)
 					{
@@ -7493,6 +7540,12 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
 
 	if (Self->ExprType == EFX_Super)
 	{
+		if (ctx.Function == nullptr)
+		{
+			ScriptPosition.Message(MSG_ERROR, "Unable to call %s from constant declaration", MethodName.GetChars());
+			delete this;
+			return nullptr;
+		}
 		auto clstype = dyn_cast<PClass>(ctx.Function->Variants[0].SelfClass);
 		if (clstype != nullptr)
 		{
@@ -7743,6 +7796,12 @@ isresolved:
 
 	if (afd->Variants[0].Flags & VARF_Method)
 	{
+		if (ctx.Function == nullptr)
+		{
+			ScriptPosition.Message(MSG_ERROR, "Unable to call %s from constant declaration", MethodName.GetChars());
+			delete this;
+			return nullptr;
+		}
 		if (Self->ExprType == EFX_Self)
 		{
 			if (!CheckFunctionCompatiblity(ScriptPosition, ctx.Function, afd))
diff --git a/src/scripting/zscript/ast.cpp b/src/scripting/zscript/ast.cpp
index b29e1eeb2..586601890 100644
--- a/src/scripting/zscript/ast.cpp
+++ b/src/scripting/zscript/ast.cpp
@@ -513,7 +513,7 @@ static void OpenExprType(FLispString &out, EZCCExprType type)
 
 	if (unsigned(type) < PEX_COUNT_OF)
 	{
-		mysnprintf(buf, countof(buf), "expr-%s", ZCC_OpInfo[type].OpName);
+		mysnprintf(buf, countof(buf), "expr %d", type);
 	}
 	else
 	{
diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp
index ddf08fbc4..714b56714 100644
--- a/src/scripting/zscript/zcc_compile.cpp
+++ b/src/scripting/zscript/zcc_compile.cpp
@@ -48,10 +48,59 @@
 #include "p_lnspec.h"
 #include "i_system.h"
 #include "gdtoa.h"
-#include "codegeneration/codegen.h"
 #include "vmbuilder.h"
 #include "version.h"
 
+static int GetIntConst(FxExpression *ex, FCompileContext &ctx)
+{
+	ex = new FxIntCast(ex, false);
+	ex = ex->Resolve(ctx);
+	return ex ? static_cast<FxConstant*>(ex)->GetValue().GetInt() : 0;
+}
+
+static double GetFloatConst(FxExpression *ex, FCompileContext &ctx)
+{
+	ex = new FxFloatCast(ex);
+	ex = ex->Resolve(ctx);
+	return ex ? static_cast<FxConstant*>(ex)->GetValue().GetFloat() : 0;
+}
+
+static FString GetStringConst(FxExpression *ex, FCompileContext &ctx)
+{
+	ex = new FxStringCast(ex);
+	ex = ex->Resolve(ctx);
+	return static_cast<FxConstant*>(ex)->GetValue().GetString();
+}
+
+int ZCCCompiler::IntConstFromNode(ZCC_TreeNode *node, PStruct *cls)
+{
+	FCompileContext ctx(cls, false);
+	FxExpression *ex = new FxIntCast(ConvertNode(node), false);
+	ex = ex->Resolve(ctx);
+	if (ex == nullptr) return 0;
+	if (!ex->isConstant())
+	{
+		ex->ScriptPosition.Message(MSG_ERROR, "Expression is not constant");
+		return 0;
+	}
+	return static_cast<FxConstant*>(ex)->GetValue().GetInt();
+}
+
+FString ZCCCompiler::StringConstFromNode(ZCC_TreeNode *node, PStruct *cls)
+{
+	FCompileContext ctx(cls, false);
+	FxExpression *ex = new FxStringCast(ConvertNode(node));
+	ex = ex->Resolve(ctx);
+	if (ex == nullptr) return "";
+	if (!ex->isConstant())
+	{
+		ex->ScriptPosition.Message(MSG_ERROR, "Expression is not constant");
+		return "";
+	}
+	return static_cast<FxConstant*>(ex)->GetValue().GetString();
+}
+
+
 //==========================================================================
 //
 // ZCCCompiler :: ProcessClass
@@ -606,11 +655,11 @@ void ZCCCompiler::CreateClassTypes()
 //
 //==========================================================================
 
-void ZCCCompiler::CopyConstants(TArray<ZCC_ConstantWork> &dest, TArray<ZCC_ConstantDef*> &Constants, PSymbolTable *ot)
+void ZCCCompiler::CopyConstants(TArray<ZCC_ConstantWork> &dest, TArray<ZCC_ConstantDef*> &Constants, PStruct *cls, PSymbolTable *ot)
 {
 	for (auto c : Constants)
 	{
-		dest.Push({ c, ot });
+		dest.Push({ c, cls, ot });
 	}
 }
 
@@ -629,14 +678,14 @@ void ZCCCompiler::CompileAllConstants()
 	// put all constants in one list to make resolving this easier.
 	TArray<ZCC_ConstantWork> constantwork;
 
-	CopyConstants(constantwork, Constants, OutputSymbols);
+	CopyConstants(constantwork, Constants, nullptr, OutputSymbols);
 	for (auto c : Classes)
 	{
-		CopyConstants(constantwork, c->Constants, &c->Type()->Symbols);
+		CopyConstants(constantwork, c->Constants, c->Type(), &c->Type()->Symbols);
 	}
 	for (auto s : Structs)
 	{
-		CopyConstants(constantwork, s->Constants, &s->Type()->Symbols);
+		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.
@@ -657,7 +706,7 @@ void ZCCCompiler::CompileAllConstants()
 		donesomething = false;
 		for (unsigned i = 0; i < constantwork.Size(); i++)
 		{
-			if (CompileConstant(constantwork[i].node, constantwork[i].outputtable))
+			if (CompileConstant(&constantwork[i]))
 			{
 				AddConstant(constantwork[i]);
 				// Remove the constant from the list
@@ -685,6 +734,9 @@ void ZCCCompiler::AddConstant(ZCC_ConstantWork &constant)
 {
 	auto def = constant.node;
 	auto val = def->Value;
+	ExpVal &c = constant.constval;
+	
+	// This is for literal constants.
 	if (val->NodeType == AST_ExprConstant)
 	{
 		ZCC_ExprConstant *cval = static_cast<ZCC_ExprConstant *>(val);
@@ -711,14 +763,40 @@ void ZCCCompiler::AddConstant(ZCC_ConstantWork &constant)
 			Error(def->Value, "Bad type for constant definiton");
 			def->Symbol = nullptr;
 		}
-
-		if (def->Symbol == nullptr)
-		{
-			// Create a dummy constant so we don't make any undefined value warnings.
-			def->Symbol = new PSymbolConstNumeric(def->NodeName, TypeError, 0);
-		}
-		constant.outputtable->ReplaceSymbol(def->Symbol);
 	}
+	else
+	{
+		if (c.Type == TypeString)
+		{
+			def->Symbol = new PSymbolConstString(def->NodeName, c.GetString());
+		}
+		else if (c.Type->IsA(RUNTIME_CLASS(PInt)))
+		{
+			// How do we get an Enum type in here without screwing everything up???
+			//auto type = def->Type != nullptr ? def->Type : cval->Type;
+			def->Symbol = new PSymbolConstNumeric(def->NodeName, c.Type, c.GetInt());
+		}
+		else if (c.Type->IsA(RUNTIME_CLASS(PFloat)))
+		{
+			if (def->Type != nullptr)
+			{
+				Error(def, "Enum members must be integer values");
+			}
+			def->Symbol = new PSymbolConstNumeric(def->NodeName, c.Type, c.GetFloat());
+		}
+		else
+		{
+			Error(def->Value, "Bad type for constant definiton");
+			def->Symbol = nullptr;
+		}
+	}
+
+	if (def->Symbol == nullptr)
+	{
+		// Create a dummy constant so we don't make any undefined value warnings.
+		def->Symbol = new PSymbolConstNumeric(def->NodeName, TypeError, 0);
+	}
+	constant.Outputtable->ReplaceSymbol(def->Symbol);
 }
 
 //==========================================================================
@@ -730,309 +808,33 @@ void ZCCCompiler::AddConstant(ZCC_ConstantWork &constant)
 //
 //==========================================================================
 
-bool ZCCCompiler::CompileConstant(ZCC_ConstantDef *def, PSymbolTable *sym)
+bool ZCCCompiler::CompileConstant(ZCC_ConstantWork *work)
 {
-	assert(def->Symbol == nullptr);
-
-	ZCC_Expression *val = Simplify(def->Value, sym, true);
-	def->Value = val;
-	return (val->NodeType == AST_ExprConstant);
-}
-
-
-//==========================================================================
-//
-// ZCCCompiler :: Simplify
-//
-// For an expression,
-//   Evaluate operators whose arguments are both constants, replacing it
-//     with a new constant.
-//   For a binary operator with one constant argument, put it on the right-
-//     hand operand, where permitted.
-//   Perform automatic type promotion.
-//
-//==========================================================================
-
-ZCC_Expression *ZCCCompiler::Simplify(ZCC_Expression *root, PSymbolTable *sym, bool wantconstant)
-{
-	SimplifyingConstant = wantconstant;
-	return  DoSimplify(root, sym);
-}
-
-ZCC_Expression *ZCCCompiler::DoSimplify(ZCC_Expression *root, PSymbolTable *sym)
-{
-	if (root->NodeType == AST_ExprUnary)
+	FCompileContext ctx(work->cls, false);
+	FxExpression *exp = ConvertNode(work->node->Value);
+	try
 	{
-		return SimplifyUnary(static_cast<ZCC_ExprUnary *>(root), sym);
-	}
-	else if (root->NodeType == AST_ExprBinary)
-	{
-		return SimplifyBinary(static_cast<ZCC_ExprBinary *>(root), sym);
-	}
-	else if (root->Operation == PEX_ID)
-	{
-		return IdentifyIdentifier(static_cast<ZCC_ExprID *>(root), sym);
-	}
-	else if (root->Operation == PEX_MemberAccess)
-	{
-		return SimplifyMemberAccess(static_cast<ZCC_ExprMemberAccess *>(root), sym);
-	}
-	else if (root->Operation == PEX_FuncCall)
-	{
-		return SimplifyFunctionCall(static_cast<ZCC_ExprFuncCall *>(root), sym);
-	}
-	return root;
-}
-
-//==========================================================================
-//
-// ZCCCompiler :: SimplifyUnary
-//
-//==========================================================================
-
-ZCC_Expression *ZCCCompiler::SimplifyUnary(ZCC_ExprUnary *unary, PSymbolTable *sym)
-{
-	unary->Operand = DoSimplify(unary->Operand, sym);
-	if (unary->Operand->Type == nullptr)
-	{
-		return unary;
-	}
-	ZCC_OpProto *op = PromoteUnary(unary->Operation, unary->Operand);
-	if (op == NULL)
-	{ // Oh, poo!
-		unary->Type = TypeError;
-	}
-	else if (unary->Operand->Operation == PEX_ConstValue)
-	{
-		return op->EvalConst1(static_cast<ZCC_ExprConstant *>(unary->Operand));
-	}
-	return unary;
-}
-
-//==========================================================================
-//
-// ZCCCompiler :: SimplifyBinary
-//
-//==========================================================================
-
-ZCC_Expression *ZCCCompiler::SimplifyBinary(ZCC_ExprBinary *binary, PSymbolTable *sym)
-{
-	binary->Left = DoSimplify(binary->Left, sym);
-	binary->Right = DoSimplify(binary->Right, sym);
-	if (binary->Left->Type == nullptr || binary->Right->Type == nullptr)
-	{
-		// We do not know yet what this is so we cannot promote it (yet.)
-		return binary;
-	}
-	ZCC_OpProto *op = PromoteBinary(binary->Operation, binary->Left, binary->Right);
-	if (op == NULL)
-	{
-		binary->Type = TypeError;
-	}
-	else if (binary->Left->Operation == PEX_ConstValue &&
-		binary->Right->Operation == PEX_ConstValue)
-	{
-		return op->EvalConst2(static_cast<ZCC_ExprConstant *>(binary->Left),
-							  static_cast<ZCC_ExprConstant *>(binary->Right), AST.Strings);
-	}
-	return binary;
-}
-
-//==========================================================================
-//
-// ZCCCompiler :: SimplifyMemberAccess
-//
-//==========================================================================
-
-ZCC_Expression *ZCCCompiler::SimplifyMemberAccess(ZCC_ExprMemberAccess *dotop, PSymbolTable *symt)
-{
-	PSymbolTable *symtable;
-
-	// TBD: Is it safe to simplify the left side here when not processing a constant?
-	dotop->Left = DoSimplify(dotop->Left, symt);
-
-	if (dotop->Left->Operation == PEX_TypeRef)
-	{ // Type refs can be evaluated now.
-		PType *ref = static_cast<ZCC_ExprTypeRef *>(dotop->Left)->RefType;
-		PSymbol *sym = ref->Symbols.FindSymbolInTable(dotop->Right, symtable);
-		if (sym != nullptr)
+		FScriptPosition::errorout = true;
+		exp = exp->Resolve(ctx);
+		if (exp == nullptr) return false;
+		FScriptPosition::errorout = false;
+		if (!exp->isConstant())
 		{
-			ZCC_Expression *expr = NodeFromSymbol(sym, dotop, symtable);
-			if (expr != nullptr)
-			{
-				return expr;
-			}
+			delete exp;
+			return false;
 		}
+		work->constval = static_cast<FxConstant*>(exp)->GetValue();
+		delete exp;
+		return true;
 	}
-	else if (dotop->Left->Operation == PEX_Super)
+	catch (...)
 	{
-		symt = symt->GetParentTable();
-		if (symt != nullptr)
-		{
-			PSymbol *sym = symt->FindSymbolInTable(dotop->Right, symtable);
-			if (sym != nullptr)
-			{
-				ZCC_Expression *expr = NodeFromSymbol(sym, dotop, symtable);
-				if (expr != nullptr)
-				{
-					return expr;
-				}
-			}
-		}
+		// eat the reported error and treat this as a temorary failure. All unresolved contants will be reported at the end.
+		FScriptPosition::errorout = false;
+		return false;
 	}
-	return dotop;
 }
 
-//==========================================================================
-//
-// ZCCCompiler :: SimplifyFunctionCall
-//
-// This may replace a function call with cast(s), since they look like the
-// same thing to the parser.
-//
-//==========================================================================
-
-ZCC_Expression *ZCCCompiler::SimplifyFunctionCall(ZCC_ExprFuncCall *callop, PSymbolTable *sym)
-{
-	ZCC_FuncParm *parm;
-	int parmcount = 0;
-
-	parm = callop->Parameters;
-	if (parm != NULL)
-	{
-		do
-		{
-			parmcount++;
-			assert(parm->NodeType == AST_FuncParm);
-			parm->Value = DoSimplify(parm->Value, sym);
-			parm = static_cast<ZCC_FuncParm *>(parm->SiblingNext);
-		}
-		while (parm != callop->Parameters);
-	}
-	// Only simplify the 'function' part if we want to retrieve a constant.
-	// This is necessary to evaluate the type casts, but for actual functions
-	// the simplification process is destructive and has to be avoided.
-	if (SimplifyingConstant)
-	{
-		callop->Function = DoSimplify(callop->Function, sym);
-	}
-	// If the left side is a type ref, then this is actually a cast
-	// and not a function call.
-	if (callop->Function->Operation == PEX_TypeRef)
-	{
-		if (parmcount != 1)
-		{
-			Error(callop, "Type cast requires one parameter");
-			callop->ToErrorNode();
-		}
-		else
-		{
-			PType *dest = static_cast<ZCC_ExprTypeRef *>(callop->Function)->RefType;
-			const PType::Conversion *route[CONVERSION_ROUTE_SIZE];
-			int routelen = parm->Value->Type->FindConversion(dest, route, countof(route));
-			if (routelen < 0)
-			{
-				///FIXME: Need real type names
-				Error(callop, "Cannot convert %s to %s", parm->Value->Type->DescriptiveName(), dest->DescriptiveName());
-				callop->ToErrorNode();
-			}
-			else
-			{
-				ZCC_Expression *val = ApplyConversion(parm->Value, route, routelen);
-				assert(val->Type == dest);
-				return val;
-			}
-		}
-	}
-	return callop;
-}
-
-//==========================================================================
-//
-// ZCCCompiler :: PromoteUnary
-//
-// Converts the operand into a format preferred by the operator.
-//
-//==========================================================================
-
-ZCC_OpProto *ZCCCompiler::PromoteUnary(EZCCExprType op, ZCC_Expression *&expr)
-{
-	if (expr->Type == TypeError)
-	{
-		return NULL;
-	}
-	const PType::Conversion *route[CONVERSION_ROUTE_SIZE];
-	int routelen = countof(route);
-	ZCC_OpProto *proto = ZCC_OpInfo[op].FindBestProto(expr->Type, route, routelen);
-
-	if (proto != NULL)
-	{
-		expr = ApplyConversion(expr, route, routelen);
-	}
-	return proto;
-}
-
-//==========================================================================
-//
-// ZCCCompiler :: PromoteBinary
-//
-// Converts the operands into a format (hopefully) compatible with the
-// operator.
-//
-//==========================================================================
-
-ZCC_OpProto *ZCCCompiler::PromoteBinary(EZCCExprType op, ZCC_Expression *&left, ZCC_Expression *&right)
-{
-	// If either operand is of type 'error', the result is also 'error'
-	if (left->Type == TypeError || right->Type == TypeError)
-	{
-		return NULL;
-	}
-	const PType::Conversion *route1[CONVERSION_ROUTE_SIZE], *route2[CONVERSION_ROUTE_SIZE];
-	int route1len = countof(route1), route2len = countof(route2);
-	ZCC_OpProto *proto = ZCC_OpInfo[op].FindBestProto(left->Type, route1, route1len, right->Type, route2, route2len);
-	if (proto != NULL)
-	{
-		left = ApplyConversion(left, route1, route1len);
-		right = ApplyConversion(right, route2, route2len);
-	}
-	return proto;
-}
-
-//==========================================================================
-//
-// ZCCCompiler :: ApplyConversion
-//
-//==========================================================================
-
-ZCC_Expression *ZCCCompiler::ApplyConversion(ZCC_Expression *expr, const PType::Conversion **route, int routelen)
-{
-	for (int i = 0; i < routelen; ++i)
-	{
-		if (expr->Operation != PEX_ConstValue)
-		{
-			expr = AddCastNode(route[i]->TargetType, expr);
-		}
-		else
-		{
-			route[i]->ConvertConstant(static_cast<ZCC_ExprConstant *>(expr), AST.Strings);
-		}
-	}
-	return expr;
-}
-
-//==========================================================================
-//
-// ZCCCompiler :: AddCastNode
-//
-//==========================================================================
-
-ZCC_Expression *ZCCCompiler::AddCastNode(PType *type, ZCC_Expression *expr)
-{
-	assert(expr->Operation != PEX_ConstValue && "Expression must not be constant");
-	// TODO: add a node here
-	return expr;
-}
 
 //==========================================================================
 //
@@ -1293,7 +1095,7 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
 
 		if (field->Type->ArraySize != nullptr)
 		{
-			fieldtype = ResolveArraySize(fieldtype, field->Type->ArraySize, &type->Symbols);
+			fieldtype = ResolveArraySize(fieldtype, field->Type->ArraySize, type);
 		}
 
 		auto name = field->Names;
@@ -1304,7 +1106,7 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
 				auto thisfieldtype = fieldtype;
 				if (name->ArraySize != nullptr)
 				{
-					thisfieldtype = ResolveArraySize(thisfieldtype, name->ArraySize, &type->Symbols);
+					thisfieldtype = ResolveArraySize(thisfieldtype, name->ArraySize, type);
 				}
 				
 				if (varflags & VARF_Native)
@@ -1662,7 +1464,7 @@ PType *ZCCCompiler::ResolveUserType(ZCC_BasicType *type, PSymbolTable *symt)
 //
 //==========================================================================
 
-PType *ZCCCompiler::ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, PSymbolTable *sym)
+PType *ZCCCompiler::ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, PStruct *cls)
 {
 	TArray<ZCC_Expression *> indices;
 
@@ -1674,15 +1476,21 @@ PType *ZCCCompiler::ResolveArraySize(PType *baseType, ZCC_Expression *arraysize,
 		node = static_cast<ZCC_Expression*>(node->SiblingNext);
 	} while (node != arraysize);
 
+
+	FCompileContext ctx(cls, false);
 	for (auto node : indices)
 	{
-		auto val = Simplify(node, sym, true);
-		if (val->Operation != PEX_ConstValue || !val->Type->IsA(RUNTIME_CLASS(PInt)))
+		// There is no float->int casting here.
+		FxExpression *ex = ConvertNode(node);
+		ex = ex->Resolve(ctx);
+
+		if (ex == nullptr) return TypeError;
+		if (!ex->isConstant() || !ex->ValueType->IsA(RUNTIME_CLASS(PInt)))
 		{
 			Error(arraysize, "Array index must be an integer constant");
 			return TypeError;
 		}
-		int size = static_cast<ZCC_ExprConstant *>(val)->IntVal;
+		int size = static_cast<FxConstant*>(ex)->GetValue().GetInt();
 		if (size < 1)
 		{
 			Error(arraysize, "Array size must be positive");
@@ -1693,78 +1501,6 @@ PType *ZCCCompiler::ResolveArraySize(PType *baseType, ZCC_Expression *arraysize,
 	return baseType;
 }
 
-//==========================================================================
-//
-// ZCCCompiler :: GetInt - Input must be a constant expression
-//
-//==========================================================================
-
-int ZCCCompiler::GetInt(ZCC_Expression *expr)
-{
-	if (expr->Type == TypeError)
-	{
-		return 0;
-	}
-	const PType::Conversion *route[CONVERSION_ROUTE_SIZE];
-	int routelen = expr->Type->FindConversion(TypeSInt32, route, countof(route));
-	if (routelen < 0)
-	{
-		Error(expr, "Cannot convert to integer");
-		return 0;
-	}
-	else
-	{
-		if (expr->Type->IsKindOf(RUNTIME_CLASS(PFloat)))
-		{
-			Warn(expr, "Truncation of floating point value");
-		}
-		auto ex = static_cast<ZCC_ExprConstant *>(ApplyConversion(expr, route, routelen));
-		return ex->IntVal;
-	}
-}
-
-double ZCCCompiler::GetDouble(ZCC_Expression *expr)
-{
-	if (expr->Type == TypeError)
-	{
-		return 0;
-	}
-	const PType::Conversion *route[CONVERSION_ROUTE_SIZE];
-	int routelen = expr->Type->FindConversion(TypeFloat64, route, countof(route));
-	if (routelen < 0)
-	{
-		Error(expr, "Cannot convert to float");
-		return 0;
-	}
-	else
-	{
-		auto ex = static_cast<ZCC_ExprConstant *>(ApplyConversion(expr, route, routelen));
-		return ex->DoubleVal;
-	}
-}
-
-const char *ZCCCompiler::GetString(ZCC_Expression *expr, bool silent)
-{
-	if (expr->Type == TypeError)
-	{
-		return nullptr;
-	}
-	else if (expr->Type->IsKindOf(RUNTIME_CLASS(PString)))
-	{
-		return static_cast<ZCC_ExprConstant *>(expr)->StringVal->GetChars();
-	}
-	else if (expr->Type->IsKindOf(RUNTIME_CLASS(PName)))
-	{
-		// Ugh... What a mess...
-		return FName(ENamedName(static_cast<ZCC_ExprConstant *>(expr)->IntVal)).GetChars();
-	}
-	else
-	{
-		if (!silent) Error(expr, "Cannot convert to string");
-		return nullptr;
-	}
-}
-
 //==========================================================================
 //
 // Parses an actor property's parameters and calls the handler
@@ -1787,16 +1523,22 @@ void ZCCCompiler::DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *proper
 			Error(property, "%s: arguments missing", prop->name);
 			return;
 		}
-		property->Values = Simplify(property->Values, &bag.Info->Symbols, true);	// need to do this before the loop so that we can find the head node again.
 		const char * p = prop->params;
 		auto exp = property->Values;
 
+		FCompileContext ctx(bag.Info, false);
 		while (true)
 		{
 			FPropParam conv;
 			FPropParam pref;
 
-			if (exp->NodeType != AST_ExprConstant)
+			FxExpression *ex = ConvertNode(exp);
+			ex = ex->Resolve(ctx);
+			if (ex == nullptr)
+			{
+				return;
+			}
+			else if (!ex->isConstant())
 			{
 				// If we get TypeError, there has already been a message from deeper down so do not print another one.
 				if (exp->Type != TypeError) Error(exp, "%s: non-constant parameter", prop->name);
@@ -1809,7 +1551,7 @@ void ZCCCompiler::DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *proper
 			{
 
 			case 'X':	// Expression in parentheses or number. We only support the constant here. The function will have to be handled by a separate property to get past the parser.
-				conv.i = GetInt(exp);
+				conv.i = GetIntConst(ex, ctx);
 				params.Push(conv);
 				conv.exp = nullptr;
 				break;
@@ -1817,15 +1559,15 @@ void ZCCCompiler::DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *proper
 			case 'I':
 			case 'M':	// special case for morph styles in DECORATE . This expression-aware parser will not need this.
 			case 'N':	// special case for thing activations in DECORATE. This expression-aware parser will not need this.
-				conv.i = GetInt(exp);
+				conv.i = GetIntConst(ex, ctx);
 				break;
 
 			case 'F':
-				conv.d = GetDouble(exp);
+				conv.d = GetFloatConst(ex, ctx);
 				break;
 
 			case 'Z':	// an optional string. Does not allow any numeric value.
-				if (!GetString(exp, true))
+				if (ex->ValueType != TypeString)
 				{
 					// apply this expression to the next argument on the list.
 					params.Push(conv);
@@ -1833,21 +1575,21 @@ void ZCCCompiler::DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *proper
 					p++;
 					continue;
 				}
-				conv.s = GetString(exp);
+				conv.s = GetStringConst(ex, ctx);
 				break;
 
 			case 'C':	// this parser accepts colors only in string form.
 				pref.i = 1;
 			case 'S':
 			case 'T': // a filtered string (ZScript only parses filtered strings so there's nothing to do here.)
-				conv.s = GetString(exp);
+				conv.s = GetStringConst(ex, ctx);
 				break;
 
 			case 'L':	// Either a number or a list of strings
-				if (!GetString(exp, true))
+				if (ex->ValueType != TypeString)
 				{
 					pref.i = 0;
-					conv.i = GetInt(exp);
+					conv.i = GetIntConst(ex, ctx);
 				}
 				else
 				{
@@ -1857,13 +1599,13 @@ void ZCCCompiler::DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *proper
 
 					do
 					{
-						conv.s = GetString(exp);
+						conv.s = GetStringConst(ex, ctx);
 						if (conv.s != nullptr)
 						{
 							params.Push(conv);
 							params[0].i++;
 						}
-						exp = Simplify(static_cast<ZCC_Expression *>(exp->SiblingNext), &bag.Info->Symbols, true);
+						exp = static_cast<ZCC_Expression *>(exp->SiblingNext);
 					} while (exp != property->Values);
 					goto endofparm;
 				}
@@ -1881,7 +1623,7 @@ void ZCCCompiler::DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *proper
 			}
 			params.Push(conv);
 			params[0].i++;
-			exp = Simplify(static_cast<ZCC_Expression *>(exp->SiblingNext), &bag.Info->Symbols, true);
+			exp = static_cast<ZCC_Expression *>(exp->SiblingNext);
 		endofparm:
 			p++;
 			// Skip the DECORATE 'no comma' marker
@@ -1950,8 +1692,8 @@ void ZCCCompiler::DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *prop
 		return;
 	}
 
-	auto values = Simplify(property->Values, &bag.Info->Symbols, true);	// need to do this before the loop so that we can find the head node again.
-	auto exp = values;
+	auto exp = property->Values;
+	FCompileContext ctx(bag.Info, false);
 	for (auto f : prop->Variables)
 	{
 		void *addr;
@@ -1965,25 +1707,38 @@ void ZCCCompiler::DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *prop
 			addr = ((char*)defaults) + f->Offset;
 		}
 
+		FxExpression *ex = ConvertNode(exp);
+		ex = ex->Resolve(ctx);
+		if (ex == nullptr)
+		{
+			return;
+		}
+		else if (!ex->isConstant())
+		{
+			// If we get TypeError, there has already been a message from deeper down so do not print another one.
+			if (exp->Type != TypeError) Error(exp, "%s: non-constant parameter", prop->SymbolName.GetChars());
+			return;
+		}
+
 		if (f->Type == TypeBool)
 		{
-			static_cast<PBool*>(f->Type)->SetValue(addr, !!GetInt(exp));
+			static_cast<PBool*>(f->Type)->SetValue(addr, !!GetIntConst(ex, ctx));
 		}
 		if (f->Type->IsKindOf(RUNTIME_CLASS(PInt)))
 		{
-			static_cast<PInt*>(f->Type)->SetValue(addr, GetInt(exp));
+			static_cast<PInt*>(f->Type)->SetValue(addr, GetIntConst(ex, ctx));
 		}
 		else if (f->Type->IsKindOf(RUNTIME_CLASS(PFloat)))
 		{
-			static_cast<PFloat*>(f->Type)->SetValue(addr, GetDouble(exp));
+			static_cast<PFloat*>(f->Type)->SetValue(addr, GetFloatConst(ex, ctx));
 		}
 		else if (f->Type->IsKindOf(RUNTIME_CLASS(PString)))
 		{
-			*(FString*)addr = GetString(exp);
+			*(FString*)addr = GetStringConst(ex, ctx);
 		}
 		else if (f->Type->IsKindOf(RUNTIME_CLASS(PClassPointer)))
 		{
-			auto clsname = GetString(exp);
+			auto clsname = GetStringConst(ex, ctx);
 			auto cls = PClass::FindClass(clsname);
 			if (cls == nullptr)
 			{
@@ -1991,7 +1746,7 @@ void ZCCCompiler::DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *prop
 			}
 			else if (!cls->IsDescendantOf(static_cast<PClassPointer*>(f->Type)->ClassRestriction))
 			{
-				Error(property, "class %s is not compatible with property type %s", clsname, static_cast<PClassPointer*>(f->Type)->ClassRestriction->TypeName.GetChars());
+				Error(property, "class %s is not compatible with property type %s", clsname.GetChars(), static_cast<PClassPointer*>(f->Type)->ClassRestriction->TypeName.GetChars());
 			}
 			*(PClass**)addr = cls;
 		}
@@ -2772,7 +2527,7 @@ void ZCCCompiler::CompileStates()
 					{
 						state.sprite = GetSpriteIndex(sl->Sprite->GetChars());
 					}
-					// It is important to call CheckRandom before Simplify, because Simplify will resolve the function's name to nonsense
+					FCompileContext ctx(c->Type(), false);
 					if (CheckRandom(sl->Duration))
 					{
 						auto func = static_cast<ZCC_ExprFuncCall *>(sl->Duration);
@@ -2780,26 +2535,16 @@ void ZCCCompiler::CompileStates()
 						{
 							Error(sl, "Random duration requires exactly 2 parameters");
 						}
-						auto p1 = Simplify(func->Parameters->Value, &c->Type()->Symbols, true);
-						auto p2 = Simplify(static_cast<ZCC_FuncParm *>(func->Parameters->SiblingNext)->Value, &c->Type()->Symbols, true);
-						int v1 = GetInt(p1);
-						int v2 = GetInt(p2);
+						int v1 = IntConstFromNode(func->Parameters->Value, c->Type());
+						int v2 = IntConstFromNode(static_cast<ZCC_FuncParm *>(func->Parameters->SiblingNext)->Value, c->Type());
 						if (v1 > v2) std::swap(v1, v2);
 						state.Tics = (int16_t)clamp<int>(v1, 0, INT16_MAX);
 						state.TicRange = (uint16_t)clamp<int>(v2 - v1, 0, UINT16_MAX);
 					}
 					else
 					{
-						auto duration = Simplify(sl->Duration, &c->Type()->Symbols, true);
-						if (duration->Operation == PEX_ConstValue)
-						{
-							state.Tics = (int16_t)clamp<int>(GetInt(duration), -1, INT16_MAX);
-							state.TicRange = 0;
-						}
-						else
-						{
-							Error(sl, "Duration is not a constant");
-						}
+						state.Tics = (int16_t)IntConstFromNode(sl->Duration, c->Type());
+						state.TicRange = 0;
 					}
 					if (sl->bBright) state.StateFlags |= STF_FULLBRIGHT;
 					if (sl->bFast) state.StateFlags |= STF_FAST;
@@ -2815,18 +2560,8 @@ void ZCCCompiler::CompileStates()
 					}
 					if (sl->Offset != nullptr)
 					{
-						auto o1 = static_cast<ZCC_Expression *>(Simplify(sl->Offset, &c->Type()->Symbols, true));
-						auto o2 = static_cast<ZCC_Expression *>(Simplify(static_cast<ZCC_Expression *>(o1->SiblingNext), &c->Type()->Symbols, true));
-
-						if (o1->Operation != PEX_ConstValue || o2->Operation != PEX_ConstValue)
-						{
-							Error(o1, "State offsets must be constant");
-						}
-						else
-						{
-							state.Misc1 = GetInt(o1);
-							state.Misc2 = GetInt(o2);
-						}
+						state.Misc1 = IntConstFromNode(sl->Offset, c->Type());
+						state.Misc2 = IntConstFromNode(static_cast<ZCC_Expression *>(sl->Offset->SiblingNext), c->Type());
 					}
 #ifdef DYNLIGHT
 					if (sl->Lights != nullptr)
@@ -2834,7 +2569,7 @@ void ZCCCompiler::CompileStates()
 						auto l = sl->Lights;
 						do
 						{
-							AddStateLight(&state, GetString(l));
+							AddStateLight(&state, StringConstFromNode(l, c->Type()));
 							l = static_cast<decltype(l)>(l->SiblingNext);
 						} while (l != sl->Lights);
 					}
@@ -2875,23 +2610,15 @@ void ZCCCompiler::CompileStates()
 					statename.Truncate((long)statename.Len() - 1);	// remove the last '.' in the label name
 					if (sg->Offset != nullptr)
 					{
-						auto ofs = Simplify(sg->Offset, &c->Type()->Symbols, true);
-						if (ofs->Operation != PEX_ConstValue)
+						int offset = IntConstFromNode(sg->Offset, c->Type());
+						if (offset < 0)
 						{
-							Error(sg, "Constant offset expected for GOTO");
+							Error(sg, "GOTO offset must be positive");
+							offset = 0;
 						}
-						else
+						if (offset > 0)
 						{
-							int offset = GetInt(ofs);
-							if (offset < 0)
-							{
-								Error(sg, "GOTO offset must be positive");
-								offset = 0;
-							}
-							if (offset > 0)
-							{
-								statename.AppendFormat("+%d", offset);
-							}
+							statename.AppendFormat("+%d", offset);
 						}
 					}
 					if (!statedef.SetGotoLabel(statename))
@@ -3292,7 +3019,7 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
 
 		if (loc->Type->ArraySize != nullptr)
 		{
-			ztype = ResolveArraySize(ztype, loc->Type->ArraySize, &ConvertClass->Symbols);
+			ztype = ResolveArraySize(ztype, loc->Type->ArraySize, ConvertClass);
 		}
 
 		do
@@ -3301,7 +3028,7 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
 
 			if (node->ArraySize != nullptr)
 			{
-				type = ResolveArraySize(ztype, node->ArraySize, &ConvertClass->Symbols);
+				type = ResolveArraySize(ztype, node->ArraySize, ConvertClass);
 			}
 			else
 			{
diff --git a/src/scripting/zscript/zcc_compile.h b/src/scripting/zscript/zcc_compile.h
index 8675b1af7..79dc3b135 100644
--- a/src/scripting/zscript/zcc_compile.h
+++ b/src/scripting/zscript/zcc_compile.h
@@ -2,6 +2,7 @@
 #define ZCC_COMPILE_H
 
 #include <memory>
+#include "codegeneration/codegen.h"
 
 struct Baggage;
 struct FPropertyInfo;
@@ -77,7 +78,9 @@ struct ZCC_PropertyWork
 struct ZCC_ConstantWork
 {
 	ZCC_ConstantDef *node;
-	PSymbolTable *outputtable;
+	PStruct *cls;
+	PSymbolTable *Outputtable;
+	ExpVal constval;
 };
 
 class ZCCCompiler
@@ -88,14 +91,16 @@ public:
 	int Compile();
 
 private:
+	int IntConstFromNode(ZCC_TreeNode *node, PStruct *cls);
+	FString ZCCCompiler::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();
 	void CreateClassTypes();
-	void CopyConstants(TArray<ZCC_ConstantWork> &dest, TArray<ZCC_ConstantDef*> &Constants, PSymbolTable *ot);
+	void CopyConstants(TArray<ZCC_ConstantWork> &dest, TArray<ZCC_ConstantDef*> &Constants, PStruct *cls, PSymbolTable *ot);
 	void CompileAllConstants();
 	void AddConstant(ZCC_ConstantWork &constant);
-	bool CompileConstant(ZCC_ConstantDef *def, PSymbolTable *Symbols);
+	bool CompileConstant(ZCC_ConstantWork *def);
 
 	void CompileAllFields();
 	bool CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fields, PClass *Outer, PSymbolTable *TreeNodes, bool forstruct, bool hasnativechildren = false);
@@ -103,7 +108,7 @@ private:
 	bool CompileProperties(PClass *type, TArray<ZCC_Property *> &Properties, FName prefix);
 	FString FlagsToString(uint32_t flags);
 	PType *DetermineType(PType *outertype, ZCC_TreeNode *field, FName name, ZCC_Type *ztype, bool allowarraytypes, bool formember);
-	PType *ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, PSymbolTable *sym);
+	PType *ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, PStruct *cls);
 	PType *ResolveUserType(ZCC_BasicType *type, PSymbolTable *sym);
 
 	void InitDefaults();
@@ -111,9 +116,6 @@ private:
 	void ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *flg, Baggage &bag);
 	void DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *pex, AActor *defaults, Baggage &bag);
 	void DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *pex, AActor *defaults, Baggage &bag);
-	int GetInt(ZCC_Expression *expr);
-	double GetDouble(ZCC_Expression *expr);
-	const char *GetString(ZCC_Expression *expr, bool silent = false);
 	void CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool forclass);
 
 	void InitFunctions();
@@ -128,18 +130,6 @@ private:
 
 	PSymbolTreeNode *AddTreeNode(FName name, ZCC_TreeNode *node, PSymbolTable *treenodes, bool searchparents = false);
 
-	ZCC_Expression *Simplify(ZCC_Expression *root, PSymbolTable *Symbols, bool wantconstant);
-	ZCC_Expression *DoSimplify(ZCC_Expression *root, PSymbolTable *Symbols);
-	ZCC_Expression *SimplifyUnary(ZCC_ExprUnary *unary, PSymbolTable *Symbols);
-	ZCC_Expression *SimplifyBinary(ZCC_ExprBinary *binary, PSymbolTable *Symbols);
-	ZCC_Expression *SimplifyMemberAccess(ZCC_ExprMemberAccess *dotop, PSymbolTable *Symbols);
-	ZCC_Expression *SimplifyFunctionCall(ZCC_ExprFuncCall *callop, PSymbolTable *Symbols);
-	ZCC_OpProto *PromoteUnary(EZCCExprType op, ZCC_Expression *&expr);
-	ZCC_OpProto *PromoteBinary(EZCCExprType op, ZCC_Expression *&left, ZCC_Expression *&right);
-
-	ZCC_Expression *ApplyConversion(ZCC_Expression *expr, const PType::Conversion **route, int routelen);
-	ZCC_Expression *AddCastNode(PType *type, ZCC_Expression *expr);
-
 	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);
diff --git a/src/scripting/zscript/zcc_expr.cpp b/src/scripting/zscript/zcc_expr.cpp
deleted file mode 100644
index 0c2c6baee..000000000
--- a/src/scripting/zscript/zcc_expr.cpp
+++ /dev/null
@@ -1,530 +0,0 @@
-/*
-** zcc_expr.cpp
-**
-**---------------------------------------------------------------------------
-** Copyright -2016 Randy Heit
-** All rights reserved.
-**
-** Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions
-** are met:
-**
-** 1. Redistributions of source code must retain the above copyright
-**    notice, this list of conditions and the following disclaimer.
-** 2. Redistributions in binary form must reproduce the above copyright
-**    notice, this list of conditions and the following disclaimer in the
-**    documentation and/or other materials provided with the distribution.
-** 3. The name of the author may not be used to endorse or promote products
-**    derived from this software without specific prior written permission.
-**
-** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-**---------------------------------------------------------------------------
-**
-*/
-
-#include <math.h>
-#include "dobject.h"
-#include "sc_man.h"
-#include "c_console.h"
-#include "c_dispatch.h"
-#include "w_wad.h"
-#include "cmdlib.h"
-#include "m_alloc.h"
-#include "zcc_parser.h"
-#include "templates.h"
-#include "math/cmath.h"
-
-#define luai_nummod(a,b)        ((a) - floor((a)/(b))*(b))
-
-static void FtoD(ZCC_ExprConstant *expr, FSharedStringArena &str_arena);
-
-ZCC_OpInfoType ZCC_OpInfo[PEX_COUNT_OF] =
-{
-#define xx(a,z)	{ #a, NULL },
-#include "zcc_exprlist.h"
-};
-
-// Structures used for initializing operator overloads
-struct OpProto1
-{
-	EZCCExprType Op;
-	PType **Type;
-	EvalConst1op EvalConst;
-};
-
-struct OpProto2
-{
-	EZCCExprType Op;
-	PType **Res, **Ltype, **Rtype;
-	EvalConst2op EvalConst;
-};
-
-static struct FreeOpInfoProtos
-{
-	~FreeOpInfoProtos()
-	{
-		for (size_t i = 0; i < countof(ZCC_OpInfo); ++i)
-		{
-			ZCC_OpInfo[i].FreeAllProtos();
-		}
-	}
-} ProtoFreeer;
-
-void ZCC_OpInfoType::FreeAllProtos()
-{
-	for (ZCC_OpProto *proto = Protos, *next = NULL; proto != NULL; proto = next)
-	{
-		next = proto->Next;
-		delete proto;
-	}
-	Protos = NULL;
-}
-
-void ZCC_OpInfoType::AddProto(PType *res, PType *optype, EvalConst1op evalconst)
-{
-	ZCC_OpProto *proto = new ZCC_OpProto(res, optype, NULL);
-	proto->EvalConst1 = evalconst;
-	proto->Next = Protos;
-	Protos = proto;
-}
-
-void ZCC_OpInfoType::AddProto(PType *res, PType *ltype, PType *rtype, EvalConst2op evalconst)
-{
-	assert(ltype != NULL);
-	ZCC_OpProto *proto = new ZCC_OpProto(res, ltype, rtype);
-	proto->EvalConst2 = evalconst;
-	proto->Next = Protos;
-	Protos = proto;
-}
-
-//==========================================================================
-//
-// ZCC_OpInfoType :: FindBestProto (Unary)
-//
-// Finds the "best" prototype for this operand type. Best is defined as the
-// one that requires the fewest conversions. Also returns the conversion
-// route necessary to get from the input type to the desired type.
-//
-//==========================================================================
-
-ZCC_OpProto *ZCC_OpInfoType::FindBestProto(PType *optype, const PType::Conversion **route, int &numslots)
-{
-	assert(optype != NULL);
-
-	const PType::Conversion *routes[2][CONVERSION_ROUTE_SIZE];
-	const PType::Conversion **best_route = NULL;
-	int cur_route = 0;
-	ZCC_OpProto *best_proto = NULL;
-	int best_dist = INT_MAX;
-
-	// Find the best prototype.
-	for (ZCC_OpProto *proto = Protos; best_dist != 0 && proto != NULL; proto = proto->Next)
-	{
-		if (proto->Type2 != NULL)
-		{ // Not a unary prototype.
-			continue;
-		}
-		int dist = optype->FindConversion(proto->Type1, routes[cur_route], CONVERSION_ROUTE_SIZE);
-		if (dist >= 0 && dist < best_dist)
-		{
-			best_dist = dist;
-			best_proto = proto;
-			best_route = routes[cur_route];
-			cur_route ^= 1;
-		}
-	}
-	// Copy best conversion route to the caller's array.
-	if (best_route != NULL && route != NULL && numslots > 0)
-	{
-		numslots = MIN(numslots, best_dist);
-		if (numslots > 0)
-		{
-			memcpy(route, best_route, sizeof(*route) * numslots);
-		}
-	}
-	return best_proto;
-}
-
-//==========================================================================
-//
-// ZCC_OpInfoType :: FindBestProto (Binary)
-//
-// Finds the "best" prototype for the given operand types. Here, best is
-// defined as the one that requires the fewest conversions for *one* of the
-// operands. For prototypes with matching distances, the first one found
-// is used. ZCC_InitOperators() initializes the prototypes in order such
-// that this will result in the precedences: double > uint > int
-//
-//==========================================================================
-
-ZCC_OpProto *ZCC_OpInfoType::FindBestProto(
-	PType *left, const PType::Conversion **route1, int &numslots1,
-	PType *right, const PType::Conversion **route2, int &numslots2)
-{
-	assert(left != NULL && right != NULL);
-
-	const PType::Conversion *routes[2][2][CONVERSION_ROUTE_SIZE];
-	const PType::Conversion **best_route1 = NULL, **best_route2 = NULL;
-	int cur_route1 = 0, cur_route2 = 0;
-	int best_dist1 = INT_MAX, best_dist2 = INT_MAX;
-
-	ZCC_OpProto *best_proto = NULL;
-	int best_low_dist = INT_MAX;
-
-	for (ZCC_OpProto *proto = Protos; best_low_dist != 0 && proto != NULL; proto = proto->Next)
-	{
-		if (proto->Type2 == NULL)
-		{ // Not a binary prototype
-			continue;
-		}
-		int dist1 = left->FindConversion(proto->Type1, routes[0][cur_route1], CONVERSION_ROUTE_SIZE);
-		int dist2 = right->FindConversion(proto->Type2, routes[1][cur_route2], CONVERSION_ROUTE_SIZE);
-		if (dist1 < 0 || dist2 < 0)
-		{ // one or both operator types are unreachable
-			continue;
-		}
-		// Do not count F32->F64 conversions in the distance comparisons. If we do, then
-		// [[float32 (op) int]] will choose the integer version instead of the floating point
-		// version, which we do not want.
-		int test_dist1 = dist1, test_dist2 = dist2;
-		if (test_dist1 > 0 && routes[0][cur_route1][0]->ConvertConstant == FtoD)
-		{
-			test_dist1--;
-		}
-		if (test_dist2 > 0 && routes[1][cur_route2][0]->ConvertConstant == FtoD)
-		{
-			test_dist2--;
-		}
-		int dist = MIN(test_dist1, test_dist2);
-		if (dist < best_low_dist)
-		{
-			best_low_dist = dist;
-			best_proto = proto;
-			best_dist1 = dist1;
-			best_dist2 = dist2;
-			best_route1 = routes[0][cur_route1];
-			best_route2 = routes[1][cur_route2];
-			cur_route1 ^= 1;
-			cur_route2 ^= 1;
-		}
-	}
-	// Copy best conversion route to the caller's arrays.
-	if (best_route1 != NULL && route1 != NULL && numslots1 > 0)
-	{
-		numslots1 = MIN(numslots1, best_dist1);
-		if (numslots1 > 0)
-		{
-			memcpy(route1, best_route1, sizeof(*route1) * numslots1);
-		}
-	}
-	if (best_route2 != NULL && route2 != NULL && numslots2 > 0)
-	{
-		numslots2 = MIN(numslots2, best_dist2);
-		if (numslots2 > 0)
-		{
-			memcpy(route2, best_route2, sizeof(*route2) * numslots2);
-		}
-	}
-	return best_proto;
-}
-
-static ZCC_ExprConstant *EvalIdentity(ZCC_ExprConstant *val)
-{
-	return val;
-}
-
-
-static ZCC_ExprConstant *EvalConcat(ZCC_ExprConstant *l, ZCC_ExprConstant *r, FSharedStringArena &strings)
-{
-	FString str = *l->StringVal + *r->StringVal;
-	l->StringVal = strings.Alloc(str);
-	return l;
-}
-
-static ZCC_ExprConstant *EvalLTGTEQSInt32(ZCC_ExprConstant *l, ZCC_ExprConstant *r, FSharedStringArena &)
-{
-	l->IntVal = l->IntVal < r->IntVal ? -1 : l->IntVal == r->IntVal ? 0 : 1;
-	return l;
-}
-
-static ZCC_ExprConstant *EvalLTGTEQUInt32(ZCC_ExprConstant *l, ZCC_ExprConstant *r, FSharedStringArena &)
-{
-	l->IntVal = l->UIntVal < r->UIntVal ? -1 : l->UIntVal == r->UIntVal ? 0 : 1;
-	l->Type = TypeSInt32;
-	return l;
-}
-
-static ZCC_ExprConstant *EvalLTGTEQFloat64(ZCC_ExprConstant *l, ZCC_ExprConstant *r, FSharedStringArena &)
-{
-	l->IntVal = l->DoubleVal < r->DoubleVal ? -1 : l->DoubleVal == r->DoubleVal ? 0 : 1;
-	l->Type = TypeSInt32;
-	return l;
-}
-
-void ZCC_InitOperators()
-{
-	// Prototypes are added from lowest to highest conversion precedence.
-
-	// Unary operators
-	static const OpProto1 UnaryOpInit[] =
-	{
-		{ PEX_PostInc	 , (PType **)&TypeSInt32,  EvalIdentity },
-		{ PEX_PostInc	 , (PType **)&TypeUInt32,  EvalIdentity },
-		{ PEX_PostInc	 , (PType **)&TypeFloat64, EvalIdentity },
-
-		{ PEX_PostDec	 , (PType **)&TypeSInt32,  EvalIdentity },
-		{ PEX_PostDec	 , (PType **)&TypeUInt32,  EvalIdentity },
-		{ PEX_PostDec	 , (PType **)&TypeFloat64, EvalIdentity },
-
-		{ PEX_PreInc	 , (PType **)&TypeSInt32,  [](auto *val) { val->IntVal += 1; return val; } },
-		{ PEX_PreInc	 , (PType **)&TypeUInt32,  [](auto *val) { val->UIntVal += 1; return val; } },
-		{ PEX_PreInc	 , (PType **)&TypeFloat64, [](auto *val) { val->DoubleVal += 1; return val; } },
-
-		{ PEX_PreDec	 , (PType **)&TypeSInt32,  [](auto *val) { val->IntVal -= 1; return val; } },
-		{ PEX_PreDec	 , (PType **)&TypeUInt32,  [](auto *val) { val->UIntVal -= 1; return val; } },
-		{ PEX_PreDec	 , (PType **)&TypeFloat64, [](auto *val) { val->DoubleVal -= 1; return val; } },
-
-		{ PEX_Negate	 , (PType **)&TypeSInt32,  [](auto *val) { val->IntVal = -val->IntVal; return val; } },
-		{ PEX_Negate	 , (PType **)&TypeFloat64, [](auto *val) { val->DoubleVal = -val->DoubleVal; return val; } },
-
-		{ PEX_AntiNegate , (PType **)&TypeSInt32,  EvalIdentity },
-		{ PEX_AntiNegate , (PType **)&TypeUInt32,  EvalIdentity },
-		{ PEX_AntiNegate , (PType **)&TypeFloat64, EvalIdentity },
-
-		{ PEX_BitNot	 , (PType **)&TypeSInt32,  [](auto *val) { val->IntVal = ~val->IntVal; return val; } },
-		{ PEX_BitNot	 , (PType **)&TypeUInt32,  [](auto *val) { val->UIntVal = ~val->UIntVal; return val; } },
-
-		{ PEX_BoolNot	 , (PType **)&TypeBool,    [](auto *val) { val->IntVal = !val->IntVal; return val; } },
-	};
-	for (size_t i = 0; i < countof(UnaryOpInit); ++i)
-	{
-		ZCC_OpInfo[UnaryOpInit[i].Op].AddProto(*UnaryOpInit[i].Type, *UnaryOpInit[i].Type, UnaryOpInit[i].EvalConst);
-	}
-
-	// Binary operators
-	static const OpProto2 BinaryOpInit[] =
-	{
-		{ PEX_Add		 , (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  [](auto *l, auto *r, auto &) { l->IntVal += r->IntVal; return l; } },
-		{ PEX_Add		 , (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  [](auto *l, auto *r, auto &) { l->UIntVal += r->UIntVal; return l; } },
-		{ PEX_Add		 , (PType **)&TypeFloat64, (PType **)&TypeFloat64, (PType **)&TypeFloat64, [](auto *l, auto *r, auto &) { l->DoubleVal += r->DoubleVal; return l; } },
-
-		{ PEX_Sub		 , (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  [](auto *l, auto *r, auto &) { l->IntVal -= r->IntVal; return l; } },
-		{ PEX_Sub		 , (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  [](auto *l, auto *r, auto &) { l->UIntVal -= r->UIntVal; return l; } },
-		{ PEX_Sub		 , (PType **)&TypeFloat64, (PType **)&TypeFloat64, (PType **)&TypeFloat64, [](auto *l, auto *r, auto &) { l->DoubleVal -= r->DoubleVal; return l; } },
-
-		{ PEX_Mul		 , (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  [](auto *l, auto *r, auto &) { l->IntVal *= r->IntVal; return l; } },
-		{ PEX_Mul		 , (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  [](auto *l, auto *r, auto &) { l->UIntVal *= r->UIntVal; return l; } },
-		{ PEX_Mul		 , (PType **)&TypeFloat64, (PType **)&TypeFloat64, (PType **)&TypeFloat64, [](auto *l, auto *r, auto &) { l->DoubleVal *= r->DoubleVal; return l; } },
-
-		{ PEX_Div		 , (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  [](auto *l, auto *r, auto &) { l->IntVal /= r->IntVal; return l; } },
-		{ PEX_Div		 , (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  [](auto *l, auto *r, auto &) { l->UIntVal /= r->UIntVal; return l; } },
-		{ PEX_Div		 , (PType **)&TypeFloat64, (PType **)&TypeFloat64, (PType **)&TypeFloat64, [](auto *l, auto *r, auto &) { l->DoubleVal /= r->DoubleVal; return l; } },
-
-		{ PEX_Mod		 , (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  [](auto *l, auto *r, auto &) { l->IntVal %= r->IntVal; return l; } },
-		{ PEX_Mod		 , (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  [](auto *l, auto *r, auto &) { l->UIntVal %= r->UIntVal; return l; } },
-		{ PEX_Mod		 , (PType **)&TypeFloat64, (PType **)&TypeFloat64, (PType **)&TypeFloat64, [](auto *l, auto *r, auto &) { l->DoubleVal = luai_nummod(l->DoubleVal, r->DoubleVal); return l; } },
-
-		{ PEX_Pow		 , (PType **)&TypeFloat64, (PType **)&TypeFloat64, (PType **)&TypeFloat64, [](auto *l, auto *r, auto &) { l->DoubleVal = g_pow(l->DoubleVal, r->DoubleVal); return l; } },
-
-		{ PEX_Concat	 , (PType **)&TypeString,  (PType **)&TypeString,  (PType **)&TypeString,  EvalConcat },
-
-		{ PEX_BitAnd	 , (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  [](auto *l, auto *r, auto &) { l->IntVal &= r->IntVal; return l; } },
-		{ PEX_BitAnd	 , (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  [](auto *l, auto *r, auto &) { l->UIntVal &= r->UIntVal; return l; } },
-
-		{ PEX_BitOr		 , (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  [](auto *l, auto *r, auto &) { l->IntVal |= r->IntVal; return l; } },
-		{ PEX_BitOr		 , (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  [](auto *l, auto *r, auto &) { l->UIntVal |= r->UIntVal; return l; } },
-
-		{ PEX_BitXor	 , (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  [](auto *l, auto *r, auto &) { l->IntVal ^= r->IntVal; return l; } },
-		{ PEX_BitXor	 , (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  [](auto *l, auto *r, auto &) { l->UIntVal ^= r->UIntVal; return l; } },
-
-		{ PEX_BoolAnd	 , (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  [](auto *l, auto *r, auto &) { l->IntVal = l->IntVal && r->IntVal; l->Type = TypeBool; return l; } },
-		{ PEX_BoolAnd	 , (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  [](auto *l, auto *r, auto &) { l->IntVal = l->UIntVal && r->UIntVal; l->Type = TypeBool; return l;  } },
-
-		{ PEX_BoolOr	 , (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  [](auto *l, auto *r, auto &) { l->IntVal = l->IntVal || r->IntVal; l->Type = TypeBool; return l; } },
-		{ PEX_BoolOr	 , (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  [](auto *l, auto *r, auto &) { l->IntVal = l->UIntVal || r->UIntVal; l->Type = TypeBool; return l; } },
-
-		{ PEX_LeftShift  , (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  (PType **)&TypeUInt32,  [](auto *l, auto *r, auto &) { l->IntVal <<= r->UIntVal; return l; } },
-		{ PEX_LeftShift  , (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  [](auto *l, auto *r, auto &) { l->UIntVal <<= r->UIntVal; return l; } },
-
-		{ PEX_RightShift , (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  (PType **)&TypeUInt32,  [](auto *l, auto *r, auto &) { l->IntVal >>= r->UIntVal; return l; } },
-		{ PEX_RightShift , (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  [](auto *l, auto *r, auto &) { l->UIntVal >>= r->UIntVal; return l; } },
-
-		{ PEX_LT		 , (PType **)&TypeBool,    (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  [](auto *l, auto *r, auto &) { l->IntVal = l->IntVal < r->IntVal; l->Type = TypeBool; return l; } },
-		{ PEX_LT		 , (PType **)&TypeBool,    (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  [](auto *l, auto *r, auto &) { l->IntVal = l->UIntVal < r->UIntVal; l->Type = TypeBool; return l; } },
-		{ PEX_LT		 , (PType **)&TypeBool,    (PType **)&TypeFloat64, (PType **)&TypeFloat64, [](auto *l, auto *r, auto &) { l->IntVal = l->DoubleVal < r->DoubleVal; l->Type = TypeBool; return l; } },
-
-		{ PEX_LTEQ		 , (PType **)&TypeBool,    (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  [](auto *l, auto *r, auto &) { l->IntVal = l->IntVal <= r->IntVal; l->Type = TypeBool; return l; } },
-		{ PEX_LTEQ		 , (PType **)&TypeBool,    (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  [](auto *l, auto *r, auto &) { l->IntVal = l->UIntVal <= r->UIntVal; l->Type = TypeBool; return l; } },
-		{ PEX_LTEQ		 , (PType **)&TypeBool,    (PType **)&TypeFloat64, (PType **)&TypeFloat64, [](auto *l, auto *r, auto &) { l->IntVal = l->DoubleVal <= r->DoubleVal; l->Type = TypeBool; return l; } },
-
-		{ PEX_GT		 , (PType **)&TypeBool,    (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  [](auto *l, auto *r, auto &) { l->IntVal = l->IntVal > r->IntVal; l->Type = TypeBool; return l; } },
-		{ PEX_GT		 , (PType **)&TypeBool,    (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  [](auto *l, auto *r, auto &) { l->IntVal = l->UIntVal > r->UIntVal; l->Type = TypeBool; return l; } },
-		{ PEX_GT		 , (PType **)&TypeBool,    (PType **)&TypeFloat64, (PType **)&TypeFloat64, [](auto *l, auto *r, auto &) { l->IntVal = l->DoubleVal > r->DoubleVal; l->Type = TypeBool; return l; } },
-
-		{ PEX_GTEQ		 , (PType **)&TypeBool,    (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  [](auto *l, auto *r, auto &) { l->IntVal = l->IntVal >= r->IntVal; l->Type = TypeBool; return l; } },
-		{ PEX_GTEQ		 , (PType **)&TypeBool,    (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  [](auto *l, auto *r, auto &) { l->IntVal = l->UIntVal >= r->UIntVal; l->Type = TypeBool; return l; } },
-		{ PEX_GTEQ		 , (PType **)&TypeBool,    (PType **)&TypeFloat64, (PType **)&TypeFloat64, [](auto *l, auto *r, auto &) { l->IntVal = l->DoubleVal >= r->DoubleVal; l->Type = TypeBool; return l; } },
-
-		{ PEX_NEQ		 , (PType **)&TypeBool,    (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  [](auto *l, auto *r, auto &) { l->IntVal = l->IntVal != r->IntVal; l->Type = TypeBool; return l; } },
-		{ PEX_NEQ		 , (PType **)&TypeBool,    (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  [](auto *l, auto *r, auto &) { l->IntVal = l->UIntVal != r->UIntVal; l->Type = TypeBool; return l; } },
-		{ PEX_NEQ		 , (PType **)&TypeBool,    (PType **)&TypeFloat64, (PType **)&TypeFloat64, [](auto *l, auto *r, auto &) { l->IntVal = l->DoubleVal != r->DoubleVal; l->Type = TypeBool; return l; } },
-
-		{ PEX_EQEQ		 , (PType **)&TypeBool,    (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  [](auto *l, auto *r, auto &) { l->IntVal = l->IntVal == r->IntVal; l->Type = TypeBool; return l; } },
-		{ PEX_EQEQ		 , (PType **)&TypeBool,    (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  [](auto *l, auto *r, auto &) { l->IntVal = l->UIntVal == r->UIntVal; l->Type = TypeBool; return l; } },
-		{ PEX_EQEQ		 , (PType **)&TypeBool,    (PType **)&TypeFloat64, (PType **)&TypeFloat64, [](auto *l, auto *r, auto &) { l->IntVal = l->DoubleVal == r->DoubleVal; l->Type = TypeBool; return l; } },
-
-		{ PEX_LTGTEQ	 , (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  (PType **)&TypeSInt32,  EvalLTGTEQSInt32 },
-		{ PEX_LTGTEQ	 , (PType **)&TypeSInt32,  (PType **)&TypeUInt32,  (PType **)&TypeUInt32,  EvalLTGTEQUInt32 },
-		{ PEX_LTGTEQ	 , (PType **)&TypeSInt32,  (PType **)&TypeFloat64, (PType **)&TypeFloat64, EvalLTGTEQFloat64 },
-	};
-	for (size_t i = 0; i < countof(BinaryOpInit); ++i)
-	{
-		ZCC_OpInfo[BinaryOpInit[i].Op].AddProto(*BinaryOpInit[i].Res, *BinaryOpInit[i].Ltype, *BinaryOpInit[i].Rtype, BinaryOpInit[i].EvalConst);
-	}
-}
-
-static void IntToS32(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
-{
-	// Integers always fill out the full sized 32-bit field, so converting
-	// from a smaller sized integer to a 32-bit one is as simple as changing
-	// the type field.
-	expr->Type = TypeSInt32;
-}
-
-static void S32toS8(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
-{
-	expr->IntVal = ((expr->IntVal << 24) >> 24);
-	expr->Type = TypeSInt8;
-}
-
-static void S32toS16(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
-{
-	expr->IntVal = ((expr->IntVal << 16) >> 16);
-	expr->Type = TypeSInt16;
-}
-
-static void S32toU8(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
-{
-	expr->IntVal &= 0xFF;
-	expr->Type = TypeUInt8;
-}
-
-static void S32toU16(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
-{
-	expr->IntVal &= 0xFFFF;
-	expr->Type = TypeUInt16;
-}
-
-static void S32toU32(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
-{
-	expr->Type = TypeUInt32;
-}
-
-static void S32toD(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
-{
-	expr->DoubleVal = expr->IntVal;
-	expr->Type = TypeFloat64;
-}
-
-static void DtoS32(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
-{
-	expr->IntVal = (int)expr->DoubleVal;
-	expr->Type = TypeSInt32;
-}
-
-static void U32toD(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
-{
-	expr->DoubleVal = expr->UIntVal;
-	expr->Type = TypeFloat64;
-}
-
-static void DtoU32(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
-{
-	expr->UIntVal = (unsigned int)expr->DoubleVal;
-	expr->Type = TypeUInt32;
-}
-
-static void FtoD(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
-{
-	// Constant single precision numbers are stored as doubles.
-	assert(expr->Type == TypeFloat32);
-	expr->Type = TypeFloat64;
-}
-
-static void DtoF(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
-{
-	// Truncate double precision to single precision.
-	float poop = (float)expr->DoubleVal;
-	expr->DoubleVal = poop;
-	expr->Type = TypeFloat32;
-}
-
-static void S32toS(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
-{
-	char str[16];
-	int len = mysnprintf(str, countof(str), "%i", expr->IntVal);
-	expr->StringVal = str_arena.Alloc(str, len);
-	expr->Type = TypeString;
-}
-
-static void U32toS(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
-{
-	char str[16];
-	int len = mysnprintf(str, countof(str), "%u", expr->UIntVal);
-	expr->StringVal = str_arena.Alloc(str, len);
-	expr->Type = TypeString;
-}
-
-static void DtoS(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
-{
-	// Convert to a string with enough precision such that converting
-	// back to a double will not lose any data.
-	char str[64];
-	IGNORE_FORMAT_PRE
-	int len = mysnprintf(str, countof(str), "%H", expr->DoubleVal);
-	IGNORE_FORMAT_POST
-	expr->StringVal = str_arena.Alloc(str, len);
-	expr->Type = TypeString;
-}
-
-//==========================================================================
-//
-// ZCC_InitConversions
-//
-//==========================================================================
-
-void ZCC_InitConversions()
-{
-	TypeUInt8->AddConversion(TypeSInt32, IntToS32);
-	TypeSInt8->AddConversion(TypeSInt32, IntToS32);
-	TypeUInt16->AddConversion(TypeSInt32, IntToS32);
-	TypeSInt16->AddConversion(TypeSInt32, IntToS32);
-
-	TypeUInt32->AddConversion(TypeSInt32, IntToS32);
-	TypeUInt32->AddConversion(TypeFloat64, U32toD);
-	TypeUInt32->AddConversion(TypeString, U32toS);
-
-	TypeSInt32->AddConversion(TypeUInt8, S32toU8);
-	TypeSInt32->AddConversion(TypeSInt8, S32toS8);
-	TypeSInt32->AddConversion(TypeSInt16, S32toS16);
-	TypeSInt32->AddConversion(TypeUInt16, S32toU16);
-	TypeSInt32->AddConversion(TypeUInt32, S32toU32);
-	TypeSInt32->AddConversion(TypeFloat64, S32toD);
-	TypeSInt32->AddConversion(TypeString, S32toS);
-
-	TypeFloat32->AddConversion(TypeFloat64, FtoD);
-
-	TypeFloat64->AddConversion(TypeUInt32, DtoU32);
-	TypeFloat64->AddConversion(TypeSInt32, DtoS32);
-	TypeFloat64->AddConversion(TypeFloat32, DtoF);
-	TypeFloat64->AddConversion(TypeString, DtoS);
-}
diff --git a/src/scripting/zscript/zcc_parser.cpp b/src/scripting/zscript/zcc_parser.cpp
index 95f75aea6..872ab2cc8 100644
--- a/src/scripting/zscript/zcc_parser.cpp
+++ b/src/scripting/zscript/zcc_parser.cpp
@@ -316,6 +316,8 @@ static void DoParse(int lumpnum)
 	FScanner sc;
 	void *parser;
 	ZCCToken value;
+	auto baselump = lumpnum;
+	auto fileno = Wads.GetLumpFile(lumpnum);
 
 	parser = ZCCParseAlloc(malloc);
 	ZCCParseState state;
@@ -344,6 +346,13 @@ static void DoParse(int lumpnum)
 		}
 		else
 		{
+			auto fileno2 = Wads.GetLumpFile(lumpnum);
+			if (fileno == 0 && fileno2 != 0)
+			{
+				I_FatalError("File %s is overriding core lump %s.",
+					Wads.GetWadFullName(Wads.GetLumpFile(baselump)), Includes[i].GetChars());
+			}
+
 			ParseSingleFile(nullptr, lumpnum, parser, state);
 		}
 	}
@@ -408,9 +417,6 @@ void ParseScripts()
 	{
 		InitTokenMap();
 	}
-	ZCC_InitOperators();
-	ZCC_InitConversions();
-
 	int lump, lastlump = 0;
 	FScriptPosition::ResetErrorCounter();
 
diff --git a/src/scripting/zscript/zcc_parser.h b/src/scripting/zscript/zcc_parser.h
index b80686bb0..95bafd54f 100644
--- a/src/scripting/zscript/zcc_parser.h
+++ b/src/scripting/zscript/zcc_parser.h
@@ -527,47 +527,8 @@ struct ZCC_FlagStmt : ZCC_Statement
 	bool set;
 };
 
-typedef ZCC_ExprConstant *(*EvalConst1op)(ZCC_ExprConstant *);
-typedef ZCC_ExprConstant *(*EvalConst2op)(ZCC_ExprConstant *, ZCC_ExprConstant *, FSharedStringArena &);
-
-struct ZCC_OpProto
-{
-	ZCC_OpProto *Next;
-	PType *ResType;
-	PType *Type1;
-	PType *Type2;
-	union
-	{
-		EvalConst1op EvalConst1;
-		EvalConst2op EvalConst2;
-	};
-
-	ZCC_OpProto(PType *res, PType *t1, PType *t2)
-		: ResType(res), Type1(t1), Type2(t2) {}
-};
-
-struct ZCC_OpInfoType
-{
-	const char *OpName;
-	ZCC_OpProto *Protos;
-
-	void AddProto(PType *res, PType *optype, EvalConst1op evalconst);
-	void AddProto(PType *res, PType *left, PType *right, EvalConst2op evalconst);
-
-	ZCC_OpProto *FindBestProto(PType *optype, const PType::Conversion **route, int &numslots);
-	ZCC_OpProto *FindBestProto(PType *left, const PType::Conversion **route1, int &numslots,
-		PType *right, const PType::Conversion **route2, int &numslots2);
-
-	void FreeAllProtos();
-};
-
-#define CONVERSION_ROUTE_SIZE	8
-
 FString ZCC_PrintAST(ZCC_TreeNode *root);
 
-void ZCC_InitOperators();
-
-extern ZCC_OpInfoType ZCC_OpInfo[PEX_COUNT_OF];
 
 struct ZCC_AST
 {
diff --git a/src/swrenderer/things/r_particle.cpp b/src/swrenderer/things/r_particle.cpp
index 4cf4803ce..510d05b7f 100644
--- a/src/swrenderer/things/r_particle.cpp
+++ b/src/swrenderer/things/r_particle.cpp
@@ -179,6 +179,8 @@ namespace swrenderer
 
 		// store information in a vissprite
 		RenderParticle *vis = RenderMemory::NewObject<RenderParticle>();
+		memset(vis, 0, sizeof(*vis));
+
 		vis->CurrentPortalUniq = renderportal->CurrentPortalUniq;
 		vis->heightsec = heightsec;
 		vis->xscale = FLOAT2FIXED(xscale);
diff --git a/src/swrenderer/things/r_playersprite.cpp b/src/swrenderer/things/r_playersprite.cpp
index 0d35fdea4..4cba801b7 100644
--- a/src/swrenderer/things/r_playersprite.cpp
+++ b/src/swrenderer/things/r_playersprite.cpp
@@ -273,6 +273,8 @@ namespace swrenderer
 
 		// store information in a vissprite
 		RenderSprite *vis = &avis[vispspindex];
+		memset(vis, 0, sizeof(*vis));
+
 		vis->renderflags = owner->renderflags;
 		vis->floorclip = 0;
 
diff --git a/src/swrenderer/things/r_sprite.cpp b/src/swrenderer/things/r_sprite.cpp
index d237348f4..377654043 100644
--- a/src/swrenderer/things/r_sprite.cpp
+++ b/src/swrenderer/things/r_sprite.cpp
@@ -157,6 +157,7 @@ namespace swrenderer
 
 		// store information in a vissprite
 		RenderSprite *vis = RenderMemory::NewObject<RenderSprite>();
+		memset(vis, 0, sizeof(*vis));
 
 		vis->CurrentPortalUniq = renderportal->CurrentPortalUniq;
 		vis->xscale = FLOAT2FIXED(xscale);
diff --git a/wadsrc/static/zscript/strife/inquisitor.txt b/wadsrc/static/zscript/strife/inquisitor.txt
index 5c15f4af7..c1265b474 100644
--- a/wadsrc/static/zscript/strife/inquisitor.txt
+++ b/wadsrc/static/zscript/strife/inquisitor.txt
@@ -143,9 +143,9 @@ class Inquisitor : Actor
 		A_PlaySound ("inquisitor/jump", CHAN_ITEM, 1, true);
 		AddZ(64);
 		A_FaceTarget ();
-		speed = Speed * (2./3);
-		VelFromAngle(speed);
-		double dist = DistanceBySpeed(target, speed);
+		let localspeed = Speed * (2./3);
+		VelFromAngle(localspeed);
+		double dist = DistanceBySpeed(target, localspeed);
 		Vel.Z = (target.pos.z - pos.z) / dist;
 		reactiontime = 60;
 		bNoGravity = true;