diff --git a/src/scripting/vmthunks.cpp b/src/scripting/vmthunks.cpp
index bfbae6206b..6512480b2c 100644
--- a/src/scripting/vmthunks.cpp
+++ b/src/scripting/vmthunks.cpp
@@ -2887,6 +2887,8 @@ DEFINE_FIELD(FLevelLocals, skyfog)
 DEFINE_FIELD(FLevelLocals, pixelstretch)
 DEFINE_FIELD(FLevelLocals, deathsequence)
 DEFINE_FIELD_BIT(FLevelLocals, frozenstate, frozen, 1)	// still needed for backwards compatibility.
+DEFINE_FIELD_NAMED(FLevelLocals, i_compatflags, compatflags)
+DEFINE_FIELD_NAMED(FLevelLocals, i_compatflags2, compatflags2)
 
 DEFINE_FIELD_BIT(FLevelLocals, flags, noinventorybar, LEVEL_NOINVENTORYBAR)
 DEFINE_FIELD_BIT(FLevelLocals, flags, monsterstelefrag, LEVEL_MONSTERSTELEFRAG)
diff --git a/src/scripting/vmthunks_actors.cpp b/src/scripting/vmthunks_actors.cpp
index 05d0f8657d..0eac413eef 100644
--- a/src/scripting/vmthunks_actors.cpp
+++ b/src/scripting/vmthunks_actors.cpp
@@ -1669,34 +1669,6 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, isFrozen, isFrozen)
 }
 
 
-//=====================================================================================
-//
-// compat flags. These two are the only ones that get checked in script code
-// so anything more complex isn't really needed.
-//
-//=====================================================================================
-static int compat_limitpain_(AActor *self)
-{
-	return self->Level->i_compatflags & COMPATF_LIMITPAIN;
-}
-
-static int compat_mushroom_(AActor *self)
-{
-	return self->Level->i_compatflags & COMPATF_MUSHROOM;
-}
-
-DEFINE_ACTION_FUNCTION_NATIVE(AActor, compat_limitpain, compat_limitpain_)
-{
-	PARAM_SELF_PROLOGUE(AActor);
-	ACTION_RETURN_INT(compat_limitpain_(self));
-}
-
-DEFINE_ACTION_FUNCTION_NATIVE(AActor, compat_mushroom, compat_mushroom_)
-{
-	PARAM_SELF_PROLOGUE(AActor);
-	ACTION_RETURN_INT(compat_mushroom_(self));
-}
-
 //===========================================================================
 //
 // PlayerPawn functions
diff --git a/wadsrc/static/zscript/actor_inventory.txt b/wadsrc/static/zscript/actor_inventory.txt
index 837184dd13..d646251095 100644
--- a/wadsrc/static/zscript/actor_inventory.txt
+++ b/wadsrc/static/zscript/actor_inventory.txt
@@ -630,7 +630,7 @@ extend class Actor
 			Actor mo;
 			double spawnz = 0;
 
-			if (!compat_notossdrops)
+			if (!(Level.compatflags & COMPATF_NOTOSSDROPS))
 			{
 				int style = sv_dropstyle;
 				if (style == 0)
@@ -651,7 +651,7 @@ extend class Actor
 			{
 				mo.bDropped = true;
 				mo.bNoGravity = false;	// [RH] Make sure it is affected by gravity
-				if (!compat_notossdrops)
+				if (!(Level.compatflags & COMPATF_NOTOSSDROPS))
 				{
 					mo.TossItem ();
 				}
diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt
index 2a81b410e9..2ba4756392 100644
--- a/wadsrc/static/zscript/base.txt
+++ b/wadsrc/static/zscript/base.txt
@@ -691,6 +691,8 @@ struct LevelLocals native
 	native readonly int skyfog;
 	native readonly float pixelstretch;
 	native name deathsequence;
+	native readonly int compatflags;
+	native readonly int compatflags2;
 // level_info_t *info cannot be done yet.
 
 	native String GetUDMFString(int type, int index, Name key);
diff --git a/wadsrc/static/zscript/constants.txt b/wadsrc/static/zscript/constants.txt
index 1212b7b761..0de7ec391d 100644
--- a/wadsrc/static/zscript/constants.txt
+++ b/wadsrc/static/zscript/constants.txt
@@ -1295,3 +1295,47 @@ enum EChangeLevelFlags
 	CHANGELEVEL_PRERAISEWEAPON = 64,
 };
 
+// [RH] Compatibility flags.
+enum ECompatFlags
+{
+	COMPATF_SHORTTEX		= 1 << 0,	// Use Doom's shortest texture around behavior?
+	COMPATF_STAIRINDEX		= 1 << 1,	// Don't fix loop index for stair building?
+	COMPATF_LIMITPAIN		= 1 << 2,	// Pain elemental is limited to 20 lost souls?
+	COMPATF_SILENTPICKUP	= 1 << 3,	// Pickups are only heard locally?
+	COMPATF_NO_PASSMOBJ		= 1 << 4,	// Pretend every actor is infinitely tall?
+	COMPATF_MAGICSILENCE	= 1 << 5,	// Limit actors to one sound at a time?
+	COMPATF_WALLRUN			= 1 << 6,	// Enable buggier wall clipping so players can wallrun?
+	COMPATF_NOTOSSDROPS		= 1 << 7,	// Spawn dropped items directly on the floor?
+	COMPATF_USEBLOCKING		= 1 << 8,	// Any special line can block a use line
+	COMPATF_NODOORLIGHT		= 1 << 9,	// Don't do the BOOM local door light effect
+	COMPATF_RAVENSCROLL		= 1 << 10,	// Raven's scrollers use their original carrying speed
+	COMPATF_SOUNDTARGET		= 1 << 11,	// Use sector based sound target code.
+	COMPATF_DEHHEALTH		= 1 << 12,	// Limit deh.MaxHealth to the health bonus (as in Doom2.exe)
+	COMPATF_TRACE			= 1 << 13,	// Trace ignores lines with the same sector on both sides
+	COMPATF_DROPOFF			= 1 << 14,	// Monsters cannot move when hanging over a dropoff
+	COMPATF_BOOMSCROLL		= 1 << 15,	// Scrolling sectors are additive like in Boom
+	COMPATF_INVISIBILITY	= 1 << 16,	// Monsters can see semi-invisible players
+	COMPATF_SILENT_INSTANT_FLOORS = 1<<17,	// Instantly moving floors are not silent
+	COMPATF_SECTORSOUNDS	= 1 << 18,	// Sector sounds use original method for sound origin.
+	COMPATF_MISSILECLIP		= 1 << 19,	// Use original Doom heights for clipping against projectiles
+	COMPATF_CROSSDROPOFF	= 1 << 20,	// monsters can't be pushed over dropoffs
+	COMPATF_ANYBOSSDEATH	= 1 << 21,	// [GZ] Any monster which calls BOSSDEATH counts for level specials
+	COMPATF_MINOTAUR		= 1 << 22,	// Minotaur's floor flame is exploded immediately when feet are clipped
+	COMPATF_MUSHROOM		= 1 << 23,	// Force original velocity calculations for A_Mushroom in Dehacked mods.
+	COMPATF_MBFMONSTERMOVE	= 1 << 24,	// Monsters are affected by friction and pushers/pullers.
+	COMPATF_CORPSEGIBS		= 1 << 25,	// Crushed monsters are turned into gibs, rather than replaced by gibs.
+	COMPATF_NOBLOCKFRIENDS	= 1 << 26,	// Friendly monsters aren't blocked by monster-blocking lines.
+	COMPATF_SPRITESORT		= 1 << 27,	// Invert sprite sorting order for sprites of equal distance
+	COMPATF_HITSCAN			= 1 << 28,	// Hitscans use original blockmap anf hit check code.
+	COMPATF_LIGHT			= 1 << 29,	// Find neighboring light level like Doom
+	COMPATF_POLYOBJ			= 1 << 30,	// Draw polyobjects the old fashioned way
+	COMPATF_MASKEDMIDTEX	= 1 << 31,	// Ignore compositing when drawing masked midtextures
+
+	COMPATF2_BADANGLES		= 1 << 0,	// It is impossible to face directly NSEW.
+	COMPATF2_FLOORMOVE		= 1 << 1,	// Use the same floor motion behavior as Doom.
+	COMPATF2_SOUNDCUTOFF	= 1 << 2,	// Cut off sounds when an actor vanishes instead of making it owner-less
+	COMPATF2_POINTONLINE	= 1 << 3,	// Use original but buggy P_PointOnLineSide() and P_PointOnDivlineSideCompat()
+	COMPATF2_MULTIEXIT		= 1 << 4,	// Level exit can be triggered multiple times (required by Daedalus's travel tubes, thanks to a faulty script)
+	COMPATF2_TELEPORT		= 1 << 5,	// Don't let indirect teleports trigger sector actions
+	COMPATF2_PUSHWINDOW		= 1 << 6,	// Disable the window check in CheckForPushSpecial()
+};
diff --git a/wadsrc/static/zscript/doom/fatso.txt b/wadsrc/static/zscript/doom/fatso.txt
index cbf820398a..e0dfd338d6 100644
--- a/wadsrc/static/zscript/doom/fatso.txt
+++ b/wadsrc/static/zscript/doom/fatso.txt
@@ -105,8 +105,6 @@ extend class Actor
 {
 	const FATSPREAD = 90./8;
 	
-	private native bool compat_mushroom();
-
 	void A_FatRaise()
 	{
 		A_FaceTarget();
@@ -196,7 +194,7 @@ extend class Actor
 		aimtarget.Height = Height;
 		
 		bool shootmode = ((flags & MSF_Classic) || // Flag explicitly set, or no flags and compat options
-					(flags == 0 && CurState.bDehacked && compat_mushroom()));
+					(flags == 0 && CurState.bDehacked && (Level.compatflags & COMPATF_MUSHROOM)));
 
 		for (i = -numspawns; i <= numspawns; i += 8)
 		{
diff --git a/wadsrc/static/zscript/doom/painelemental.txt b/wadsrc/static/zscript/doom/painelemental.txt
index 7995eae1be..2442613031 100644
--- a/wadsrc/static/zscript/doom/painelemental.txt
+++ b/wadsrc/static/zscript/doom/painelemental.txt
@@ -62,8 +62,6 @@ class PainElemental : Actor
 
 extend class Actor
 {
-	private native bool compat_limitpain();
-
 	//
 	// A_PainShootSkull
 	// Spawn a lost soul and launch it at the target
@@ -88,7 +86,7 @@ extend class Actor
 		}
 
 		// [RH] make this optional
-		if (limit < 0 && compat_limitpain())
+		if (limit < 0 && (Level.compatflags & COMPATF_LIMITPAIN))
 			limit = 21;
 
 		if (limit > 0)
diff --git a/wadsrc/static/zscript/raven/minotaur.txt b/wadsrc/static/zscript/raven/minotaur.txt
index 35deebdaef..b618f018c3 100644
--- a/wadsrc/static/zscript/raven/minotaur.txt
+++ b/wadsrc/static/zscript/raven/minotaur.txt
@@ -347,7 +347,7 @@ class Minotaur : Actor
 		}
 		else
 		{
-			if (Floorclip > 0 && compat_minotaur)
+			if (Floorclip > 0 && (Level.compatflags & COMPAT_MINOTAUR))
 			{
 				// only play the sound. 
 				A_PlaySound ("minotaur/fx2hit", CHAN_WEAPON);
diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt
index 9cf20b4f37..ecb029c198 100644
--- a/wadsrc/static/zscript/shared/player.txt
+++ b/wadsrc/static/zscript/shared/player.txt
@@ -1930,7 +1930,7 @@ class PlayerPawn : Actor
 
 	override int GetMaxHealth(bool withupgrades) const
 	{
-		int ret = MaxHealth > 0? MaxHealth : (compat_dehhealth? 100 : deh.MaxHealth);
+		int ret = MaxHealth > 0? MaxHealth : ((Level.compatflags & COMPATF_DEHHEALTH)? 100 : deh.MaxHealth);
 		if (withupgrades) ret += stamina + BonusHealth;
 		return ret;
 	}