diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 8c0a30ea0..fdb668b01 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -484,6 +484,9 @@ if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
 		set( CMAKE_C_FLAGS "-Wno-unused-result ${CMAKE_C_FLAGS}" )
 		set( CMAKE_CXX_FLAGS "-Wno-unused-result ${CMAKE_CXX_FLAGS}" )
 	endif()
+	if(APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+		set( CMAKE_CXX_FLAGS "-Wno-inconsistent-missing-override ${CMAKE_CXX_FLAGS}" )
+	endif()
 	set( CMAKE_C_FLAGS "-Wall -Wextra -Wno-unused -Wno-unused-parameter -Wno-missing-field-initializers -ffp-contract=off ${CMAKE_C_FLAGS}" )
 	set( CMAKE_CXX_FLAGS "-Wall -Wextra -Wno-unused -Wno-unused-parameter -Wno-missing-field-initializers -ffp-contract=off ${CMAKE_CXX_FLAGS}" )
 
@@ -511,6 +514,13 @@ if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
 		endif ()
 	endif ()
 
+	# With standard Apple tools -stdlib=libc++ needs to be specified in order to get
+	# C++11 support using SDKs 10.7 and 10.8.
+	if ( APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang" )
+		set( CMAKE_CXX_FLAGS "-stdlib=libc++ ${CMAKE_CXX_FLAGS}" )
+		set( CMAKE_EXE_LINKER_FLAGS "-stdlib=libc++ ${CMAKE_EXE_LINKER_FLAGS}" )
+	endif ()
+
 	# Remove extra warnings when using the official DirectX headers.
 	# Also, TDM-GCC 4.4.0 no longer accepts glibc-style printf formats as valid,
 	# which is a royal pain. The previous version I had been using was fine with them.
diff --git a/src/actor.h b/src/actor.h
index ea4d15ad9..93d2834a8 100644
--- a/src/actor.h
+++ b/src/actor.h
@@ -407,9 +407,12 @@ enum ActorRenderFlag
 	RF_SPRITETYPEMASK	= 0x7000,	// ---Different sprite types, not all implemented
 	RF_FACESPRITE		= 0x0000,	// Face sprite
 	RF_WALLSPRITE		= 0x1000,	// Wall sprite
-	RF_FLOORSPRITE		= 0x2000,	// Floor sprite
+	RF_FLATSPRITE		= 0x2000,	// Flat sprite
 	RF_VOXELSPRITE		= 0x3000,	// Voxel object
 	RF_INVISIBLE		= 0x8000,	// Don't bother drawing this actor
+	RF_ROLLSPRITE		= 0x40000,	//[marrub]roll the sprite billboard
+	RF_DONTFLIP			= 0x80000,	// Don't flip it when viewed from behind.
+	RF_ROLLCENTER		= 0x100000, // Rotate from the center of sprite instead of offsets
 
 	RF_FORCEYBILLBOARD		= 0x10000,	// [BB] OpenGL only: draw with y axis billboard, i.e. anchored to the floor (overrides gl_billboard_mode setting)
 	RF_FORCEXYBILLBOARD		= 0x20000,	// [BB] OpenGL only: draw with xy axis billboard, i.e. unanchored (overrides gl_billboard_mode setting)
diff --git a/src/cmdlib.h b/src/cmdlib.h
index 05e515374..b035842cd 100644
--- a/src/cmdlib.h
+++ b/src/cmdlib.h
@@ -16,7 +16,7 @@
 #include <stdarg.h>
 
 // the dec offsetof macro doesnt work very well...
-#define myoffsetof(type,identifier) ((size_t)&((type *)1)->identifier - 1)
+#define myoffsetof(type,identifier) ((size_t)&((type *)alignof(type))->identifier - alignof(type))
 
 int		Q_filelength (FILE *f);
 bool FileExists (const char *filename);
diff --git a/src/d_player.h b/src/d_player.h
index 48649de7a..e932223aa 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -532,7 +532,10 @@ public:
 	void TickPSprites();
 	void DestroyPSprites();
 	DPSprite *FindPSprite(int layer);
-	DPSprite *GetPSprite(PSPLayers layer); // Used ONLY for compatibility with the old hardcoded layers.
+	// Used ONLY for compatibility with the old hardcoded layers.
+	// Make sure that a state is properly set after calling this unless
+	// you are 100% sure the context already implies the layer exists.
+	DPSprite *GetPSprite(PSPLayers layer);
 };
 
 // Bookkeeping on players - state.
diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp
index 6b963f6d4..a7f07247f 100644
--- a/src/dobjtype.cpp
+++ b/src/dobjtype.cpp
@@ -1272,6 +1272,7 @@ void PFloat::WriteValue(FArchive &ar, const void *addr) const
 		{
 			ar.WriteByte(VAL_Float64);
 			ar << doubleprecision;
+			return;
 		}
 	}
 	else
diff --git a/src/doomdata.h b/src/doomdata.h
index 4e4976800..f809f05ea 100644
--- a/src/doomdata.h
+++ b/src/doomdata.h
@@ -334,7 +334,7 @@ struct mapthinghexen_t
 	SWORD		z;
 	SWORD		angle;
 	SWORD		type;
-	SWORD		flags;
+	WORD		flags;
 	BYTE		special;
 	BYTE		args[5];
 };
diff --git a/src/farchive.cpp b/src/farchive.cpp
index fc3d75bbb..6697fd5b7 100644
--- a/src/farchive.cpp
+++ b/src/farchive.cpp
@@ -728,13 +728,13 @@ void FArchive::WriteByte(BYTE val)
 
 void FArchive::WriteInt16(WORD val)
 {
-	WORD out = LittleShort(val);
+	WORD out = SWAP_WORD(val);
 	m_File->Write(&out, 2);
 }
 
 void FArchive::WriteInt32(DWORD val)
 {
-	int out = LittleLong(val);
+	int out = SWAP_DWORD(val);
 	m_File->Write(&out, 4);
 }
 
diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp
index f8648808d..f02843c89 100644
--- a/src/fragglescript/t_func.cpp
+++ b/src/fragglescript/t_func.cpp
@@ -1573,7 +1573,7 @@ void FParser::SF_FloorHeight(void)
 					sectors[i].floorplane.PointToDist (sectors[i].centerspot, dest),
 					crush? 10:-1, 
 					(dest > sectors[i].CenterFloor()) ? 1 : -1,
-					false) != EMoveResult::crushed)
+					false) == EMoveResult::crushed)
 				{
 					returnval = 0;
 				}
@@ -1662,7 +1662,7 @@ void FParser::SF_CeilingHeight(void)
 					sectors[i].ceilingplane.PointToDist (sectors[i].centerspot, dest), 
 					crush? 10:-1,
 					(dest > sectors[i].CenterCeiling()) ? 1 : -1,
-					false) != EMoveResult::crushed)
+					false) == EMoveResult::crushed)
 				{
 					returnval = 0;
 				}
@@ -3473,12 +3473,7 @@ void FParser::SF_Resurrect()
 		mo->SetState(state);
 		mo->Height = mo->GetDefault()->Height;
 		mo->radius = mo->GetDefault()->radius;
-		mo->flags =  mo->GetDefault()->flags;
-		mo->flags2 = mo->GetDefault()->flags2;
-		mo->flags3 = mo->GetDefault()->flags3;
-		mo->flags4 = mo->GetDefault()->flags4;
-		mo->flags5 = mo->GetDefault()->flags5;
-		mo->health = mo->GetDefault()->health;
+		mo->Revive();
 		mo->target = NULL;
 	}
 }
@@ -3766,30 +3761,6 @@ void FParser::SF_SetCorona(void)
 	t_return.value.i = 0;
 }
 
-//==========================================================================
-//
-// new for GZDoom: Call a Hexen line special (deprecated, superseded by direct use)
-//
-//==========================================================================
-
-void FParser::SF_Ls()
-{
-	int args[5]={0,0,0,0,0};
-	int spc;
-
-	if (CheckArgs(1))
-	{
-		spc=intvalue(t_argv[0]);
-		for(int i=0;i<5;i++)
-		{
-			if (t_argc>=i+2) args[i]=intvalue(t_argv[i+1]);
-		}
-		if (spc>=0 && spc<256)
-			P_ExecuteSpecial(spc, NULL,Script->trigger,false, args[0],args[1],args[2],args[3],args[4]);
-	}
-}
-
-
 //==========================================================================
 //
 // new for GZDoom: Gets the levelnum
@@ -4024,17 +3995,6 @@ void  FParser::SF_KillInSector()
 	}
 }
 
-//==========================================================================
-//
-// new for GZDoom: Sets a sector's type
-//
-//==========================================================================
-
-void FParser::SF_SectorType(void)
-{
-	// I don't think this was ever used publicly so I'm not going to bother fixing it.
-}
-
 //==========================================================================
 //
 // new for GZDoom: Sets a new line trigger type (Doom format!)
@@ -4069,30 +4029,6 @@ void FParser::SF_SetLineTrigger()
 }
 
 
-//==========================================================================
-//
-//
-//
-//==========================================================================
-
-void FParser::SF_ChangeTag()
-{
-	// Development garbage!
-}
-
-
-//==========================================================================
-//
-//
-//
-//==========================================================================
-
-void FParser::SF_WallGlow()
-{
-	// Development garbage!
-}
-
-
 //==========================================================================
 //
 // new for GZDoom: Call a Hexen line special
@@ -4510,13 +4446,10 @@ void init_functions(void)
 	// new for GZDoom
 	gscr->NewFunction("spawnshot2", &FParser::SF_SpawnShot2);
 	gscr->NewFunction("setcolor", &FParser::SF_SetColor);
-	gscr->NewFunction("sectortype", &FParser::SF_SectorType);
-	gscr->NewFunction("wallglow", &FParser::SF_WallGlow);
 	gscr->NewFunction("objradius", &FParser::SF_MobjRadius);
 	gscr->NewFunction("objheight", &FParser::SF_MobjHeight);
 	gscr->NewFunction("thingcount", &FParser::SF_ThingCount);
 	gscr->NewFunction("killinsector", &FParser::SF_KillInSector);
-	gscr->NewFunction("changetag", &FParser::SF_ChangeTag);
 	gscr->NewFunction("levelnum", &FParser::SF_LevelNum);
 
 	// new inventory
@@ -4525,8 +4458,6 @@ void init_functions(void)
 	gscr->NewFunction("checkinventory", &FParser::SF_CheckInventory);
 	gscr->NewFunction("setweapon", &FParser::SF_SetWeapon);
 
-	gscr->NewFunction("ls", &FParser::SF_Ls);	// execute Hexen type line special
-
 	// Dummies - shut up warnings
 	gscr->NewFunction("setcorona", &FParser::SF_SetCorona);
 }
diff --git a/src/g_doom/a_bossbrain.cpp b/src/g_doom/a_bossbrain.cpp
index 51ee77ff8..5d10e0138 100644
--- a/src/g_doom/a_bossbrain.cpp
+++ b/src/g_doom/a_bossbrain.cpp
@@ -147,7 +147,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BrainSpit)
 			{
 				spit->special2 = 0;
 			}
-			else if (fabs(spit->Vel.X) > fabs(spit->Vel.Y))
+			else if (fabs(spit->Vel.Y) > fabs(spit->Vel.X))
 			{
 				spit->special2 = int((targ->Y() - self->Y()) / spit->Vel.Y);
 			}
diff --git a/src/g_heretic/a_chicken.cpp b/src/g_heretic/a_chicken.cpp
index b70e4ff08..7f62d5f0e 100644
--- a/src/g_heretic/a_chicken.cpp
+++ b/src/g_heretic/a_chicken.cpp
@@ -123,9 +123,10 @@ DEFINE_ACTION_FUNCTION(AActor, A_Feathers)
 
 void P_UpdateBeak (AActor *self)
 {
-	if (self->player != nullptr)
+	DPSprite *pspr;
+	if (self->player != nullptr && (pspr = self->player->FindPSprite(PSP_WEAPON)) != nullptr)
 	{
-		self->player->GetPSprite(PSP_WEAPON)->y = WEAPONTOP + self->player->chickenPeck / 2;
+		pspr->y = WEAPONTOP + self->player->chickenPeck / 2;
 	}
 }
 
diff --git a/src/g_shared/a_artifacts.cpp b/src/g_shared/a_artifacts.cpp
index 786e49695..4dc154533 100644
--- a/src/g_shared/a_artifacts.cpp
+++ b/src/g_shared/a_artifacts.cpp
@@ -1128,12 +1128,27 @@ void APowerWeaponLevel2::InitEffect ()
 
 	assert (sister->SisterWeapon == weapon);
 
-	Owner->player->ReadyWeapon = sister;
 
 	if (weapon->GetReadyState() != sister->GetReadyState())
 	{
+		Owner->player->ReadyWeapon = sister;
 		P_SetPsprite(Owner->player, PSP_WEAPON, sister->GetReadyState());
 	}
+	else
+	{
+		DPSprite *psp = Owner->player->FindPSprite(PSP_WEAPON);
+		if (psp != nullptr && psp->GetCaller() == Owner->player->ReadyWeapon)
+		{
+			// If the weapon changes but the state does not, we have to manually change the PSprite's caller here.
+			psp->SetCaller(sister);
+			Owner->player->ReadyWeapon = sister;
+		}
+		else
+		{
+			// Something went wrong. Initiate a regular weapon change.
+			Owner->player->PendingWeapon = sister;
+		}
+	}
 }
 
 //===========================================================================
diff --git a/src/g_shared/a_weapons.cpp b/src/g_shared/a_weapons.cpp
index e7ebfc77b..575914fe8 100644
--- a/src/g_shared/a_weapons.cpp
+++ b/src/g_shared/a_weapons.cpp
@@ -642,7 +642,18 @@ void AWeapon::EndPowerup ()
 		}
 		else
 		{
-			Owner->player->ReadyWeapon = SisterWeapon;
+			DPSprite *psp = Owner->player->FindPSprite(PSP_WEAPON);
+			if (psp != nullptr && psp->GetCaller() == Owner->player->ReadyWeapon)
+			{
+				// If the weapon changes but the state does not, we have to manually change the PSprite's caller here.
+				psp->SetCaller(SisterWeapon);
+				Owner->player->ReadyWeapon = SisterWeapon;
+			}
+			else
+			{
+				// Something went wrong. Initiate a regular weapon change.
+				Owner->player->PendingWeapon = SisterWeapon;
+			}
 		}
 	}
 }
diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp
index a09c49303..b4a011510 100644
--- a/src/g_shared/sbarinfo_commands.cpp
+++ b/src/g_shared/sbarinfo_commands.cpp
@@ -3440,6 +3440,30 @@ class CommandIfInvulnerable : public SBarInfoNegatableFlowControl
 
 ////////////////////////////////////////////////////////////////////////////////
 
+class CommandIfWaterLevel : public SBarInfoNegatableFlowControl
+{
+	public:
+		CommandIfWaterLevel(SBarInfo *script) : SBarInfoNegatableFlowControl(script)
+		{
+		}
+
+		void	ParseNegatable(FScanner &sc, bool fullScreenOffsets)
+		{
+			sc.MustGetToken(TK_IntConst);
+			value = sc.Number;
+		}
+		void	Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged)
+		{
+			SBarInfoNegatableFlowControl::Tick(block, statusBar, hudChanged);
+
+			SetTruth(statusBar->CPlayer->mo->waterlevel >= value, block, statusBar);
+		}
+	protected:
+		int value;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
 static const char *SBarInfoCommandNames[] =
 {
 	"drawimage", "drawnumber", "drawswitchableimage",
@@ -3450,7 +3474,7 @@ static const char *SBarInfoCommandNames[] =
 	"isselected", "usesammo", "usessecondaryammo",
 	"hasweaponpiece", "inventorybarnotvisible",
 	"weaponammo", "ininventory", "alpha", "ifhealth",
-	"ifinvulnerable",
+	"ifinvulnerable", "ifwaterlevel",
 	NULL
 };
 
@@ -3464,7 +3488,7 @@ enum SBarInfoCommands
 	SBARINFO_ISSELECTED, SBARINFO_USESAMMO, SBARINFO_USESSECONDARYAMMO,
 	SBARINFO_HASWEAPONPIECE, SBARINFO_INVENTORYBARNOTVISIBLE,
 	SBARINFO_WEAPONAMMO, SBARINFO_ININVENTORY, SBARINFO_ALPHA, SBARINFO_IFHEALTH,
-	SBARINFO_IFINVULNERABLE,
+	SBARINFO_IFINVULNERABLE, SBARINFO_IFWATERLEVEL,
 };
 
 SBarInfoCommand *SBarInfoCommandFlowControl::NextCommand(FScanner &sc)
@@ -3499,6 +3523,7 @@ SBarInfoCommand *SBarInfoCommandFlowControl::NextCommand(FScanner &sc)
 			case SBARINFO_ALPHA: return new CommandAlpha(script);
 			case SBARINFO_IFHEALTH: return new CommandIfHealth(script);
 			case SBARINFO_IFINVULNERABLE: return new CommandIfInvulnerable(script);
+			case SBARINFO_IFWATERLEVEL: return new CommandIfWaterLevel(script);
 		}
 
 		sc.ScriptError("Unknown command '%s'.\n", sc.String);
diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp
index 4bfe85271..46cf61d54 100644
--- a/src/g_shared/shared_hud.cpp
+++ b/src/g_shared/shared_hud.cpp
@@ -81,6 +81,7 @@ CVAR (Int ,  hud_showtime,		0,	    CVAR_ARCHIVE);	// Show time on HUD
 CVAR (Int ,  hud_timecolor,		CR_GOLD,CVAR_ARCHIVE);	// Color of in-game time on HUD
 CVAR (Int ,  hud_showlag,		0, CVAR_ARCHIVE);		// Show input latency (maketic - gametic difference)
 
+CVAR (Int, hud_ammo_order, 0, CVAR_ARCHIVE);				// ammo image and text order
 CVAR (Int, hud_ammo_red, 25, CVAR_ARCHIVE)					// ammo percent less than which status is red    
 CVAR (Int, hud_ammo_yellow, 50, CVAR_ARCHIVE)				// ammo percent less is yellow more green        
 CVAR (Int, hud_health_red, 25, CVAR_ARCHIVE)				// health amount less than which status is red   
@@ -586,9 +587,21 @@ static int DrawAmmo(player_t *CPlayer, int x, int y)
 	// ok, we got all ammo types. Now draw the list back to front (bottom to top)
 
 	int def_width = ConFont->StringWidth("000/000");
-	x-=def_width;
 	int yadd = ConFont->GetHeight();
 
+	int xtext = x - def_width;
+	int ximage = x;
+
+	if (hud_ammo_order > 0)
+	{
+		xtext -= 24;
+		ximage -= 20;
+	}
+	else
+	{
+		ximage -= def_width + 20;
+	}
+
 	for(i=orderedammos.Size()-1;i>=0;i--)
 	{
 
@@ -613,8 +626,8 @@ static int DrawAmmo(player_t *CPlayer, int x, int y)
 						 ammo < ( (maxammo * hud_ammo_red) / 100) ? CR_RED :   
 						 ammo < ( (maxammo * hud_ammo_yellow) / 100) ? CR_GOLD : CR_GREEN );
 
-		DrawHudText(ConFont, fontcolor, buf, x-tex_width, y+yadd, trans);
-		DrawImageToBox(TexMan[icon], x-20, y, 16, 8, trans);
+		DrawHudText(ConFont, fontcolor, buf, xtext-tex_width, y+yadd, trans);
+		DrawImageToBox(TexMan[icon], ximage, y, 16, 8, trans);
 		y-=10;
 	}
 	return y;
diff --git a/src/g_strife/a_strifestuff.cpp b/src/g_strife/a_strifestuff.cpp
index 8b5788d44..4533016ec 100644
--- a/src/g_strife/a_strifestuff.cpp
+++ b/src/g_strife/a_strifestuff.cpp
@@ -317,7 +317,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_CheckTerrain)
 			int anglespeed = tagManager.GetFirstSectorTag(sec) - 100;
 			double speed = (anglespeed % 10) / 16.;
 			DAngle an = (anglespeed / 10) * (360 / 8.);
-			self->VelFromAngle(an, speed);
+			self->Thrust(an, speed);
 		}
 	}
 	return 0;
@@ -352,22 +352,12 @@ DEFINE_ACTION_FUNCTION(AActor, A_ItBurnsItBurns)
 
 	if (self->player != nullptr && self->player->mo == self)
 	{
-		FState *firehands = self->FindState("FireHands");
-		if (firehands != NULL)
-		{
-			DPSprite *psp = self->player->GetPSprite(PSP_STRIFEHANDS);
-			if (psp != nullptr)
-			{
-				psp->SetState(firehands);
-				psp->Flags &= PSPF_ADDWEAPON | PSPF_ADDBOB;
-				psp->y = WEAPONTOP;
-			}
+		P_SetPsprite(self->player, PSP_STRIFEHANDS, self->FindState("FireHands"));
 
-			self->player->ReadyWeapon = nullptr;
-			self->player->PendingWeapon = WP_NOCHANGE;
-			self->player->playerstate = PST_LIVE;
-			self->player->extralight = 3;
-		}
+		self->player->ReadyWeapon = nullptr;
+		self->player->PendingWeapon = WP_NOCHANGE;
+		self->player->playerstate = PST_LIVE;
+		self->player->extralight = 3;
 	}
 	return 0;
 }
@@ -388,14 +378,22 @@ DEFINE_ACTION_FUNCTION(AActor, A_CrispyPlayer)
 
 	if (self->player != nullptr && self->player->mo == self)
 	{
-		self->player->playerstate = PST_DEAD;
-
 		DPSprite *psp;
 		psp = self->player->GetPSprite(PSP_STRIFEHANDS);
+
 		FState *firehandslower = self->FindState("FireHandsLower");
 		FState *firehands = self->FindState("FireHands");
-		if (firehandslower != NULL && firehands != NULL && firehands < firehandslower)
-			psp->SetState(psp->GetState() + (firehandslower - firehands));
+		FState *state = psp->GetState();
+
+		if (state != nullptr && firehandslower != nullptr && firehands != nullptr && firehands < firehandslower)
+		{
+			self->player->playerstate = PST_DEAD;
+			psp->SetState(state + (firehandslower - firehands));
+		}
+		else if (state == nullptr)
+		{
+			psp->SetState(nullptr);
+		}
 	}
 	return 0;
 }
@@ -407,13 +405,20 @@ DEFINE_ACTION_FUNCTION(AActor, A_HandLower)
 	if (self->player != nullptr)
 	{
 		DPSprite *psp = self->player->GetPSprite(PSP_STRIFEHANDS);
+
+		if (psp->GetState() == nullptr)
+		{
+			psp->SetState(nullptr);
+			return 0;
+		}
+
 		psp->y += 9;
 		if (psp->y > WEAPONBOTTOM*2)
 		{
 			psp->SetState(nullptr);
 		}
+
 		if (self->player->extralight > 0) self->player->extralight--;
 	}
 	return 0;
 }
-
diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp
index d1753c405..1948153e4 100644
--- a/src/menu/menudef.cpp
+++ b/src/menu/menudef.cpp
@@ -306,7 +306,15 @@ static void ParseListMenuBody(FScanner &sc, FListMenuDescriptor *desc)
 			int y = sc.Number;
 			sc.MustGetStringName(",");
 			sc.MustGetString();
-			FListMenuItem *it = new FListMenuItemStaticText(x, y, sc.String, desc->mFont, desc->mFontColor, centered);
+			FString label = sc.String;
+			EColorRange cr = desc->mFontColor;
+			if (sc.CheckString(","))
+			{
+				sc.MustGetString();
+				cr = V_FindFontColor(sc.String);
+				if (cr == CR_UNTRANSLATED && !sc.Compare("untranslated")) cr = desc->mFontColor;
+			}
+			FListMenuItem *it = new FListMenuItemStaticText(x, y, label, desc->mFont, cr, centered);
 			desc->mItems.Push(it);
 		}
 		else if (sc.Compare("PatchItem"))
@@ -648,6 +656,21 @@ static void ParseOptionSettings(FScanner &sc)
 //
 //=============================================================================
 
+static EColorRange ParseOptionColor(FScanner &sc, FOptionMenuDescriptor *desc)
+{
+	EColorRange cr = OptionSettings.mFontColor;
+	if (sc.CheckString(","))
+	{
+		sc.MustGetString();
+		cr = V_FindFontColor(sc.String);
+		if (cr == CR_UNTRANSLATED && !sc.Compare("untranslated") && isdigit(sc.String[0]))
+		{
+			if (strtol(sc.String, NULL, 0)) cr = OptionSettings.mFontColorHeader;
+		}
+	}
+	return cr;
+}
+
 static void ParseOptionMenuBody(FScanner &sc, FOptionMenuDescriptor *desc)
 {
 	sc.MustGetStringName("{");
@@ -784,12 +807,7 @@ static void ParseOptionMenuBody(FScanner &sc, FOptionMenuDescriptor *desc)
 		{
 			sc.MustGetString();
 			FString label = sc.String;
-			bool cr = false;
-			if (sc.CheckString(","))
-			{
-				sc.MustGetNumber();
-				cr = !!sc.Number;
-			}
+			EColorRange cr = ParseOptionColor(sc, desc);
 			FOptionMenuItem *it = new FOptionMenuItemStaticText(label, cr);
 			desc->mItems.Push(it);
 		}
@@ -803,12 +821,7 @@ static void ParseOptionMenuBody(FScanner &sc, FOptionMenuDescriptor *desc)
 			sc.MustGetStringName(",");
 			sc.MustGetString();
 			FName action = sc.String;
-			bool cr = false;
-			if (sc.CheckString(","))
-			{
-				sc.MustGetNumber();
-				cr = !!sc.Number;
-			}
+			EColorRange cr = ParseOptionColor(sc, desc);
 			FOptionMenuItem *it = new FOptionMenuItemStaticTextSwitchable(label, label2, action, cr);
 			desc->mItems.Push(it);
 		}
diff --git a/src/menu/optionmenuitems.h b/src/menu/optionmenuitems.h
index 8a1840027..3131e35f7 100644
--- a/src/menu/optionmenuitems.h
+++ b/src/menu/optionmenuitems.h
@@ -483,7 +483,13 @@ public:
 	FOptionMenuItemStaticText(const char *label, bool header)
 		: FOptionMenuItem(label, NAME_None, true)
 	{
-		mColor = header? OptionSettings.mFontColorHeader : OptionSettings.mFontColor;
+		mColor = header ? OptionSettings.mFontColorHeader : OptionSettings.mFontColor;
+	}
+
+	FOptionMenuItemStaticText(const char *label, EColorRange cr)
+		: FOptionMenuItem(label, NAME_None, true)
+	{
+		mColor = cr;
 	}
 
 	int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
@@ -512,10 +518,10 @@ class FOptionMenuItemStaticTextSwitchable : public FOptionMenuItem
 	int mCurrent;
 
 public:
-	FOptionMenuItemStaticTextSwitchable(const char *label, const char *label2, FName action, bool header)
+	FOptionMenuItemStaticTextSwitchable(const char *label, const char *label2, FName action, EColorRange cr)
 		: FOptionMenuItem(label, action, true)
 	{
-		mColor = header? OptionSettings.mFontColorHeader : OptionSettings.mFontColor;
+		mColor = cr;
 		mAltText = label2;
 		mCurrent = 0;
 	}
diff --git a/src/p_acs.cpp b/src/p_acs.cpp
index 7cbfd69b4..919834a06 100644
--- a/src/p_acs.cpp
+++ b/src/p_acs.cpp
@@ -5712,8 +5712,8 @@ doplaysound:			if (funcIndex == ACSF_PlayActorSound)
 				argCount > 11 ? ACSToDouble(args[11]) : 1.0,
 				argCount > 12 ? args[12] : 0,
 				argCount > 13 ? args[13] : 0,
-				argCount > 14 ? args[14] : 0,
-				argCount > 15 ? args[15] : 0);
+				argCount > 14 ? ACSToDouble(args[14]) : 0,
+				argCount > 15 ? ACSToDouble(args[15]) : 0);
 		}
 
 		case ACSF_SetLineActivation:
diff --git a/src/p_doors.cpp b/src/p_doors.cpp
index d6a5f3a44..ab551de1b 100644
--- a/src/p_doors.cpp
+++ b/src/p_doors.cpp
@@ -688,14 +688,11 @@ void DAnimatedDoor::Tick ()
 //============================================================================
 
 DAnimatedDoor::DAnimatedDoor (sector_t *sec, line_t *line, int speed, int delay, FDoorAnimation *anim)
-	: DMovingCeiling (sec)
+	: DMovingCeiling (sec, false)
 {
 	double topdist;
 	FTextureID picnum;
 
-	// The DMovingCeiling constructor automatically sets up an interpolation for us.
-	// Stop it, since the ceiling is moving instantly here.
-	StopInterpolation();
 	m_DoorAnim = anim;
 
 	m_Line1 = line;
diff --git a/src/p_local.h b/src/p_local.h
index ac6b264d9..42b4da7e7 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -325,7 +325,8 @@ enum	// P_LineAttack flags
 {
 	LAF_ISMELEEATTACK = 1,
 	LAF_NORANDOMPUFFZ = 2,
-	LAF_NOIMPACTDECAL = 4
+	LAF_NOIMPACTDECAL = 4,
+	LAF_NOINTERACT =	8,
 };
 
 AActor *P_LineAttack(AActor *t1, DAngle angle, double distance, DAngle pitch, int damage, FName damageType, PClassActor *pufftype, int flags = 0, FTranslatedLineTarget *victim = NULL, int *actualdamage = NULL);
diff --git a/src/p_map.cpp b/src/p_map.cpp
index cf7655917..1684ee534 100644
--- a/src/p_map.cpp
+++ b/src/p_map.cpp
@@ -4096,6 +4096,7 @@ static ETraceStatus CheckForActor(FTraceResults &res, void *userdata)
 AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
 	DAngle pitch, int damage, FName damageType, PClassActor *pufftype, int flags, FTranslatedLineTarget*victim, int *actualdamage)
 {
+	bool nointeract = !!(flags & LAF_NOINTERACT);
 	DVector3 direction;
 	double shootz;
 	FTraceResults trace;
@@ -4185,26 +4186,32 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
 	}
 
 	int tflags;
-	if (puffDefaults != NULL && puffDefaults->flags6 & MF6_NOTRIGGER) tflags = TRACE_NoSky;
+	if (nointeract || (puffDefaults && puffDefaults->flags6 & MF6_NOTRIGGER)) tflags = TRACE_NoSky;
 	else tflags = TRACE_NoSky | TRACE_Impact;
 
 	if (!Trace(t1->PosAtZ(shootz), t1->Sector, direction, distance, MF_SHOOTABLE, 
 		ML_BLOCKEVERYTHING | ML_BLOCKHITSCAN, t1, trace, tflags, CheckForActor, &TData))
 	{ // hit nothing
-		if (puffDefaults == NULL)
-		{
-		}
-		else if (puffDefaults->ActiveSound)
+		if (!nointeract && puffDefaults && puffDefaults->ActiveSound)
 		{ // Play miss sound
 			S_Sound(t1, CHAN_WEAPON, puffDefaults->ActiveSound, 1, ATTN_NORM);
 		}
-		if (puffDefaults != NULL && puffDefaults->flags3 & MF3_ALWAYSPUFF)
+
+		// [MC] LAF_NOINTERACT guarantees puff spawning and returns it directly to the calling function.
+		// No damage caused, no sounds played, no blood splatters.
+
+		if (nointeract || (puffDefaults && puffDefaults->flags3 & MF3_ALWAYSPUFF))
 		{ // Spawn the puff anyway
 			puff = P_SpawnPuff(t1, pufftype, trace.HitPos, trace.SrcAngleFromTarget, trace.SrcAngleFromTarget, 2, puffFlags);
+
+			if (nointeract)
+			{
+				return puff;
+			}
 		}
 		else
 		{
-			return NULL;
+			return nullptr;
 		}
 	}
 	else
@@ -4212,12 +4219,17 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
 		if (trace.HitType != TRACE_HitActor)
 		{
 			// position a bit closer for puffs
-			if (trace.HitType != TRACE_HitWall || ((trace.Line->special != Line_Horizon) || spawnSky))
+			if (nointeract || trace.HitType != TRACE_HitWall || ((trace.Line->special != Line_Horizon) || spawnSky))
 			{
 				DVector2 pos = P_GetOffsetPosition(trace.HitPos.X, trace.HitPos.Y, -trace.HitVector.X * 4, -trace.HitVector.Y * 4);
 				puff = P_SpawnPuff(t1, pufftype, DVector3(pos, trace.HitPos.Z - trace.HitVector.Z * 4), trace.SrcAngleFromTarget,
 					trace.SrcAngleFromTarget - 90, 0, puffFlags);
 				puff->radius = 1/65536.;
+
+				if (nointeract)
+				{
+					return puff;
+				}
 			}
 
 			// [RH] Spawn a decal
@@ -4255,14 +4267,6 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
 		}
 		else
 		{
-			bool bloodsplatter = (t1->flags5 & MF5_BLOODSPLATTER) ||
-				(t1->player != NULL &&	t1->player->ReadyWeapon != NULL &&
-				(t1->player->ReadyWeapon->WeaponFlags & WIF_AXEBLOOD));
-
-			bool axeBlood = (t1->player != NULL &&
-				t1->player->ReadyWeapon != NULL &&
-				(t1->player->ReadyWeapon->WeaponFlags & WIF_AXEBLOOD));
-
 			// Hit a thing, so it could be either a puff or blood
 			DVector3 bleedpos = trace.HitPos;
 			// position a bit closer for puffs/blood if using compatibility mode.
@@ -4275,7 +4279,7 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
 			}
 
 			// Spawn bullet puffs or blood spots, depending on target type.
-			if ((puffDefaults != NULL && puffDefaults->flags3 & MF3_PUFFONACTORS) ||
+			if (nointeract || (puffDefaults && puffDefaults->flags3 & MF3_PUFFONACTORS) ||
 				(trace.Actor->flags & MF_NOBLOOD) ||
 				(trace.Actor->flags2 & (MF2_INVULNERABLE | MF2_DORMANT)))
 			{
@@ -4284,6 +4288,11 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
 
 				// We must pass the unreplaced puff type here 
 				puff = P_SpawnPuff(t1, pufftype, bleedpos, trace.SrcAngleFromTarget, trace.SrcAngleFromTarget - 90, 2, puffFlags | PF_HITTHING, trace.Actor);
+
+				if (nointeract)
+				{
+					return puff;
+				}
 			}
 
 			// Allow puffs to inflict poison damage, so that hitscans can poison, too.
@@ -4320,6 +4329,14 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
 			}
 			if (!(puffDefaults != NULL && puffDefaults->flags3&MF3_BLOODLESSIMPACT))
 			{
+				bool bloodsplatter = (t1->flags5 & MF5_BLOODSPLATTER) ||
+					(t1->player != nullptr &&	t1->player->ReadyWeapon != nullptr &&
+						(t1->player->ReadyWeapon->WeaponFlags & WIF_AXEBLOOD));
+
+				bool axeBlood = (t1->player != nullptr &&
+					t1->player->ReadyWeapon != nullptr &&
+					(t1->player->ReadyWeapon->WeaponFlags & WIF_AXEBLOOD));
+
 				if (!bloodsplatter && !axeBlood &&
 					!(trace.Actor->flags & MF_NOBLOOD) &&
 					!(trace.Actor->flags2 & (MF2_INVULNERABLE | MF2_DORMANT)))
@@ -5466,14 +5483,25 @@ bool P_AdjustFloorCeil(AActor *thing, FChangePosition *cpos)
 	}
 
 	bool isgood = P_CheckPosition(thing, thing->Pos(), tm);
-	thing->floorz = tm.floorz;
-	thing->ceilingz = tm.ceilingz;
-	thing->dropoffz = tm.dropoffz;		// killough 11/98: remember dropoffs
-	thing->floorpic = tm.floorpic;
-	thing->floorterrain = tm.floorterrain;
-	thing->floorsector = tm.floorsector;
-	thing->ceilingpic = tm.ceilingpic;
-	thing->ceilingsector = tm.ceilingsector;
+	if (!(thing->flags4 & MF4_ACTLIKEBRIDGE))
+	{
+		thing->floorz = tm.floorz;
+		thing->ceilingz = tm.ceilingz;
+		thing->dropoffz = tm.dropoffz;		// killough 11/98: remember dropoffs
+		thing->floorpic = tm.floorpic;
+		thing->floorterrain = tm.floorterrain;
+		thing->floorsector = tm.floorsector;
+		thing->ceilingpic = tm.ceilingpic;
+		thing->ceilingsector = tm.ceilingsector;
+	}
+	else
+	{
+		// Bridges only keep the info at their spawn position
+		// This is necessary to prevent moving sectors from altering the bridge's z-position.
+		// The bridge should remain at its current z, even if the sector change would cause
+		// floorz or ceilingz to be changed in a way that would make P_ZMovement adjust the bridge.
+		P_FindFloorCeiling(thing, FFCF_ONLYSPAWNPOS);
+	}
 
 	// restore the PASSMOBJ flag but leave the other flags alone.
 	thing->flags2 = (thing->flags2 & ~MF2_PASSMOBJ) | flags2;
diff --git a/src/p_maputl.h b/src/p_maputl.h
index e5a8a5f4f..81d3c9d13 100644
--- a/src/p_maputl.h
+++ b/src/p_maputl.h
@@ -38,18 +38,17 @@ struct intercept_t
 
 inline int P_PointOnLineSidePrecise(double x, double y, const line_t *line)
 {
-	return (y - line->v1->fY()) * line->Delta().X + (line->v1->fX() - x) * line->Delta().Y > -EQUAL_EPSILON;
+	return (y - line->v1->fY()) * line->Delta().X + (line->v1->fX() - x) * line->Delta().Y > EQUAL_EPSILON;
 }
 
 inline int P_PointOnLineSidePrecise(const DVector2 &pt, const line_t *line)
 {
-	return (pt.Y - line->v1->fY()) * line->Delta().X + (line->v1->fX() - pt.X) * line->Delta().Y > -EQUAL_EPSILON;
+	return (pt.Y - line->v1->fY()) * line->Delta().X + (line->v1->fX() - pt.X) * line->Delta().Y > EQUAL_EPSILON;
 }
 
 inline int P_PointOnLineSide (double x, double y, const line_t *line)
 {
 	extern int P_VanillaPointOnLineSide(double x, double y, const line_t* line);
-
 	return i_compatflags2 & COMPATF2_POINTONLINE
 		? P_VanillaPointOnLineSide(x, y, line) : P_PointOnLineSidePrecise(x, y, line);
 }
@@ -73,12 +72,12 @@ inline int P_PointOnLineSide(const DVector2 & p, const line_t *line)
 
 inline int P_PointOnDivlineSide(double x, double y, const divline_t *line)
 {
-	return (y - line->y) * line->dx + (line->x - x) * line->dy > -EQUAL_EPSILON;
+	return (y - line->y) * line->dx + (line->x - x) * line->dy > EQUAL_EPSILON;
 }
 
 inline int P_PointOnDivlineSide(const DVector2 &pos, const divline_t *line)
 {
-	return (pos.Y - line->y) * line->dx + (line->x - pos.X) * line->dy > -EQUAL_EPSILON;
+	return (pos.Y - line->y) * line->dx + (line->x - pos.X) * line->dy > EQUAL_EPSILON;
 }
 
 //==========================================================================
diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp
index ebfc14da3..bad9c2d73 100644
--- a/src/p_pspr.cpp
+++ b/src/p_pspr.cpp
@@ -216,15 +216,29 @@ DPSprite *player_t::GetPSprite(PSPLayers layer)
 	}
 
 	// Always update the caller here in case we switched weapon
-	// or if the layer was being used by an inventory item before.
+	// or if the layer was being used by something else before.
 	pspr->Caller = newcaller;
 
 	if (newcaller != oldcaller)
-	{ // Only change the flags if this layer was created now or if we updated the caller.
+	{ // Only reset stuff if this layer was created now or if it was being used before.
 		if (layer >= PSP_TARGETCENTER)
 		{ // The targeter layers were affected by those.
-			pspr->Flags |= (PSPF_CVARFAST|PSPF_POWDOUBLE);
+			pspr->Flags = (PSPF_CVARFAST|PSPF_POWDOUBLE);
 		}
+		else
+		{
+			pspr->Flags = (PSPF_ADDWEAPON|PSPF_ADDBOB|PSPF_CVARFAST|PSPF_POWDOUBLE);
+		}
+		if (layer == PSP_STRIFEHANDS)
+		{
+			// Some of the old hacks rely on this layer coming from the FireHands state.
+			// This is the ONLY time a psprite's state is actually null.
+			pspr->State = nullptr;
+			pspr->y = WEAPONTOP;
+		}
+
+		pspr->oldx = pspr->x;
+		pspr->oldy = pspr->y;
 	}
 
 	return pspr;
@@ -1071,9 +1085,8 @@ DEFINE_ACTION_FUNCTION(AInventory, A_Lower)
 	}
 	if (player->playerstate == PST_DEAD)
 	{ // Player is dead, so don't bring up a pending weapon
-		psp->y = WEAPONBOTTOM;
-	
 		// Player is dead, so keep the weapon off screen
+		P_SetPsprite(player, PSP_FLASH, nullptr);
 		psp->SetState(nullptr);
 		return 0;
 	}
@@ -1157,41 +1170,39 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ClearOverlays)
 	PARAM_INT_OPT(stop) { stop = 0; }
 	PARAM_BOOL_OPT(safety) { safety = true; }
 
-	if (!self->player)
+	if (self->player == nullptr)
 		ACTION_RETURN_INT(0);
 
-	player_t *player = self->player;
 	if (!start && !stop)
 	{
 		start = INT_MIN;
 		stop = safety ? PSP_TARGETCENTER - 1 : INT_MAX;
 	}
 
-	int count = 0;
-	DPSprite *pspr = player->psprites;
-	while (pspr != nullptr)
-	{
-		int id = pspr->GetID();
+	unsigned int count = 0;
+	int id;
 
-		//Do not wipe out layer 0. Ever.
-		if (!id || id < start)
+	for (DPSprite *pspr = self->player->psprites; pspr != nullptr; pspr = pspr->GetNext())
+	{
+		id = pspr->GetID();
+
+		if (id < start || id == 0)
 			continue;
-		if (id > stop)
+		else if (id > stop)
 			break;
 
 		if (safety)
 		{
 			if (id >= PSP_TARGETCENTER)
 				break;
-			else if ((id >= PSP_STRIFEHANDS && id <= PSP_WEAPON) || (id == PSP_FLASH))
+			else if (id == PSP_STRIFEHANDS || id == PSP_WEAPON || id == PSP_FLASH)
 				continue;
 		}
 
-		// [MC]Don't affect non-hardcoded layers unless it's really desired.
 		pspr->SetState(nullptr);
 		count++;
-		pspr = pspr->GetNext();
 	}
+
 	ACTION_RETURN_INT(count);
 }
 
diff --git a/src/p_pspr.h b/src/p_pspr.h
index 2c7a36518..41d258df8 100644
--- a/src/p_pspr.h
+++ b/src/p_pspr.h
@@ -42,7 +42,7 @@ class FArchive;
 // drawn directly on the view screen,
 // coordinates are given for a 320*200 view screen.
 //
-enum PSPLayers // These are all called by the owner's ReadyWeapon.
+enum PSPLayers
 {
 	PSP_STRIFEHANDS = -1,
 	PSP_WEAPON = 1,
@@ -76,6 +76,7 @@ public:
 	FState*		GetState()	const { return State; }
 	DPSprite*	GetNext()	      { return Next; }
 	AActor*		GetCaller()	      { return Caller; }
+	void		SetCaller(AActor *newcaller) { Caller = newcaller; }
 
 	double x, y;
 	double oldx, oldy;
diff --git a/src/portal.cpp b/src/portal.cpp
index f180a4fbf..bf919f09a 100644
--- a/src/portal.cpp
+++ b/src/portal.cpp
@@ -288,7 +288,7 @@ static void SetRotation(FLinePortal *port)
 			}
 			else
 			{
-				port->mFlags &= PORTF_POLYOBJ;
+				port->mFlags &= ~PORTF_POLYOBJ;
 			}
 		}
 		else
diff --git a/src/posix/cocoa/i_main.mm b/src/posix/cocoa/i_main.mm
index 5729a3716..bbe344809 100644
--- a/src/posix/cocoa/i_main.mm
+++ b/src/posix/cocoa/i_main.mm
@@ -220,8 +220,8 @@ int OriginalMain(int argc, char** argv)
 
 
 @interface ApplicationController : NSResponder
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
-	<NSFileManagerDelegate>
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
+	<NSApplicationDelegate>
 #endif
 {
 }
diff --git a/src/r_plane.cpp b/src/r_plane.cpp
index 75826d328..e25812fbd 100644
--- a/src/r_plane.cpp
+++ b/src/r_plane.cpp
@@ -1791,7 +1791,7 @@ void R_DrawTiltedPlane (visplane_t *pl, double _xscale, double _yscale, fixed_t
 	// the textures a constant size across the surface's plane instead.
 	cosine = cos(planeang), sine = sin(planeang);
 	m[1] = pl->height.ZatPoint(ViewPos.X + yscale * sine, ViewPos.Y + yscale * cosine) - zeroheight;
-	n[1] = pl->height.ZatPoint(ViewPos.X - xscale * cosine, ViewPos.Y + xscale * sine) - zeroheight;
+	n[1] = -(pl->height.ZatPoint(ViewPos.X - xscale * cosine, ViewPos.Y + xscale * sine) - zeroheight);
 
 	plane_su = p ^ m;
 	plane_sv = p ^ n;
diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp
index 7a9ac1ce0..63ba6ec30 100644
--- a/src/thingdef/thingdef_codeptr.cpp
+++ b/src/thingdef/thingdef_codeptr.cpp
@@ -329,13 +329,19 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetDistance)
 // NON-ACTION function to get the angle in degrees (normalized to -180..180)
 //
 //==========================================================================
+enum GAFlags
+{
+	GAF_RELATIVE =			1,
+	GAF_SWITCH =			1 << 1,
+};
+
 DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetAngle)
 {
 	if (numret > 0)
 	{
 		assert(ret != NULL);
 		PARAM_SELF_PROLOGUE(AActor);
-		PARAM_BOOL(relative);
+		PARAM_INT(flags);
 		PARAM_INT_OPT(ptr) { ptr = AAPTR_TARGET; }
 
 		AActor *target = COPY_AAPTR(self, ptr);
@@ -346,9 +352,10 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetAngle)
 		}
 		else
 		{
-			DVector3 diff = self->Vec3To(target);
+			DVector3 diff = (flags & GAF_SWITCH) ? target->Vec3To(self) : self->Vec3To(target);
 			DAngle angto = diff.Angle();
-			if (relative) angto = deltaangle(self->Angles.Yaw, angto);
+			DAngle yaw = (flags & GAF_SWITCH) ? target->Angles.Yaw : self->Angles.Yaw;
+			if (flags & GAF_RELATIVE) angto = deltaangle(yaw, angto);
 			ret->SetFloat(angto.Degrees);
 		}
 		return 1;
@@ -1615,8 +1622,13 @@ enum CBA_Flags
 	CBAF_EXPLICITANGLE = 4,
 	CBAF_NOPITCH = 8,
 	CBAF_NORANDOMPUFFZ = 16,
+	CBAF_PUFFTARGET = 32,
+	CBAF_PUFFMASTER = 64,
+	CBAF_PUFFTRACER = 128,
 };
 
+static void AimBulletMissile(AActor *proj, AActor *puff, int flags, bool temp, bool cba);
+
 DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomBulletAttack)
 {
 	PARAM_SELF_PROLOGUE(AActor);
@@ -1628,6 +1640,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomBulletAttack)
 	PARAM_FLOAT_OPT	(range)			   { range = 0; }
 	PARAM_INT_OPT	(flags)			   { flags = 0; }
 	PARAM_INT_OPT	(ptr)			   { ptr = AAPTR_TARGET; }
+	PARAM_CLASS_OPT (missile, AActor)	{ missile = nullptr; }
+	PARAM_FLOAT_OPT (Spawnheight)		{ Spawnheight = 32; }
+	PARAM_FLOAT_OPT (Spawnofs_xy)		{ Spawnofs_xy = 0; }
 
 	AActor *ref = COPY_AAPTR(self, ptr);
 
@@ -1672,7 +1687,30 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomBulletAttack)
 			if (!(flags & CBAF_NORANDOM))
 				damage *= ((pr_cabullet()%3)+1);
 
-			P_LineAttack(self, angle, range, slope, damage, NAME_Hitscan, pufftype, laflags);
+			AActor *puff = P_LineAttack(self, angle, range, slope, damage, NAME_Hitscan, pufftype, laflags);
+			if (missile != nullptr && pufftype != nullptr)
+			{
+				double x = Spawnofs_xy * angle.Cos();
+				double y = Spawnofs_xy * angle.Sin();
+				
+				DVector3 pos = self->Pos();
+				self->SetXYZ(self->Vec3Offset(x, y, 0.));
+				AActor *proj = P_SpawnMissileAngleZSpeed(self, self->Z() + self->GetBobOffset() + Spawnheight, missile, self->Angles.Yaw, 0, GetDefaultByType(missile)->Speed, self, false);
+				self->SetXYZ(pos);
+				
+				if (proj)
+				{
+					bool temp = (puff == nullptr);
+					if (!puff)
+					{
+						puff = P_LineAttack(self, angle, range, slope, 0, NAME_Hitscan, pufftype, laflags | LAF_NOINTERACT);
+					}
+					if (puff)
+					{			
+						AimBulletMissile(proj, puff, flags, temp, true);
+					}
+				}
+			}
 		}
     }
 	return 0;
@@ -1801,8 +1839,46 @@ enum FB_Flags
 	FBF_NOPITCH = 8,
 	FBF_NOFLASH = 16,
 	FBF_NORANDOMPUFFZ = 32,
+	FBF_PUFFTARGET = 64,
+	FBF_PUFFMASTER = 128,
+	FBF_PUFFTRACER = 256,
 };
 
+static void AimBulletMissile(AActor *proj, AActor *puff, int flags, bool temp, bool cba)
+{
+	if (proj && puff)
+	{
+		if (proj)
+		{
+			// FAF_BOTTOM = 1
+			// Aim for the base of the puff as that's where blood puffs will spawn... roughly.
+
+			A_Face(proj, puff, 0., 0., 0., 0., 1);
+			proj->Vel3DFromAngle(-proj->Angles.Pitch, proj->Speed);
+
+			if (!temp)
+			{
+				if (cba)
+				{
+					if (flags & CBAF_PUFFTARGET)	proj->target = puff;
+					if (flags & CBAF_PUFFMASTER)	proj->master = puff;
+					if (flags & CBAF_PUFFTRACER)	proj->tracer = puff;
+				}
+				else
+				{
+					if (flags & FBF_PUFFTARGET)	proj->target = puff;
+					if (flags & FBF_PUFFMASTER)	proj->master = puff;
+					if (flags & FBF_PUFFTRACER)	proj->tracer = puff;
+				}
+			}
+		}
+	}
+	if (puff && temp)
+	{
+		puff->Destroy();
+	}
+}
+
 DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireBullets)
 {
 	PARAM_ACTION_PROLOGUE;
@@ -1810,9 +1886,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireBullets)
 	PARAM_ANGLE		(spread_z);
 	PARAM_INT		(numbullets);
 	PARAM_INT		(damageperbullet);
-	PARAM_CLASS_OPT	(pufftype, AActor)	{ pufftype = NULL; }
+	PARAM_CLASS_OPT	(pufftype, AActor)	{ pufftype = nullptr; }
 	PARAM_INT_OPT	(flags)				{ flags = FBF_USEAMMO; }
 	PARAM_FLOAT_OPT	(range)				{ range = 0; }
+	PARAM_CLASS_OPT (missile, AActor)	{ missile = nullptr; }
+	PARAM_FLOAT_OPT (Spawnheight)		{ Spawnheight = 0; }
+	PARAM_FLOAT_OPT (Spawnofs_xy)		{ Spawnofs_xy = 0; }
 
 	if (!self->player) return 0;
 
@@ -1851,7 +1930,24 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireBullets)
 		if (!(flags & FBF_NORANDOM))
 			damage *= ((pr_cwbullet()%3)+1);
 
-		P_LineAttack(self, bangle, range, bslope, damage, NAME_Hitscan, pufftype, laflags);
+		AActor *puff = P_LineAttack(self, bangle, range, bslope, damage, NAME_Hitscan, pufftype, laflags);
+
+		if (missile != nullptr)
+		{
+			bool temp = false;
+			DAngle ang = self->Angles.Yaw - 90;
+			DVector2 ofs = ang.ToVector(Spawnofs_xy);
+			AActor *proj = P_SpawnPlayerMissile(self, ofs.X, ofs.Y, Spawnheight, missile, bangle, nullptr, nullptr, false, true);
+			if (proj)
+			{
+				if (!puff)
+				{
+					temp = true;
+					puff = P_LineAttack(self, bangle, range, bslope, 0, NAME_Hitscan, pufftype, laflags | LAF_NOINTERACT);
+				}
+				AimBulletMissile(proj, puff, flags, temp, false);
+			}
+		}
 	}
 	else 
 	{
@@ -1878,7 +1974,24 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireBullets)
 			if (!(flags & FBF_NORANDOM))
 				damage *= ((pr_cwbullet()%3)+1);
 
-			P_LineAttack(self, angle, range, slope, damage, NAME_Hitscan, pufftype, laflags);
+			AActor *puff = P_LineAttack(self, angle, range, slope, damage, NAME_Hitscan, pufftype, laflags);
+
+			if (missile != nullptr)
+			{
+				bool temp = false;
+				DAngle ang = self->Angles.Yaw - 90;
+				DVector2 ofs = ang.ToVector(Spawnofs_xy);
+				AActor *proj = P_SpawnPlayerMissile(self, ofs.X, ofs.Y, Spawnheight, missile, angle, nullptr, nullptr, false, true);
+				if (proj)
+				{
+					if (!puff)
+					{
+						temp = true;
+						puff = P_LineAttack(self, angle, range, slope, 0, NAME_Hitscan, pufftype, laflags | LAF_NOINTERACT);
+					}
+					AimBulletMissile(proj, puff, flags, temp, false);
+				}
+			}
 		}
 	}
 	return 0;
@@ -5669,7 +5782,9 @@ enum RadiusGiveFlags
 						RGF_MONSTERS |
 						RGF_OBJECTS |
 						RGF_VOODOO |
-						RGF_CORPSES | RGF_MISSILES,
+						RGF_CORPSES | 
+						RGF_MISSILES |
+						RGF_ITEMS,
 };
 
 static bool DoRadiusGive(AActor *self, AActor *thing, PClassActor *item, int amount, double distance, int flags, PClassActor *filter, FName species, double mindist)
@@ -7068,3 +7183,4 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FaceMovementDirection)
 	}
 	ACTION_RETURN_BOOL(true);
 }
+
diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp
index efb7be0c2..a86e98566 100644
--- a/src/thingdef/thingdef_data.cpp
+++ b/src/thingdef/thingdef_data.cpp
@@ -265,6 +265,12 @@ static FFlagDef ActorFlagDefs[]=
 	DEFINE_FLAG(RF, INVISIBLE, AActor, renderflags),
 	DEFINE_FLAG(RF, FORCEYBILLBOARD, AActor, renderflags),
 	DEFINE_FLAG(RF, FORCEXYBILLBOARD, AActor, renderflags),
+	DEFINE_FLAG(RF, ROLLSPRITE, AActor, renderflags), // [marrub] roll the sprite billboard 
+			// [fgsfds] Flat sprites
+	DEFINE_FLAG(RF, FLATSPRITE, AActor, renderflags),
+	DEFINE_FLAG(RF, WALLSPRITE, AActor, renderflags),
+	DEFINE_FLAG(RF, DONTFLIP, AActor, renderflags),
+	DEFINE_FLAG(RF, ROLLCENTER, AActor, renderflags),
 
 	// Bounce flags
 	DEFINE_FLAG2(BOUNCE_Walls, BOUNCEONWALLS, AActor, BounceFlags),
diff --git a/src/thingdef/thingdef_exp.cpp b/src/thingdef/thingdef_exp.cpp
index ca812e2e5..35932e0be 100644
--- a/src/thingdef/thingdef_exp.cpp
+++ b/src/thingdef/thingdef_exp.cpp
@@ -331,11 +331,11 @@ static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls)
 	}
 	else if (sc.CheckToken(TK_True))
 	{
-		return new FxConstant(1, scpos);
+		return new FxConstant(true, scpos);
 	}
 	else if (sc.CheckToken(TK_False))
 	{
-		return new FxConstant(0, scpos);
+		return new FxConstant(false, scpos);
 	}
 	else if (sc.CheckToken(TK_IntConst))
 	{
diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h
index ac6b7eda3..58820ea15 100644
--- a/src/thingdef/thingdef_exp.h
+++ b/src/thingdef/thingdef_exp.h
@@ -203,7 +203,6 @@ protected:
 public:
 	virtual ~FxExpression() {}
 	virtual FxExpression *Resolve(FCompileContext &ctx);
-	FxExpression *ResolveAsBoolean(FCompileContext &ctx);
 	
 	virtual bool isConstant() const;
 	virtual void RequestAddress();
@@ -280,6 +279,13 @@ class FxConstant : public FxExpression
 	ExpVal value;
 
 public:
+	FxConstant(bool val, const FScriptPosition &pos) : FxExpression(pos)
+	{
+		ValueType = value.Type = TypeBool;
+		value.Int = val;
+		isresolved = true;
+	}
+
 	FxConstant(int val, const FScriptPosition &pos) : FxExpression(pos)
 	{
 		ValueType = value.Type = TypeSInt32;
@@ -358,6 +364,19 @@ public:
 //
 //==========================================================================
 
+class FxBoolCast : public FxExpression
+{
+	FxExpression *basex;
+
+public:
+
+	FxBoolCast(FxExpression *x);
+	~FxBoolCast();
+	FxExpression *Resolve(FCompileContext&);
+
+	ExpEmit Emit(VMFunctionBuilder *build);
+};
+
 class FxIntCast : public FxExpression
 {
 	FxExpression *basex;
@@ -384,18 +403,6 @@ public:
 	ExpEmit Emit(VMFunctionBuilder *build);
 };
 
-class FxCastStateToBool : public FxExpression
-{
-	FxExpression *basex;
-
-public:
-	FxCastStateToBool(FxExpression *x);
-	~FxCastStateToBool();
-	FxExpression *Resolve(FCompileContext&);
-
-	ExpEmit Emit(VMFunctionBuilder *build);
-};
-
 //==========================================================================
 //
 //	FxSign
diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp
index 04410189a..b4a0b3fda 100644
--- a/src/thingdef/thingdef_expression.cpp
+++ b/src/thingdef/thingdef_expression.cpp
@@ -177,38 +177,6 @@ FxExpression *FxExpression::Resolve(FCompileContext &ctx)
 	return this;
 }
 
-
-//==========================================================================
-//
-//
-//
-//==========================================================================
-
-FxExpression *FxExpression::ResolveAsBoolean(FCompileContext &ctx)
-{
-	///FIXME: Use an actual boolean type
-	FxExpression *x = Resolve(ctx);
-	if (x != NULL)
-	{
-		if (x->ValueType->GetRegType() == REGT_INT)
-		{
-			x->ValueType = TypeSInt32;
-		}
-		else if (x->ValueType == TypeState)
-		{
-			x = new FxCastStateToBool(x);
-			x = x->Resolve(ctx);
-		}
-		else
-		{
-			ScriptPosition.Message(MSG_ERROR, "Not an integral type");
-			delete this;
-			return NULL;
-		}
-	}
-	return x;
-}
-
 //==========================================================================
 //
 //
@@ -327,6 +295,104 @@ ExpEmit FxConstant::Emit(VMFunctionBuilder *build)
 //
 //==========================================================================
 
+FxBoolCast::FxBoolCast(FxExpression *x)
+	: FxExpression(x->ScriptPosition)
+{
+	basex = x;
+	ValueType = TypeBool;
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+FxBoolCast::~FxBoolCast()
+{
+	SAFE_DELETE(basex);
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+FxExpression *FxBoolCast::Resolve(FCompileContext &ctx)
+{
+	CHECKRESOLVED();
+	SAFE_RESOLVE(basex, ctx);
+
+	if (basex->ValueType == TypeBool)
+	{
+		FxExpression *x = basex;
+		basex = nullptr;
+		delete this;
+		return x;
+	}
+	else if (basex->ValueType->GetRegType() == REGT_INT || basex->ValueType->GetRegType() == REGT_FLOAT || basex->ValueType->GetRegType() == REGT_POINTER)
+	{
+		if (basex->isConstant())
+		{
+			assert(basex->ValueType != TypeState && "We shouldn't be able to generate a constant state ref");
+
+			ExpVal constval = static_cast<FxConstant *>(basex)->GetValue();
+			FxExpression *x = new FxConstant(constval.GetBool(), ScriptPosition);
+			delete this;
+			return x;
+		}
+		return this;
+	}
+	ScriptPosition.Message(MSG_ERROR, "Numeric type expected");
+	delete this;
+	return nullptr;
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+ExpEmit FxBoolCast::Emit(VMFunctionBuilder *build)
+{
+	ExpEmit from = basex->Emit(build);
+	assert(!from.Konst);
+	assert(basex->ValueType->GetRegType() == REGT_INT || basex->ValueType->GetRegType() == REGT_FLOAT || basex->ValueType->GetRegType() == REGT_POINTER);
+	ExpEmit to(build, REGT_INT);
+	from.Free(build);
+
+	// Preload result with 0.
+	build->Emit(OP_LI, to.RegNum, 0);
+
+	// Check source against 0.
+	if (from.RegType == REGT_INT)
+	{
+		build->Emit(OP_EQ_R, 1, from.RegNum, to.RegNum);
+	}
+	else if (from.RegType == REGT_FLOAT)
+	{
+		build->Emit(OP_EQF_K, 1, from.RegNum, build->GetConstantFloat(0.));
+	}
+	else if (from.RegNum == REGT_POINTER)
+	{
+		build->Emit(OP_EQA_K, 1, from.RegNum, build->GetConstantAddress(nullptr, ATAG_GENERIC));
+	}
+	build->Emit(OP_JMP, 1);
+
+	// Reload result with 1 if the comparison fell through.
+	build->Emit(OP_LI, to.RegNum, 1);
+
+	return to;
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
 FxIntCast::FxIntCast(FxExpression *x)
 : FxExpression(x->ScriptPosition)
 {
@@ -504,67 +570,6 @@ ExpEmit FxFloatCast::Emit(VMFunctionBuilder *build)
 //
 //==========================================================================
 
-FxCastStateToBool::FxCastStateToBool(FxExpression *x)
-: FxExpression(x->ScriptPosition)
-{
-	basex = x;
-	ValueType = TypeSInt32;
-}
-
-//==========================================================================
-//
-//
-//
-//==========================================================================
-
-FxCastStateToBool::~FxCastStateToBool()
-{
-	SAFE_DELETE(basex);
-}
-
-//==========================================================================
-//
-//
-//
-//==========================================================================
-
-FxExpression *FxCastStateToBool::Resolve(FCompileContext &ctx)
-{
-	CHECKRESOLVED();
-	SAFE_RESOLVE(basex, ctx);
-
-	assert(basex->ValueType == TypeState);
-	assert(!basex->isConstant() && "We shouldn't be able to generate a constant state ref");
-	return this;
-}
-
-//==========================================================================
-//
-//
-//
-//==========================================================================
-
-ExpEmit FxCastStateToBool::Emit(VMFunctionBuilder *build)
-{
-	ExpEmit from = basex->Emit(build);
-	assert(from.RegType == REGT_POINTER);
-	from.Free(build);
-	ExpEmit to(build, REGT_INT);
-
-	// If from is NULL, produce 0. Otherwise, produce 1.
-	build->Emit(OP_LI, to.RegNum, 0);
-	build->Emit(OP_EQA_K, 1, from.RegNum, build->GetConstantAddress(NULL, ATAG_GENERIC));
-	build->Emit(OP_JMP, 1);
-	build->Emit(OP_LI, to.RegNum, 1);
-	return to;
-}
-
-//==========================================================================
-//
-//
-//
-//==========================================================================
-
 FxPlusSign::FxPlusSign(FxExpression *operand)
 : FxExpression(operand->ScriptPosition)
 {
@@ -765,10 +770,9 @@ FxExpression *FxUnaryNotBitwise::Resolve(FCompileContext& ctx)
 
 ExpEmit FxUnaryNotBitwise::Emit(VMFunctionBuilder *build)
 {
-	assert(ValueType == Operand->ValueType);
-	assert(ValueType == TypeSInt32);
+	assert(Operand->ValueType->GetRegType() == REGT_INT);
 	ExpEmit from = Operand->Emit(build);
-	assert(from.Konst == 0);
+	assert(!from.Konst);
 	// Do it in-place.
 	build->Emit(OP_NOT, from.RegNum, from.RegNum, 0);
 	return from;
@@ -806,33 +810,23 @@ FxUnaryNotBoolean::~FxUnaryNotBoolean()
 FxExpression *FxUnaryNotBoolean::Resolve(FCompileContext& ctx)
 {
 	CHECKRESOLVED();
-	if (Operand)
+	SAFE_RESOLVE(Operand, ctx);
+
+	if (Operand->ValueType != TypeBool)
 	{
-		Operand = Operand->ResolveAsBoolean(ctx);
-	}
-	if (!Operand)
-	{
-		delete this;
-		return NULL;
+		Operand = new FxBoolCast(Operand);
+		SAFE_RESOLVE(Operand, ctx);
 	}
 
-	if (Operand->IsNumeric() || Operand->IsPointer())
+	if (Operand->isConstant())
 	{
-		if (Operand->isConstant())
-		{
-			bool result = !static_cast<FxConstant *>(Operand)->GetValue().GetBool();
-			FxExpression *e = new FxConstant(result, ScriptPosition);
-			delete this;
-			return e;
-		}
-	}
-	else
-	{
-		ScriptPosition.Message(MSG_ERROR, "Numeric type expected");
+		bool result = !static_cast<FxConstant *>(Operand)->GetValue().GetBool();
+		FxExpression *e = new FxConstant(result, ScriptPosition);
 		delete this;
-		return NULL;
+		return e;
 	}
-	ValueType = TypeSInt32;
+
+	ValueType = TypeBool;
 	return this;
 }
 
@@ -844,32 +838,14 @@ FxExpression *FxUnaryNotBoolean::Resolve(FCompileContext& ctx)
 
 ExpEmit FxUnaryNotBoolean::Emit(VMFunctionBuilder *build)
 {
+	assert(Operand->ValueType == ValueType);
+	assert(ValueType == TypeBool);
 	ExpEmit from = Operand->Emit(build);
 	assert(!from.Konst);
-	ExpEmit to(build, REGT_INT);
-	from.Free(build);
-
-	// Preload result with 0.
-	build->Emit(OP_LI, to.RegNum, 0, 0);
-
-	// Check source against 0.
-	if (from.RegType == REGT_INT)
-	{
-		build->Emit(OP_EQ_R, 0, from.RegNum, to.RegNum);
-	}
-	else if (from.RegType == REGT_FLOAT)
-	{
-		build->Emit(OP_EQF_K, 0, from.RegNum, build->GetConstantFloat(0));
-	}
-	else if (from.RegNum == REGT_POINTER)
-	{
-		build->Emit(OP_EQA_K, 0, from.RegNum, build->GetConstantAddress(NULL, ATAG_GENERIC));
-	}
-	build->Emit(OP_JMP, 1);
-
-	// Reload result with 1 if the comparison fell through.
-	build->Emit(OP_LI, to.RegNum, 1);
-	return to;
+	// ~x & 1
+	build->Emit(OP_NOT, from.RegNum, from.RegNum, 0);
+	build->Emit(OP_AND_RK, from.RegNum, from.RegNum, build->GetConstantInt(1));
+	return from;
 }
 
 //==========================================================================
@@ -914,6 +890,10 @@ bool FxBinary::ResolveLR(FCompileContext& ctx, bool castnumeric)
 		return false;
 	}
 
+	if (left->ValueType == TypeBool && right->ValueType == TypeBool)
+	{
+		ValueType = TypeBool;
+	}
 	if (left->ValueType->GetRegType() == REGT_INT && right->ValueType->GetRegType() == REGT_INT)
 	{
 		ValueType = TypeSInt32;
@@ -1276,7 +1256,7 @@ FxExpression *FxCompareRel::Resolve(FCompileContext& ctx)
 		return e;
 	}
 	Promote(ctx);
-	ValueType = TypeSInt32;
+	ValueType = TypeBool;
 	return this;
 }
 
@@ -1327,7 +1307,7 @@ ExpEmit FxCompareRel::Emit(VMFunctionBuilder *build)
 		op1.Free(build);
 	}
 
-	// See FxUnaryNotBoolean for comments, since it's the same thing.
+	// See FxBoolCast for comments, since it's the same thing.
 	build->Emit(OP_LI, to.RegNum, 0, 0);
 	build->Emit(instr, check, op1.RegNum, op2.RegNum);
 	build->Emit(OP_JMP, 1);
@@ -1392,7 +1372,7 @@ FxExpression *FxCompareEq::Resolve(FCompileContext& ctx)
 		return e;
 	}
 	Promote(ctx);
-	ValueType = TypeSInt32;
+	ValueType = TypeBool;
 	return this;
 }
 
@@ -1600,7 +1580,7 @@ FxBinaryLogical::FxBinaryLogical(int o, FxExpression *l, FxExpression *r)
 	Operator=o;
 	left=l;
 	right=r;
-	ValueType = TypeSInt32;
+	ValueType = TypeBool;
 }
 
 //==========================================================================
@@ -1624,16 +1604,22 @@ FxBinaryLogical::~FxBinaryLogical()
 FxExpression *FxBinaryLogical::Resolve(FCompileContext& ctx)
 {
 	CHECKRESOLVED();
-	if (left) left = left->ResolveAsBoolean(ctx);
-	if (right) right = right->ResolveAsBoolean(ctx);
-	if (!left || !right)
+	RESOLVE(left, ctx);
+	RESOLVE(right, ctx);
+	ABORT(right && left);
+
+	if (left->ValueType != TypeBool)
 	{
-		delete this;
-		return NULL;
+		left = new FxBoolCast(left);
+		SAFE_RESOLVE(left, ctx);
+	}
+	if (right->ValueType != TypeBool)
+	{
+		right = new FxBoolCast(right);
+		SAFE_RESOLVE(right, ctx);
 	}
 
 	int b_left=-1, b_right=-1;
-
 	if (left->isConstant()) b_left = static_cast<FxConstant *>(left)->GetValue().GetBool();
 	if (right->isConstant()) b_right = static_cast<FxConstant *>(right)->GetValue().GetBool();
 
@@ -1643,13 +1629,13 @@ FxExpression *FxBinaryLogical::Resolve(FCompileContext& ctx)
 	{
 		if (b_left==0 || b_right==0)
 		{
-			FxExpression *x = new FxConstant(0, ScriptPosition);
+			FxExpression *x = new FxConstant(true, ScriptPosition);
 			delete this;
 			return x;
 		}
 		else if (b_left==1 && b_right==1)
 		{
-			FxExpression *x = new FxConstant(1, ScriptPosition);
+			FxExpression *x = new FxConstant(false, ScriptPosition);
 			delete this;
 			return x;
 		}
@@ -1672,13 +1658,13 @@ FxExpression *FxBinaryLogical::Resolve(FCompileContext& ctx)
 	{
 		if (b_left==1 || b_right==1)
 		{
-			FxExpression *x = new FxConstant(1, ScriptPosition);
+			FxExpression *x = new FxConstant(true, ScriptPosition);
 			delete this;
 			return x;
 		}
 		if (b_left==0 && b_right==0)
 		{
-			FxExpression *x = new FxConstant(0, ScriptPosition);
+			FxExpression *x = new FxConstant(false, ScriptPosition);
 			delete this;
 			return x;
 		}
@@ -1697,14 +1683,6 @@ FxExpression *FxBinaryLogical::Resolve(FCompileContext& ctx)
 			return x;
 		}
 	}
-	if (left->ValueType->GetRegType() != REGT_INT)
-	{
-		left = new FxIntCast(left);
-	}
-	if (right->ValueType->GetRegType() != REGT_INT)
-	{
-		right = new FxIntCast(right);
-	}
 	return this;
 }
 
@@ -1804,17 +1782,25 @@ FxConditional::~FxConditional()
 FxExpression *FxConditional::Resolve(FCompileContext& ctx)
 {
 	CHECKRESOLVED();
-	if (condition) condition = condition->ResolveAsBoolean(ctx);
+	RESOLVE(condition, ctx);
 	RESOLVE(truex, ctx);
 	RESOLVE(falsex, ctx);
 	ABORT(condition && truex && falsex);
 
-	if (truex->ValueType->GetRegType() == REGT_INT && falsex->ValueType->GetRegType() == REGT_INT)
+	if (truex->ValueType == TypeBool && falsex->ValueType == TypeBool)
+		ValueType = TypeBool;
+	else if (truex->ValueType->GetRegType() == REGT_INT && falsex->ValueType->GetRegType() == REGT_INT)
 		ValueType = TypeSInt32;
 	else if (truex->IsNumeric() && falsex->IsNumeric())
 		ValueType = TypeFloat64;
 	//else if (truex->ValueType != falsex->ValueType)
 
+	if (condition->ValueType != TypeBool)
+	{
+		condition = new FxBoolCast(condition);
+		SAFE_RESOLVE(condition, ctx);
+	}
+
 	if (condition->isConstant())
 	{
 		ExpVal condval = static_cast<FxConstant *>(condition)->GetValue();
@@ -1852,6 +1838,7 @@ FxExpression *FxConditional::Resolve(FCompileContext& ctx)
 
 ExpEmit FxConditional::Emit(VMFunctionBuilder *build)
 {
+	size_t truejump, falsejump;
 	ExpEmit out;
 
 	// The true and false expressions ought to be assigned to the
@@ -1862,7 +1849,7 @@ ExpEmit FxConditional::Emit(VMFunctionBuilder *build)
 
 	// Test condition.
 	build->Emit(OP_EQ_K, 1, cond.RegNum, build->GetConstantInt(0));
-	size_t patchspot = build->Emit(OP_JMP, 0);
+	falsejump = build->Emit(OP_JMP, 0);
 
 	// Evaluate true expression.
 	if (truex->isConstant() && truex->ValueType->GetRegType() == REGT_INT)
@@ -1886,9 +1873,11 @@ ExpEmit FxConditional::Emit(VMFunctionBuilder *build)
 			out = trueop;
 		}
 	}
+	// Make sure to skip the false path.
+	truejump = build->Emit(OP_JMP, 0);
 
 	// Evaluate false expression.
-	build->BackpatchToHere(patchspot);
+	build->BackpatchToHere(falsejump);
 	if (falsex->isConstant() && falsex->ValueType->GetRegType() == REGT_INT)
 	{
 		build->EmitLoadInt(out.RegNum, static_cast<FxConstant *>(falsex)->GetValue().GetInt());
@@ -1918,6 +1907,7 @@ ExpEmit FxConditional::Emit(VMFunctionBuilder *build)
 			}
 		}
 	}
+	build->BackpatchToHere(truejump);
 
 	return out;
 }
@@ -3735,23 +3725,32 @@ FxIfStatement::~FxIfStatement()
 FxExpression *FxIfStatement::Resolve(FCompileContext &ctx)
 {
 	CHECKRESOLVED();
-	if (WhenTrue == NULL && WhenFalse == NULL)
+
+	if (WhenTrue == nullptr && WhenFalse == nullptr)
 	{ // We don't do anything either way, so disappear
 		delete this;
-		return NULL;
+		return nullptr;
 	}
-	Condition = Condition->ResolveAsBoolean(ctx);
-	ABORT(Condition);
-	if (WhenTrue != NULL)
+
+	SAFE_RESOLVE(Condition, ctx);
+
+	if (Condition->ValueType != TypeBool)
+	{
+		Condition = new FxBoolCast(Condition);
+		SAFE_RESOLVE(Condition, ctx);
+	}
+
+	if (WhenTrue != nullptr)
 	{
 		WhenTrue = WhenTrue->Resolve(ctx);
 		ABORT(WhenTrue);
 	}
-	if (WhenFalse != NULL)
+	if (WhenFalse != nullptr)
 	{
 		WhenFalse = WhenFalse->Resolve(ctx);
 		ABORT(WhenFalse);
 	}
+
 	ValueType = TypeVoid;
 
 	if (Condition->isConstant())
@@ -3766,6 +3765,7 @@ FxExpression *FxIfStatement::Resolve(FCompileContext &ctx)
 		delete this;
 		return e;
 	}
+
 	return this;
 }
 
diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp
index e4488de45..c97930277 100644
--- a/src/thingdef/thingdef_parse.cpp
+++ b/src/thingdef/thingdef_parse.cpp
@@ -77,7 +77,7 @@ FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, PType *type, bool c
 		sc.MustGetString();
 		x = new FxConstant(FSoundID(sc.String), sc);
 	}
-	else if (type == TypeSInt32 || type == TypeFloat64)
+	else if (type == TypeBool || type == TypeSInt32 || type == TypeFloat64)
 	{
 		x = ParseExpression (sc, cls, constant);
 		if (constant && !x->isConstant())
@@ -85,8 +85,12 @@ FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, PType *type, bool c
 			sc.ScriptMessage("Default parameter must be constant.");
 			FScriptPosition::ErrorCounter++;
 		}
-		// Do automatic coercion between ints and floats.
-		if (type == TypeSInt32)
+		// Do automatic coercion between bools, ints and floats.
+		if (type == TypeBool)
+		{
+			x = new FxBoolCast(x);
+		}
+		else if (type == TypeSInt32)
 		{
 			x = new FxIntCast(x);
 		}
@@ -306,6 +310,9 @@ static void ParseArgListDef(FScanner &sc, PClassActor *cls,
 			switch (sc.TokenType)
 			{
 			case TK_Bool:
+				type = TypeBool;
+				break;
+
 			case TK_Int:
 				type = TypeSInt32;
 				break;
@@ -477,8 +484,11 @@ static void ParseNativeFunction(FScanner &sc, PClassActor *cls)
 	sc.MustGetAnyToken();
 	switch (sc.TokenType)
 	{
-	case TK_Int:
 	case TK_Bool:
+		rets.Push(TypeBool);
+		break;
+
+	case TK_Int:
 		rets.Push(TypeSInt32);
 		break;
 
@@ -1064,7 +1074,11 @@ static void ParseActionDef (FScanner &sc, PClassActor *cls)
 	// check for a return value
 	do
 	{
-		if (sc.CheckToken(TK_Int) || sc.CheckToken(TK_Bool))
+		if (sc.CheckToken(TK_Bool))
+		{
+			rets.Push(TypeBool);
+		}
+		else if (sc.CheckToken(TK_Int))
 		{
 			rets.Push(TypeSInt32);
 		}
diff --git a/strifehelp.acs b/strifehelp.acs
index 9176ca006..495893edf 100644
--- a/strifehelp.acs
+++ b/strifehelp.acs
@@ -1,6 +1,7 @@
+#library "strfhelp"
+
 #include "zcommon.acs"
 
-#library "strfhelp"
 
 #define VDOORSPEED  16
 #define VDOORWAIT   150
@@ -69,6 +70,7 @@ script << 0 >> (int type, int tag)
 	
 	case 230:
 		i = GetLineRowOffset() & 31;
+		if (i == 0) break;
 		if (CheckInventory (QuestItems[i]) || gametype() == GAME_NET_DEATHMATCH)
 		{
 			Door_Open (tag, VDOORSPEED);
@@ -78,6 +80,7 @@ script << 0 >> (int type, int tag)
 		
 	case 227:
 		i = GetLineRowOffset() & 31;
+		if (i == 0) break;
 		if (CheckInventory (QuestItems[i]) || gametype() == GAME_NET_DEATHMATCH)
 		{
 			Door_Close (tag, VDOORSPEED);
@@ -126,6 +129,7 @@ script << 0 >> (int type, int tag)
 
 	case 193:
 		i = GetLineRowOffset() & 31;
+		if (i == 0) break;
 		if (CheckInventory (QuestItems[i]) || gametype() == GAME_NET_DEATHMATCH)
 		{
 			Floor_LowerToLowest (tag, 8);
@@ -158,6 +162,7 @@ script << 0 >> (int type, int tag)
 
 	case 187:
 		i = GetLineRowOffset() & 31;
+		if (i == 0) break;
 		if (CheckInventory (QuestItems[i]) || gametype() == GAME_NET_DEATHMATCH)
 		{
 			ClearForceField (tag);
@@ -203,6 +208,7 @@ script << 0 >> (int type, int tag)
 	
 	case 216:
 		i = GetLineRowOffset() & 31;
+		if (i == 0) break;
 		if (CheckInventory (QuestItems[i]) || gametype() == GAME_NET_DEATHMATCH)
 		{
 			Door_Raise (tag, VDOORSPEED, VDOORWAIT);
diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt
index a531838d0..8c35df21b 100644
--- a/wadsrc/static/actors/actor.txt
+++ b/wadsrc/static/actors/actor.txt
@@ -34,15 +34,14 @@ ACTOR Actor native //: Thinker
 	DefThreshold 100
 	BloodType "Blood", "BloodSplatter", "AxeBlood"
 	ExplosionDamage 128
-	MissileHeight 32
-	
+	MissileHeight 32	
 
 	// Functions
 	native bool CheckClass(class<Actor> checkclass, int ptr_select = AAPTR_DEFAULT, bool match_superclass = false);
 	native bool IsPointerEqual(int ptr_select1, int ptr_select2);
 	native int	CountInv(class<Inventory> itemtype, int ptr_select = AAPTR_DEFAULT);
 	native float GetDistance(bool checkz, int ptr = AAPTR_DEFAULT);
-	native float GetAngle(bool relative, int ptr = AAPTR_DEFAULT);
+	native float GetAngle(int flags, int ptr = AAPTR_DEFAULT);
 	native float GetZAt(float px = 0, float py = 0, float angle = 0, int flags = 0, int pick_pointer = AAPTR_DEFAULT);
 	native int GetSpawnHealth();
 	native int GetGibHealth();
@@ -190,7 +189,7 @@ ACTOR Actor native //: Thinker
 	native void A_SeekerMissile(int threshold, int turnmax, int flags = 0, int chance = 50, int distance = 10);
 	native state A_Jump(int chance = 256, state label, ...);
 	native void A_CustomMissile(class<Actor> missiletype, float spawnheight = 32, float spawnofs_xy = 0, float angle = 0, int flags = 0, float pitch = 0, int ptr = AAPTR_TARGET);
-	native void A_CustomBulletAttack(float/*angle*/ spread_xy, float/*angle*/ spread_z, int numbullets, int damageperbullet, class<Actor> pufftype = "BulletPuff", float range = 0, int flags = 0, int ptr = AAPTR_TARGET);
+	native void A_CustomBulletAttack(float/*angle*/ spread_xy, float/*angle*/ spread_z, int numbullets, int damageperbullet, class<Actor> pufftype = "BulletPuff", float range = 0, int flags = 0, int ptr = AAPTR_TARGET, class<Actor> missile = "", float Spawnheight = 32, float Spawnofs_xy = 0);
 	native void A_CustomRailgun(int damage, int spawnofs_xy = 0, color color1 = "", color color2 = "", int flags = 0, int aim = 0, float maxdiff = 0, class<Actor> pufftype = "BulletPuff", float/*angle*/ spread_xy = 0, float/*angle*/ spread_z = 0, float range = 0, int duration = 0, float sparsity = 1.0, float driftspeed = 1.0, class<Actor> spawnclass = "none", float spawnofs_z = 0, int spiraloffset = 270, int limit = 0);
 	native state A_JumpIfHealthLower(int health, state label, int ptr_selector = AAPTR_DEFAULT);
 	native state A_JumpIfCloser(float distance, state label, bool noz = false);
diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt
index 7b75b481d..05049e598 100644
--- a/wadsrc/static/actors/constants.txt
+++ b/wadsrc/static/actors/constants.txt
@@ -38,6 +38,9 @@ const int CBAF_NORANDOM = 2;
 const int CBAF_EXPLICITANGLE = 4;
 const int CBAF_NOPITCH = 8;
 const int CBAF_NORANDOMPUFFZ = 16;
+const int CBAF_PUFFTARGET = 32;
+const int CBAF_PUFFMASTER = 64;
+const int CBAF_PUFFTRACER = 128;
 
 // Flags for A_GunFlash
 const int GFF_NOEXTCHANGE = 1;
@@ -210,6 +213,9 @@ const int CPF_STEALARMOR = 32;
 const int FPF_AIMATANGLE = 1;
 const int FPF_TRANSFERTRANSLATION = 2;
 const int FPF_NOAUTOAIM = 4;
+const int FBF_PUFFTARGET = 64;
+const int FBF_PUFFMASTER = 128;
+const int FBF_PUFFTRACER = 256;
 
 // Flags for A_Teleport
 enum
@@ -652,3 +658,9 @@ enum
 	BT_USER3		= 1<<23,
 	BT_USER4		= 1<<24,
 };
+// Flags for GetAngle
+enum
+{
+	GAF_RELATIVE =			1,
+	GAF_SWITCH =			1 << 1,
+};
\ No newline at end of file
diff --git a/wadsrc/static/actors/shared/inventory.txt b/wadsrc/static/actors/shared/inventory.txt
index a49dfd14c..43a33f93a 100644
--- a/wadsrc/static/actors/shared/inventory.txt
+++ b/wadsrc/static/actors/shared/inventory.txt
@@ -9,7 +9,7 @@ ACTOR Inventory native
 	
 	action native state A_JumpIfNoAmmo(state label);
 	action native A_CustomPunch(int damage, bool norandom = false, int flags = CPF_USEAMMO, class<Actor> pufftype = "BulletPuff", float range = 0, float lifesteal = 0, int lifestealmax = 0, class<BasicArmorBonus> armorbonustype = "ArmorBonus", sound MeleeSound = "", sound MissSound = "");
-	action native A_FireBullets(float/*angle*/ spread_xy, float/*angle*/ spread_z, int numbullets, int damageperbullet, class<Actor> pufftype = "BulletPuff", int flags = 1, float range = 0);
+	action native A_FireBullets(float/*angle*/ spread_xy, float/*angle*/ spread_z, int numbullets, int damageperbullet, class<Actor> pufftype = "BulletPuff", int flags = 1, float range = 0, class<Actor> missile = "", float Spawnheight = 32, float Spawnofs_xy = 0);
 	action native A_FireCustomMissile(class<Actor> missiletype, float angle = 0, bool useammo = true, float spawnofs_xy = 0, float spawnheight = 0, int flags = 0, float pitch = 0);
 	action native A_RailAttack(int damage, int spawnofs_xy = 0, bool useammo = true, color color1 = "", color color2 = "", int flags = 0, float maxdiff = 0, class<Actor> pufftype = "BulletPuff", float/*angle*/ spread_xy = 0, float/*angle*/ spread_z = 0, float range = 0, int duration = 0, float sparsity = 1.0, float driftspeed = 1.0, class<Actor> spawnclass = "none", float spawnofs_z = 0, int spiraloffset = 270, int limit = 0);
 	action native A_Light(int extralight);
diff --git a/wadsrc/static/filter/game-strife/acs/strfhelp.o b/wadsrc/static/filter/game-strife/acs/strfhelp.o
index 45664cbc4..521d88714 100644
Binary files a/wadsrc/static/filter/game-strife/acs/strfhelp.o and b/wadsrc/static/filter/game-strife/acs/strfhelp.o differ
diff --git a/wadsrc/static/language.eng b/wadsrc/static/language.eng
index ca27bf80a..ed0f95a9a 100644
--- a/wadsrc/static/language.eng
+++ b/wadsrc/static/language.eng
@@ -108,3 +108,5 @@ CMPTMNU_SECTORSOUNDS = "Sector sounds use centre as source";
 OPTVAL_MAPDEFINEDCOLORSONLY = "Map defined colours only";
 C_GRAY = "\ccgrey";
 C_DARKGRAY = "\cudark grey";
+DSPLYMNU_MOVEBOB = "View bob amount while moving";
+DSPLYMNU_STILLBOB = "View bob amount while not moving";
diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu
index 0bb3a84fd..b0cbf4d91 100644
--- a/wadsrc/static/language.enu
+++ b/wadsrc/static/language.enu
@@ -1839,6 +1839,7 @@ ALTHUDMNU_SHOWAMMO				= "Show ammo for";
 ALTHUDMNU_SHOWTIME				= "Show time";
 ALTHUDMNU_TIMECOLOR				= "Time color";
 ALTHUDMNU_SHOWLAG				= "Show network latency";
+ALTHUDMNU_AMMOORDER				= "Ammo display order";
 ALTHUDMNU_AMMORED				= "Red ammo display below %";
 ALTHUDMNU_AMMOYELLOW			= "Yellow ammo display below %";
 ALTHUDMNU_HEALTHRED				= "Red health display below";
@@ -2214,6 +2215,8 @@ OPTVAL_SYSTEMSECONDS		= "System, seconds";
 OPTVAL_SYSTEM				= "System";
 OPTVAL_NETGAMESONLY			= "Netgames only";
 OPTVAL_ALWAYS				= "Always";
+OPTVAL_AMMOIMAGETEXT		= "Image and Text";
+OPTVAL_AMMOTEXTIMAGE		= "Text and Image";
 OPTVAL_SCRIPTSONLY			= "Scripts Only";
 OPTVAL_NEVER				= "Never";
 OPTVAL_ALL					= "All";
diff --git a/wadsrc/static/lockdefs.txt b/wadsrc/static/lockdefs.txt
index 2167f935d..04e490224 100644
--- a/wadsrc/static/lockdefs.txt
+++ b/wadsrc/static/lockdefs.txt
@@ -5,6 +5,7 @@ ClearLocks
 
 Lock 1 Doom
 {
+	//$Title "Red key card"
 	RedCard
 	Message "$PD_REDC"
 	RemoteMessage "$PD_REDCO"
@@ -14,6 +15,7 @@ Lock 1 Doom
 
 Lock 2 Doom
 {
+	//$Title "Blue key card"
 	BlueCard
 	Message "$PD_BLUEC"
 	RemoteMessage "$PD_BLUECO"
@@ -23,6 +25,7 @@ Lock 2 Doom
 
 Lock 3 Doom
 {
+	//$Title "Yellow key card"
 	YellowCard
 	Message "$PD_YELLOWC"
 	RemoteMessage "$PD_YELLOWCO"
@@ -32,6 +35,7 @@ Lock 3 Doom
 
 Lock 4 Doom
 {
+	//$Title "Red skull"
 	RedSkull
 	Message "$PD_REDS"
 	RemoteMessage "$PD_REDSO"
@@ -41,6 +45,7 @@ Lock 4 Doom
 
 Lock 5 Doom
 {
+	//$Title "Blue skull"
 	BlueSkull
 	Message "$PD_BLUES"
 	RemoteMessage "$PD_BLUESO"
@@ -50,6 +55,7 @@ Lock 5 Doom
 
 Lock 6 Doom
 {
+	//$Title "Yellow skull"
 	YellowSkull
 	Message "$PD_YELLOWS"
 	RemoteMessage "$PD_YELLOWSO"
@@ -59,6 +65,7 @@ Lock 6 Doom
 
 Lock 129 Doom
 {
+	//$Title "Any red key"
 	Any { RedCard RedSkull KeyGreen }
 	Message "$PD_REDK"
 	RemoteMessage "$PD_REDO"
@@ -68,6 +75,7 @@ Lock 129 Doom
 
 Lock 130 Doom
 {
+	//$Title "Any blue key"
 	Any { BlueCard BlueSkull KeyBlue }
 	Message "$PD_BLUEK"
 	RemoteMessage "$PD_BLUEO"
@@ -77,6 +85,7 @@ Lock 130 Doom
 
 Lock 131 Doom
 {
+	//$Title "Any yellow key"
 	Any { YellowCard YellowSkull KeyYellow }
 	Message "$PD_YELLOWK"
 	RemoteMessage "$PD_YELLOWO"
@@ -86,6 +95,7 @@ Lock 131 Doom
 
 Lock 132 Doom
 {
+	//$Title "Red card or skull"
 	Any { RedCard RedSkull }
 	Message "$PD_REDK"
 	RemoteMessage "$PD_REDO"
@@ -95,6 +105,7 @@ Lock 132 Doom
 
 Lock 133 Doom
 {
+	//$Title "Blue card or skull"
 	Any { BlueCard BlueSkull }
 	Message "$PD_BLUEK"
 	RemoteMessage "$PD_BLUEO"
@@ -104,6 +115,7 @@ Lock 133 Doom
 
 Lock 134 Doom
 {
+	//$Title "Yellow card or skull"
 	Any { YellowCard YellowSkull }
 	Message "$PD_YELLOWK"
 	RemoteMessage "$PD_YELLOWO"
@@ -112,6 +124,7 @@ Lock 134 Doom
 
 Lock 100
 {
+	//$Title "Any key"
 	Message "$PD_ANY"
 	RemoteMessage "$PD_ANYOBJ"
 	Mapcolor 128 128 255
@@ -119,6 +132,7 @@ Lock 100
 
 Lock 228
 {
+	//$Title "Any key"
 	Message "$PD_ANY"
 	RemoteMessage "$PD_ANYOBJ"
 	Mapcolor 128 128 255
@@ -126,6 +140,7 @@ Lock 228
 
 Lock 229 Doom
 {
+	//$Title "One of each color"
 	Any { BlueCard BlueSkull KeyBlue}
 	Any { YellowCard YellowSkull KeyYellow}
 	Any { RedCard RedSkull KeyGreen}
@@ -135,6 +150,7 @@ Lock 229 Doom
 
 Lock 101 Doom
 {
+	//$Title "All keys"
 	BlueCard 
 	BlueSkull
 	YellowCard 
@@ -151,6 +167,7 @@ Lock 101 Doom
 
 Lock 1 Heretic
 {
+	//$Title "Green key"
 	KeyGreen
 	Message "$TXT_NEEDGREENKEY"
 	Mapcolor 0 255 0
@@ -159,6 +176,7 @@ Lock 1 Heretic
 
 Lock 2 Heretic
 {
+	//$Title "Blue key"
 	KeyBlue 
 	Message "$TXT_NEEDBLUEKEY"
 	Mapcolor 0 0 255
@@ -167,6 +185,7 @@ Lock 2 Heretic
 
 Lock 3 Heretic
 {
+	//$Title "Yellow key"
 	KeyYellow
 	Message "$TXT_NEEDYELLOWKEY"
 	Mapcolor 255 255 0
@@ -175,6 +194,7 @@ Lock 3 Heretic
 
 Lock 129 Heretic
 {
+	//$Title "Green key"
 	KeyGreen
 	Message "$TXT_NEEDGREENKEY"
 	Mapcolor 0 255 0
@@ -183,6 +203,7 @@ Lock 129 Heretic
 
 Lock 130 Heretic
 {
+	//$Title "Blue key"
 	KeyBlue 
 	Message "$TXT_NEEDBLUEKEY"
 	Mapcolor 0 0 255
@@ -191,6 +212,7 @@ Lock 130 Heretic
 
 Lock 131 Heretic
 {
+	//$Title "Yellow key"
 	KeyYellow
 	Message "$TXT_NEEDYELLOWKEY"
 	Mapcolor 255 255 0
@@ -199,6 +221,7 @@ Lock 131 Heretic
 
 Lock 229 Heretic
 {
+	//$Title "All keys"
 	KeyGreen 
 	KeyYellow 
 	KeyBlue
@@ -208,6 +231,7 @@ Lock 229 Heretic
 
 Lock 101 Heretic
 {
+	//$Title "All keys"
 	KeyGreen 
 	KeyYellow 
 	KeyBlue
@@ -222,6 +246,7 @@ Lock 101 Heretic
 
 Lock 1 Hexen
 {
+	//$Title "Steel key"
 	KeySteel
 	Message "$TXT_NEED_KEY_STEEL"
 	Mapcolor 150 150 150
@@ -230,6 +255,7 @@ Lock 1 Hexen
 
 Lock 2 Hexen
 {
+	//$Title "Cave key"
 	KeyCave
 	Message "$TXT_NEED_KEY_CAVE"
 	Mapcolor 255 218 0
@@ -238,6 +264,7 @@ Lock 2 Hexen
 
 Lock 3 Hexen
 {
+	//$Title "Axe key"
 	KeyAxe
 	Message "$TXT_NEED_KEY_AXE"
 	Mapcolor 64 64 255
@@ -246,6 +273,7 @@ Lock 3 Hexen
 
 Lock 4 Hexen
 {
+	//$Title "Fire key"
 	KeyFire
 	Message "$TXT_NEED_KEY_FIRE"
 	Mapcolor 255 128 0
@@ -254,6 +282,7 @@ Lock 4 Hexen
 
 Lock 5 Hexen
 {
+	//$Title "Emerald key"
 	KeyEmerald
 	Message "$TXT_NEED_KEY_EMERALD"
 	Mapcolor 0 255 0
@@ -262,6 +291,7 @@ Lock 5 Hexen
 
 Lock 6 Hexen
 {
+	//$Title "Dungeon key"
 	KeyDungeon
 	Message "$TXT_NEED_KEY_DUNGEON"
 	Mapcolor 47 151 255
@@ -270,6 +300,7 @@ Lock 6 Hexen
 
 Lock 7 Hexen
 {
+	//$Title "Silver key"
 	KeySilver
 	Message "$TXT_NEED_KEY_SILVER"
 	Mapcolor 154 152 188
@@ -278,6 +309,7 @@ Lock 7 Hexen
 
 Lock 8 Hexen
 {
+	//$Title "Rusted key"
 	KeyRusted
 	Message "$TXT_NEED_KEY_RUSTED"
 	Mapcolor 156 76 0
@@ -286,6 +318,7 @@ Lock 8 Hexen
 
 Lock 9 Hexen
 {
+	//$Title "Horn key"
 	KeyHorn
 	Message "$TXT_NEED_KEY_HORN"
 	Mapcolor 255 218 0
@@ -294,6 +327,7 @@ Lock 9 Hexen
 
 Lock 10 Hexen
 {
+	//$Title "Swamp key"
 	KeySwamp
 	Message "$TXT_NEED_KEY_SWAMP"
 	Mapcolor 64 255 64
@@ -302,6 +336,7 @@ Lock 10 Hexen
 
 Lock 11 Hexen
 {
+	//$Title "Castle key"
 	KeyCastle
 	Message "$TXT_NEED_KEY_CASTLE"
 	Mapcolor 255 64 64
@@ -310,6 +345,7 @@ Lock 11 Hexen
 
 Lock 101 Hexen
 {
+	//$Title "All keys"
 	KeySteel
 	KeyCave
 	KeyAxe
@@ -326,6 +362,7 @@ Lock 101 Hexen
 
 Lock 229 Hexen
 {
+	//$Title "All keys"
 	KeySteel
 	KeyCave
 	KeyAxe
@@ -345,14 +382,16 @@ Lock 229 Hexen
 
 Lock 1 Strife
 {
+	//$Title "Base key"
 	BaseKey
-	Message "You don't have the key"
+	Message "$TXT_NEEDKEY"
 	Mapcolor 150 150 150
 }
 
 
 Lock 2 Strife
 {
+	//$Title "Governor's key"
 	GovsKey
 	Message "$TXT_NEEDKEY"
 	Mapcolor 150 150 150
@@ -361,6 +400,7 @@ Lock 2 Strife
 
 Lock 3 Strife
 {
+	//$Title "Travel passcard"
 	Passcard
 	RemoteMessage "$TXT_NEED_PASSCARD"
 	Message "$TXT_NEED_PASSCARD_DOOR"
@@ -370,6 +410,7 @@ Lock 3 Strife
 
 Lock 4 Strife
 {
+	//$Title "ID badge"
 	IDBadge
 	Message "$TXT_NEED_IDCARD"
 	Mapcolor 255 128 0
@@ -378,6 +419,7 @@ Lock 4 Strife
 
 Lock 5 Strife
 {
+	//$Title "Prison key"
 	PrisonKey
 	Message "$TXT_NEED_PRISONKEY"
 	Mapcolor 0 255 0
@@ -386,6 +428,7 @@ Lock 5 Strife
 
 Lock 6 Strife
 {
+	//$Title "Severed hand"
 	SeveredHand
 	Message "$TXT_NEED_HANDPRINT"
 	Mapcolor 255 151 100
@@ -394,6 +437,7 @@ Lock 6 Strife
 
 Lock 7 Strife
 {
+	//$Title "Power key 1"
 	Power1Key
 	Message "$TXT_NEEDKEY"
 	Mapcolor 150 150 150
@@ -402,6 +446,7 @@ Lock 7 Strife
 
 Lock 8 Strife
 {
+	//$Title "Power key 2"
 	Power2Key
 	Message "$TXT_NEEDKEY"
 	Mapcolor 150 150 150
@@ -410,6 +455,7 @@ Lock 8 Strife
 
 Lock 9 Strife
 {
+	//$Title "Power key 3"
 	Power3Key
 	Message "$TXT_NEEDKEY"
 	Mapcolor 150 150 150
@@ -418,6 +464,7 @@ Lock 9 Strife
 
 Lock 10 Strife
 {
+	//$Title "Gold key"
 	GoldKey
 	Message "$TXT_NEED_GOLDKEY"
 	Mapcolor 255 200 0
@@ -426,6 +473,7 @@ Lock 10 Strife
 
 Lock 11 Strife
 {
+	//$Title "ID card"
 	IDCard
 	RemoteMessage "$TXT_NEED_IDBADGE"
 	Message "$TXT_NEED_IDBADGE_DOOR"
@@ -434,6 +482,7 @@ Lock 11 Strife
 
 Lock 12 Strife
 {
+	//$Title "Silver key"
 	SilverKey
 	Message "$TXT_NEED_SILVERKEY"
 	Mapcolor 150 150 150
@@ -441,6 +490,7 @@ Lock 12 Strife
 
 Lock 13 Strife
 {
+	//$Title "Oracle key"
 	OracleKey
 	Message "$TXT_NEEDKEY"
 	Mapcolor 150 150 150
@@ -448,6 +498,7 @@ Lock 13 Strife
 
 Lock 14 Strife
 {
+	//$Title "Military key"
 	MilitaryID
 	Message "$TXT_NEEDKEY"
 	Mapcolor 150 150 150
@@ -455,6 +506,7 @@ Lock 14 Strife
 
 Lock 15 Strife
 {
+	//$Title "Order key"
 	OrderKey
 	Message "$TXT_NEEDKEY"
 	Mapcolor 150 150 150
@@ -462,6 +514,7 @@ Lock 15 Strife
 
 Lock 16 Strife
 {
+	//$Title "Warehouse key"
 	WarehouseKey
 	Message "$TXT_NEEDKEY"
 	Mapcolor 150 150 150
@@ -469,6 +522,7 @@ Lock 16 Strife
 
 Lock 17 Strife
 {
+	//$Title "Brass key"
 	BrassKey
 	Message "$TXT_NEED_BRASSKEY"
 	Mapcolor 150 75 0
@@ -476,6 +530,7 @@ Lock 17 Strife
 
 Lock 18 Strife
 {
+	//$Title "Red crystal key"
 	RedCrystalKey
 	Message "$TXT_NEED_REDCRYSTAL"
 	Mapcolor 150 150 150
@@ -483,6 +538,7 @@ Lock 18 Strife
 
 Lock 19 Strife
 {
+	//$Title "Blue crystal key"
 	BlueCrystalKey
 	Message "$TXT_NEED_BLUECRYSTAL"
 	Mapcolor 150 150 150
@@ -490,6 +546,7 @@ Lock 19 Strife
 
 Lock 20 Strife
 {
+	//$Title "Chapel key"
 	ChapelKey
 	Message "$TXT_NEEDKEY"
 	Mapcolor 150 150 150
@@ -497,6 +554,7 @@ Lock 20 Strife
 
 Lock 21 Strife
 {
+	//$Title "Catacomb key"
 	CatacombKey
 	Message "$TXT_NEEDKEY"
 	Mapcolor 150 150 150
@@ -504,6 +562,7 @@ Lock 21 Strife
 
 Lock 22 Strife
 {
+	//$Title "Security key"
 	SecurityKey
 	Message "$TXT_NEEDKEY"
 	Mapcolor 150 150 150
@@ -511,6 +570,7 @@ Lock 22 Strife
 
 Lock 23 Strife
 {
+	//$Title "Core key"
 	CoreKey
 	Message "$TXT_NEEDKEY"
 	Mapcolor 150 150 150
@@ -518,6 +578,7 @@ Lock 23 Strife
 
 Lock 24 Strife
 {
+	//$Title "Mauler key"
 	MaulerKey
 	Message "$TXT_NEEDKEY"
 	Mapcolor 150 150 150
@@ -525,6 +586,7 @@ Lock 24 Strife
 
 Lock 25 Strife
 {
+	//$Title "Factory key"
 	FactoryKey
 	Message "$TXT_NEEDKEY"
  	Mapcolor 150 150 150
@@ -532,6 +594,7 @@ Lock 25 Strife
 
 Lock 26 Strife
 {
+	//$Title "Mine key"
 	MineKey
 	Message "$TXT_NEEDKEY"
 	Mapcolor 150 150 150
@@ -539,6 +602,7 @@ Lock 26 Strife
 
 Lock 27 Strife
 {
+	//$Title "New key 5"
 	NewKey5
 	Message "$TXT_NEEDKEY"
 	Mapcolor 150 150 150
@@ -546,6 +610,7 @@ Lock 27 Strife
 
 Lock 50 Strife
 {
+	//$Title "Prison key"
 	PrisonPass
 	Message "$TXT_NEEDKEY"
 	Mapcolor 150 150 150
@@ -553,6 +618,7 @@ Lock 50 Strife
 
 Lock 51 Strife
 {
+	//$Title "Oracle pass"
 	OraclePass
 	Message "$TXT_NEEDKEY"
 	Mapcolor 150 150 150
@@ -564,6 +630,7 @@ Lock 51 Strife
 
 Lock 1 Chex
 {
+	//$Title "Red key card"
 	ChexRedCard
 	Message "$PD_REDC"
 	RemoteMessage "$PD_REDCO"
@@ -573,6 +640,7 @@ Lock 1 Chex
 
 Lock 2 Chex
 {
+	//$Title "Blue key card"
 	ChexBlueCard
 	Message "$PD_BLUEC"
 	RemoteMessage "$PD_BLUECO"
@@ -582,6 +650,7 @@ Lock 2 Chex
 
 Lock 3 Chex
 {
+	//$Title "Yellow key card"
 	ChexYellowCard
 	Message "$PD_YELLOWC"
 	RemoteMessage "$PD_YELLOWCO"
@@ -590,6 +659,7 @@ Lock 3 Chex
 
 Lock 129 Chex
 {
+	//$Title "Red key"
 	ChexRedCard
 	Message "$PD_REDK"
 	RemoteMessage "$PD_REDO"
@@ -599,6 +669,7 @@ Lock 129 Chex
 
 Lock 130 Chex
 {
+	//$Title "Blue key"
 	ChexBlueCard
 	Message "$PD_BLUEK"
 	RemoteMessage "$PD_BLUEO"
@@ -608,6 +679,7 @@ Lock 130 Chex
 
 Lock 131 Chex
 {
+	//$Title "Yellow key"
 	ChexYellowCard
 	Message "$PD_YELLOWK"
 	RemoteMessage "$PD_YELLOWO"
diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt
index 679db909b..c1f6fd805 100644
--- a/wadsrc/static/menudef.txt
+++ b/wadsrc/static/menudef.txt
@@ -692,6 +692,8 @@ OptionMenu "VideoOptions"
 	Option "$DSPLYMNU_NOMONSTERINTERPOLATION",	"nomonsterinterpolation", "NoYes"
 	Slider "$DSPLYMNU_MENUDIM",					"dimamount", 0, 1.0, 0.05, 2
 	ColorPicker "$DSPLYMNU_DIMCOLOR",			"dimcolor"
+	Slider "$DSPLYMNU_MOVEBOB",			"movebob", 0, 1.0, 0.05, 2
+	Slider "$DSPLYMNU_STILLBOB",		"stillbob", 0, 1.0, 0.05, 2
 }
 
 //-------------------------------------------------------------------------------------------
@@ -823,6 +825,12 @@ OptionValue "AltHUDLag"
 	2, "$OPTVAL_ALWAYS"
 }
 
+OptionValue "AltHUDAmmoOrder"
+{
+	0, "$OPTVAL_AMMOIMAGETEXT"
+	1, "$OPTVAL_AMMOTEXTIMAGE"
+}
+
 OptionMenu "AltHUDOptions"
 {
 	Title "$ALTHUDMNU_TITLE"
@@ -839,6 +847,7 @@ OptionMenu "AltHUDOptions"
 	Option "$ALTHUDMNU_SHOWTIME",				"hud_showtime", "AltHUDTime"
 	Option "$ALTHUDMNU_TIMECOLOR",				"hud_timecolor", "TextColors"
 	Option "$ALTHUDMNU_SHOWLAG",				"hud_showlag", "AltHUDLag"
+	Option "$ALTHUDMNU_AMMOORDER",				"hud_ammo_order", "AltHUDAmmoOrder"
 	Slider "$ALTHUDMNU_AMMORED",				"hud_ammo_red", 0, 100, 1, 0
 	Slider "$ALTHUDMNU_AMMOYELLOW",				"hud_ammo_yellow", 0, 100, 1, 0
 	Slider "$ALTHUDMNU_HEALTHRED",				"hud_health_red", 0, 100, 1, 0
diff --git a/wadsrc/static/xlat/eternity.txt b/wadsrc/static/xlat/eternity.txt
index bc5159d11..4db96f2da 100644
--- a/wadsrc/static/xlat/eternity.txt
+++ b/wadsrc/static/xlat/eternity.txt
@@ -234,3 +234,20 @@ enum
 447 = 0, Exit_Normal(0)
 448 = 0, Exit_Secret(0)
 449 = 0, Teleport_NewMap(0)
+450 = 0, Line_Horizon(0)
+451 = 0, Floor_RaiseAndCrush(0)
+452 = 0, Floor_CrushStop(0)
+453 = 0, FloorAndCeiling_LowerByValue(0)
+454 = 0, FloorAndCeiling_RaiseByValue(0)
+457 = 0, Door_LockedOpen(0)
+458 = 0, Elevator_RaiseToNearest(0)
+459 = 0, Elevator_LowerToNearest(0)
+460 = 0, Elevator_MoveToFloor(0)
+461 = 0, Light_MaxNeighbor(0)
+462 = 0, ChangeSkill(0)
+463 = 0, Light_StrobeDoom(0)
+464 = 0, Generic_Floor(0)
+465 = 0, Generic_Ceiling(0)
+466 = 0, Floor_TransferTrigger(0)
+467 = 0, Floor_TransferNumeric(0)
+468 = 0, FloorAndCeiling_LowerRaise(0)