From ab6b5e337ec419a68c3da6f81d2346e9ab1d813d Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <coelckers@zdoom.fake>
Date: Thu, 22 May 2008 19:35:38 +0000
Subject: [PATCH] - Reworked a few options that previously depended on
 LEVEL_HEXENFORMAT   (actors being forced to the ground by instantly moving
 sectors, strife   railing handling and shooting lines with a non-zero but
 unassigned tag.)   With UDMF such semantics have to be handled diffently. -
 finalized UDMF 1.0 implementation. - Added Martin Howe's latest morph update.

SVN r987 (trunk)
---
 docs/rh-log.txt                      |   8 +
 src/g_game.cpp                       |   2 +-
 src/g_heretic/a_hereticartifacts.cpp |   2 +-
 src/g_level.h                        |   2 +
 src/g_raven/a_artitele.cpp           |   2 +-
 src/g_shared/a_artifacts.cpp         |  60 ++-
 src/g_shared/a_artifacts.h           |   2 +-
 src/g_shared/a_morph.cpp             | 127 +++++-
 src/g_shared/a_morph.h               |  19 +-
 src/m_cheat.cpp                      |   4 +-
 src/namedef.h                        |   4 +-
 src/p_acs.cpp                        | 125 +++++-
 src/p_acs.h                          |   2 +
 src/p_conversation.cpp               |   2 +-
 src/p_interaction.cpp                |  35 +-
 src/p_local.h                        |   3 -
 src/p_map.cpp                        |   7 +-
 src/p_setup.cpp                      |  31 +-
 src/p_spec.cpp                       |   5 +-
 src/p_udmf.cpp                       | 570 ++++++++++++++++++++-------
 src/p_user.cpp                       |   2 +-
 src/po_man.cpp                       |  13 +-
 src/r_defs.h                         |   1 +
 src/sc_man.cpp                       |  26 ++
 src/sc_man.h                         |   1 +
 src/textures/texturemanager.cpp      |   2 +-
 src/thingdef/thingdef_properties.cpp |   8 +-
 src/xlat/parse_xlat.cpp              |   4 +-
 28 files changed, 843 insertions(+), 226 deletions(-)

diff --git a/docs/rh-log.txt b/docs/rh-log.txt
index 8e10a4fdd..ccccd9e1d 100644
--- a/docs/rh-log.txt
+++ b/docs/rh-log.txt
@@ -1,3 +1,11 @@
+May 22, 2008 (Changes by Graf Zahl)
+- Reworked a few options that previously depended on LEVEL_HEXENFORMAT
+  (actors being forced to the ground by instantly moving sectors, strife
+  railing handling and shooting lines with a non-zero but unassigned tag.)
+  With UDMF such semantics have to be handled diffently. 
+- finalized UDMF 1.0 implementation.
+- Added Martin Howe's latest morph update.
+
 May 21, 2008
 - Fixed: When R_DrawTiltedPlane() calculates the p vector, it can overflow
   if the view is near the bounds of the fixed point coordinate system. This
diff --git a/src/g_game.cpp b/src/g_game.cpp
index 5db3eb0c3..a5d8179f4 100644
--- a/src/g_game.cpp
+++ b/src/g_game.cpp
@@ -1123,7 +1123,7 @@ void G_PlayerFinishLevel (int player, EFinishLevelType mode, bool resetinventory
 
 	if (p->morphTics)
 	{ // Undo morph
-		P_UndoPlayerMorph (p, true);
+		P_UndoPlayerMorph (p, p, true);
 	}
 
 	// Clears the entire inventory and gives back the defaults for starting a game
diff --git a/src/g_heretic/a_hereticartifacts.cpp b/src/g_heretic/a_hereticartifacts.cpp
index ee2422fee..9afdcf8bb 100644
--- a/src/g_heretic/a_hereticartifacts.cpp
+++ b/src/g_heretic/a_hereticartifacts.cpp
@@ -34,7 +34,7 @@ bool AArtiTomeOfPower::Use (bool pickup)
 {
 	if (Owner->player->morphTics && (Owner->player->MorphStyle & MORPH_UNDOBYTOMEOFPOWER))
 	{ // Attempt to undo chicken
-		if (!P_UndoPlayerMorph (Owner->player))
+		if (!P_UndoPlayerMorph (Owner->player, Owner->player))
 		{ // Failed
 			if (!(Owner->player->MorphStyle & MORPH_FAILNOTELEFRAG))
 			{
diff --git a/src/g_level.h b/src/g_level.h
index 3340659aa..cce5ab220 100644
--- a/src/g_level.h
+++ b/src/g_level.h
@@ -118,6 +118,8 @@
 #define LEVEL_FORCETEAMPLAYOFF		UCONST64(0x8000000000000)
 
 #define LEVEL_CONV_SINGLE_UNFREEZE	UCONST64(0x10000000000000)
+#define LEVEL_RAILINGHACK			UCONST64(0x20000000000000)	// but UDMF requires them to be separate to have more control
+#define LEVEL_DUMMYSWITCHES			UCONST64(0x40000000000000)
 
 
 struct acsdefered_s;
diff --git a/src/g_raven/a_artitele.cpp b/src/g_raven/a_artitele.cpp
index 679f08e06..d75b1d58c 100644
--- a/src/g_raven/a_artitele.cpp
+++ b/src/g_raven/a_artitele.cpp
@@ -62,7 +62,7 @@ bool AArtiTeleport::Use (bool pickup)
 	bool canlaugh = true;
  	if (Owner->player->morphTics && (Owner->player->MorphStyle & MORPH_UNDOBYCHAOSDEVICE))
  	{ // Teleporting away will undo any morph effects (pig)
-		if (!P_UndoPlayerMorph (Owner->player) && (Owner->player->MorphStyle & MORPH_FAILNOLAUGH))
+		if (!P_UndoPlayerMorph (Owner->player, Owner->player) && (Owner->player->MorphStyle & MORPH_FAILNOLAUGH))
 		{
 			canlaugh = false;
 		}
diff --git a/src/g_shared/a_artifacts.cpp b/src/g_shared/a_artifacts.cpp
index 4643bad3b..0b41d39d8 100644
--- a/src/g_shared/a_artifacts.cpp
+++ b/src/g_shared/a_artifacts.cpp
@@ -1740,7 +1740,7 @@ void APowerMorph::Serialize (FArchive &arc)
 {
 	Super::Serialize (arc);
 	arc << PlayerClass << MorphStyle << MorphFlash << UnMorphFlash;
-	arc << player;
+	arc << Player;
 }
 
 //===========================================================================
@@ -1761,7 +1761,7 @@ void APowerMorph::InitEffect( )
 		{
 			Owner = realplayer->mo;				// Replace the new owner in our owner; safe because we are not attached to anything yet
 			ItemFlags |= IF_CREATECOPYMOVED;	// Let the caller know the "real" owner has changed (to the morphed actor)
-			player = realplayer;				// Store the player identity (morphing clears the unmorphed actor's "player" field)
+			Player = realplayer;				// Store the player identity (morphing clears the unmorphed actor's "player" field)
 		}
 		else // morph failed - give the caller an opportunity to fail the pickup completely
 		{
@@ -1778,23 +1778,45 @@ void APowerMorph::InitEffect( )
 
 void APowerMorph::EndEffect( )
 {
-	if (Owner != NULL && player != NULL)
+	// Abort if owner already destroyed
+	if (Owner == NULL)
 	{
-		int savedMorphTics = player->morphTics;
-		P_UndoPlayerMorph (player);
-		if (player->morphTics /*failed*/)
-		{
-			// Transfer retry timeout
-			// to the powerup's timer.
-			EffectTics = player->morphTics;
-			// Reload negative morph tics;
-			// use actual value; it may
-			// be in use for animation.
-			player->morphTics = savedMorphTics;
-		}
-		else // unmorph succeeded
-		{
-			player = NULL;
-		}
+		assert(Player == NULL);
+		return;
 	}
+	
+	// Abort if owner already unmorphed
+	if (Player == NULL)
+	{
+		return;
+	}
+
+	// Abort if owner is dead; their Die() method will
+	// take care of any required unmorphing on death.
+	if (Player->health <= 0)
+	{
+		return;
+	}
+
+	// Unmorph if possible
+	int savedMorphTics = Player->morphTics;
+	P_UndoPlayerMorph (Player, Player);
+
+	// Abort if unmorph failed; in that case,
+	// set the usual retry timer and return.
+	if (Player->morphTics)
+	{
+		// Transfer retry timeout
+		// to the powerup's timer.
+		EffectTics = Player->morphTics;
+		// Reload negative morph tics;
+		// use actual value; it may
+		// be in use for animation.
+		Player->morphTics = savedMorphTics;
+		// Try again some time later
+		return;
+	}
+
+	// Unmorph suceeded
+	Player = NULL;
 }
diff --git a/src/g_shared/a_artifacts.h b/src/g_shared/a_artifacts.h
index 21df07952..e4ade50f5 100644
--- a/src/g_shared/a_artifacts.h
+++ b/src/g_shared/a_artifacts.h
@@ -267,7 +267,7 @@ protected:
 	void InitEffect ();
 	void EndEffect ();
 	// Variables
-	player_s *player;
+	player_s *Player;
 };
 
 #endif //__A_ARTIFACTS_H__
diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp
index 1e54e0cfd..80e31d6f3 100644
--- a/src/g_shared/a_morph.cpp
+++ b/src/g_shared/a_morph.cpp
@@ -63,6 +63,13 @@ bool P_MorphPlayer (player_t *activator, player_t *p, const PClass *spawntype, i
 
 	morphed = static_cast<APlayerPawn *>(Spawn (spawntype, actor->x, actor->y, actor->z, NO_REPLACE));
 	DObject::StaticPointerSubstitution (actor, morphed);
+	if ((actor->tid != 0) && (style & MORPH_NEWTIDBEHAVIOUR))
+	{
+		morphed->tid = actor->tid;
+		morphed->AddToHash ();
+		actor->RemoveFromHash ();
+		actor->tid = 0;
+	}
 	morphed->angle = actor->angle;
 	morphed->target = actor->target;
 	morphed->tracer = actor;
@@ -159,7 +166,7 @@ bool P_MorphPlayer (player_t *activator, player_t *p, const PClass *spawntype, i
 //
 //----------------------------------------------------------------------------
 
-bool P_UndoPlayerMorph (player_t *player, bool force)
+bool P_UndoPlayerMorph (player_s *activator, player_t *player, bool force)
 {
 	AWeapon *beastweap;
 	APlayerPawn *mo;
@@ -177,11 +184,19 @@ bool P_UndoPlayerMorph (player_t *player, bool force)
 	{
 		return false;
 	}
+
+	if ((pmo->flags2 & MF2_INVULNERABLE) && ((player != activator) || (!(player->MorphStyle & MORPH_WHENINVULNERABLE))))
+	{ // Immune when invulnerable unless this is something we initiated.
+		// If the WORLD is the initiator, the same player should be given
+		// as the activator; WORLD initiated actions should always succeed.
+		return false;
+	}
+
 	mo = barrier_cast<APlayerPawn *>(pmo->tracer);
 	mo->SetOrigin (pmo->x, pmo->y, pmo->z);
 	mo->flags |= MF_SOLID;
 	pmo->flags &= ~MF_SOLID;
-	if (!force && P_TestMobjLocation (mo) == false)
+	if (!force && !P_TestMobjLocation (mo))
 	{ // Didn't fit
 		mo->flags &= ~MF_SOLID;
 		pmo->flags |= MF_SOLID;
@@ -192,6 +207,11 @@ bool P_UndoPlayerMorph (player_t *player, bool force)
 
 	mo->ObtainInventory (pmo);
 	DObject::StaticPointerSubstitution (pmo, mo);
+	if ((pmo->tid != 0) && (player->MorphStyle & MORPH_NEWTIDBEHAVIOUR))
+	{
+		mo->tid = pmo->tid;
+		mo->AddToHash ();
+	}
 	mo->angle = pmo->angle;
 	mo->player = player;
 	mo->reactiontime = 18;
@@ -211,6 +231,7 @@ bool P_UndoPlayerMorph (player_t *player, bool force)
 
 	const PClass *exit_flash = player->MorphExitFlash;
 	bool correctweapon = !!(player->MorphStyle & MORPH_LOSEACTUALWEAPON);
+	bool undobydeathsaves = !!(player->MorphStyle & MORPH_UNDOBYDEATHSAVES);
 
 	player->morphTics = 0;
 	player->MorphedPlayerClass = 0;
@@ -222,7 +243,16 @@ bool P_UndoPlayerMorph (player_t *player, bool force)
 	{
 		level2->Destroy ();
 	}
-	player->health = mo->health = mo->GetDefault()->health;
+
+	if ((player->health > 0) || undobydeathsaves)
+	{
+		player->health = mo->health = mo->GetDefault()->health;
+	}
+	else // killed when morphed so stay dead
+	{
+		mo->health = player->health;
+	}
+
 	player->mo = mo;
 	if (player->camera == pmo)
 	{
@@ -363,18 +393,17 @@ bool P_MorphMonster (AActor *actor, const PClass *spawntype, int duration, int s
 
 //----------------------------------------------------------------------------
 //
-// FUNC P_UpdateMorphedMonster
+// FUNC P_UndoMonsterMorph
 //
 // Returns true if the monster unmorphs.
 //
 //----------------------------------------------------------------------------
 
-bool P_UpdateMorphedMonster (AMorphedMonster *beast)
+bool P_UndoMonsterMorph (AMorphedMonster *beast, bool force)
 {
 	AActor *actor;
 
-	if (beast->UnmorphTime == 0 ||
-		beast->UnmorphTime > level.time ||
+	if (beast->UnmorphTime == 0 || 
 		beast->UnmorphedMe == NULL ||
 		beast->flags3 & MF3_STAYMORPHED)
 	{
@@ -384,7 +413,7 @@ bool P_UpdateMorphedMonster (AMorphedMonster *beast)
 	actor->SetOrigin (beast->x, beast->y, beast->z);
 	actor->flags |= MF_SOLID;
 	beast->flags &= ~MF_SOLID;
-	if (P_TestMobjLocation (actor) == false)
+	if (!force && !P_TestMobjLocation (actor))
 	{ // Didn't fit
 		actor->flags &= ~MF_SOLID;
 		beast->flags |= MF_SOLID;
@@ -417,6 +446,82 @@ bool P_UpdateMorphedMonster (AMorphedMonster *beast)
 	return true;
 }
 
+//----------------------------------------------------------------------------
+//
+// FUNC P_UpdateMorphedMonster
+//
+// Returns true if the monster unmorphs.
+//
+//----------------------------------------------------------------------------
+
+bool P_UpdateMorphedMonster (AMorphedMonster *beast)
+{
+	if (beast->UnmorphTime > level.time)
+	{
+		return false;
+	}
+	return P_UndoMonsterMorph (beast);
+}
+
+//----------------------------------------------------------------------------
+//
+// FUNC P_MorphedDeath
+//
+// Unmorphs the actor if possible.
+// Returns the unmorphed actor, the style with which they were morphed and the
+// health (of the AActor, not the player_s) they last had before unmorphing.
+//
+//----------------------------------------------------------------------------
+
+bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *morphedhealth)
+{
+	// May be a morphed player
+	if ((actor->player) &&
+		(actor->player->morphTics) &&
+		(actor->player->MorphStyle & MORPH_UNDOBYDEATH) &&
+		(actor->player->mo) &&
+		(actor->player->mo->tracer))
+	{
+		AActor *realme = actor->player->mo->tracer;
+		int realstyle = actor->player->MorphStyle;
+		int realhealth = actor->health;
+		if (P_UndoPlayerMorph(actor->player, actor->player, !!(actor->player->MorphStyle & MORPH_UNDOBYDEATHFORCED)))
+		{
+			*morphed = realme;
+			*morphedstyle = realstyle;
+			*morphedhealth = realhealth;
+			return true;
+		}
+		return false;
+	}
+
+	// May be a morphed monster
+	if (actor->GetClass()->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster)))
+	{
+		AMorphedMonster *fakeme = static_cast<AMorphedMonster *>(actor);
+		if ((fakeme->UnmorphTime) &&
+			(fakeme->MorphStyle & MORPH_UNDOBYDEATH) &&
+			(fakeme->UnmorphedMe))
+		{
+			AActor *realme = fakeme->UnmorphedMe;
+			int realstyle = fakeme->MorphStyle;
+			int realhealth = fakeme->health;
+			if (P_UndoMonsterMorph(fakeme, !!(fakeme->MorphStyle & MORPH_UNDOBYDEATHFORCED)))
+			{
+				*morphed = realme;
+				*morphedstyle = realstyle;
+				*morphedhealth = realhealth;
+				return true;
+			}
+		}
+		fakeme->flags3 |= MF3_STAYMORPHED; // moved here from AMorphedMonster::Die()
+		return false;
+	}
+
+	// Not a morphed player or monster
+	return false;
+}
+
 // Base class for morphing projectiles --------------------------------------
 
 IMPLEMENT_STATELESS_ACTOR(AMorphProjectile, Any, -1, 0)
@@ -479,7 +584,11 @@ void AMorphedMonster::Destroy ()
 void AMorphedMonster::Die (AActor *source, AActor *inflictor)
 {
 	// Dead things don't unmorph
-	flags3 |= MF3_STAYMORPHED;
+//	flags3 |= MF3_STAYMORPHED;
+	// [MH]
+	// But they can now, so that line above has been
+	// moved into P_MorphedDeath() and is now set by
+	// that function if and only if it is needed.
 	Super::Die (source, inflictor);
 	if (UnmorphedMe != NULL && (UnmorphedMe->flags & MF_UNMORPHED))
 	{
diff --git a/src/g_shared/a_morph.h b/src/g_shared/a_morph.h
index 18cb19848..c8afbed5d 100644
--- a/src/g_shared/a_morph.h
+++ b/src/g_shared/a_morph.h
@@ -2,7 +2,7 @@
 #define __A_MORPH__
 
 #define MORPHTICS (40*TICRATE)
-#define MAXMORPHHEALTH	30
+#define MAXMORPHHEALTH 30
 
 // Morph style states how morphing affects health and
 // other effects in the game; only valid for players.
@@ -11,25 +11,32 @@
 enum
 {
 	MORPH_OLDEFFECTS		= 0x00000000,	// Default to old Heretic/HeXen behaviour unless flags given
-	MORPH_ADDSTAMINA		= 0x00000001,	// Power instead of curse (add stamina instead of limiting to health)
-	MORPH_FULLHEALTH		= 0x00000002,	// New health semantics (!POWER => MaxHealth of animal, POWER => Normal health behaviour)
+	MORPH_ADDSTAMINA		= 0x00000001,	// Player has a "power" instead of a "curse" (add stamina instead of limiting to health)
+	MORPH_FULLHEALTH		= 0x00000002,	// Player uses new health semantics (!POWER => MaxHealth of animal, POWER => Normal health behaviour)
 	MORPH_UNDOBYTOMEOFPOWER	= 0x00000004,	// Player unmorphs upon activating a Tome of Power
 	MORPH_UNDOBYCHAOSDEVICE	= 0x00000008,	// Player unmorphs upon activating a Chaos Device
 	MORPH_FAILNOTELEFRAG	= 0x00000010,	// Player stays morphed if unmorph by Tome of Power fails
 	MORPH_FAILNOLAUGH		= 0x00000020,	// Player doesn't laugh if unmorph by Chaos Device fails
-	MORPH_WHENINVULNERABLE	= 0x00000040,	// Player can morph when invulnerable but ONLY if doing it to themselves
-	MORPH_LOSEACTUALWEAPON	= 0X00000080	// Player loses specified morph weapon only (not "whichever they have when unmorphing")
+	MORPH_WHENINVULNERABLE	= 0x00000040,	// Player can morph (or scripted unmorph) when invulnerable but ONLY if doing it to themselves
+	MORPH_LOSEACTUALWEAPON	= 0x00000080,	// Player loses specified morph weapon only (not "whichever they have when unmorphing")
+	MORPH_NEWTIDBEHAVIOUR	= 0x00000100,	// Actor TID is by default transferred from the old actor to the new actor
+	MORPH_UNDOBYDEATH		= 0x00000200,	// Actor unmorphs when killed and (unless MORPH_UNDOBYDEATHSAVES) stays dead
+	MORPH_UNDOBYDEATHFORCED	= 0x00000400,	// Actor (if unmorphed when killed) forces unmorph (not very useful with UNDOBYDEATHSAVES)
+	MORPH_UNDOBYDEATHSAVES	= 0x00000800,	// Actor (if unmorphed when killed) regains their health and doesn't die
 };
 
 struct PClass;
 class AActor;
 class player_s;
+class AMorphedMonster;
 
 bool P_MorphPlayer (player_s *activator, player_s *player, const PClass *morphclass, int duration = 0, int style = 0,
 					const PClass *enter_flash = NULL, const PClass *exit_flash = NULL);
-bool P_UndoPlayerMorph (player_s *player, bool force = false);
+bool P_UndoPlayerMorph (player_s *activator, player_s *player, bool force = false);
 bool P_MorphMonster (AActor *actor, const PClass *morphclass, int duration = 0, int style = 0,
 					 const PClass *enter_flash = NULL, const PClass *exit_flash = NULL);
+bool P_UndoMonsterMorph (AMorphedMonster *beast, bool force = false);
 bool P_UpdateMorphedMonster (AActor *actor);
+bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *morphedhealth);
 
 #endif //__A_MORPH__
diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp
index e3e01b9d9..a7e4f37aa 100644
--- a/src/m_cheat.cpp
+++ b/src/m_cheat.cpp
@@ -291,7 +291,7 @@ void cht_DoCheat (player_t *player, int cheat)
 
 				if (player->morphTics > 0)
 				{
-					P_UndoPlayerMorph(player);
+					P_UndoPlayerMorph(player, player);
 				}
 
 			}
@@ -438,7 +438,7 @@ const char *cht_Morph (player_t *player, const PClass *morphclass, bool quickund
 
 	if (player->morphTics)
 	{
-		if (P_UndoPlayerMorph (player))
+		if (P_UndoPlayerMorph (player, player))
 		{
 			if (!quickundo && oldclass != morphclass && P_MorphPlayer (player, player, morphclass, 0, style))
 			{
diff --git a/src/namedef.h b/src/namedef.h
index 0383a6764..c307c3744 100644
--- a/src/namedef.h
+++ b/src/namedef.h
@@ -249,7 +249,6 @@ xx(Sector)
 xx(Heightfloor)
 xx(Heightceiling)
 xx(Lightlevel)
-xx(Tag)
 xx(Texturefloor)
 xx(Textureceiling)
 
@@ -305,7 +304,7 @@ xx(Twosided)
 xx(Dontpegtop)
 xx(Dontpegbottom)
 xx(Secret)
-xx(Soundblock)
+xx(Blocksound)
 xx(Dontdraw)
 xx(Mapped)
 xx(Monsteractivate)
@@ -321,6 +320,7 @@ xx(Checkswitchrange)
 xx(Firstsideonly)
 xx(Transparent)	
 xx(Passuse)
+xx(Repeatspecial)
 
 xx(Playercross)
 xx(Playeruse)
diff --git a/src/p_acs.cpp b/src/p_acs.cpp
index 6ef9873a3..804c98053 100644
--- a/src/p_acs.cpp
+++ b/src/p_acs.cpp
@@ -5351,8 +5351,129 @@ int DLevelScript::RunScript ()
 				}
 			}
 			break;
-		}
-	}
+
+		case PCD_MORPHACTOR:
+			{
+				int tag = STACK(7);
+				FName playerclass_name = FBehavior::StaticLookupString(STACK(6));
+				const PClass *playerclass = PClass::FindClass (playerclass_name);
+				FName monsterclass_name = FBehavior::StaticLookupString(STACK(5));
+				const PClass *monsterclass = PClass::FindClass (monsterclass_name);
+				int duration = STACK(4);
+				int style = STACK(3);
+				FName morphflash_name = FBehavior::StaticLookupString(STACK(2));
+				const PClass *morphflash = PClass::FindClass (morphflash_name);
+				FName unmorphflash_name = FBehavior::StaticLookupString(STACK(1));
+				const PClass *unmorphflash = PClass::FindClass (unmorphflash_name);
+				int changes = 0;
+
+				if (tag == 0)
+				{
+					if (activator->player)
+					{
+						if (P_MorphPlayer(activator->player, activator->player, playerclass, duration, style, morphflash, unmorphflash))
+						{
+							changes++;
+						}
+					}
+					else
+					{
+						if (P_MorphMonster(activator, monsterclass, duration, style, morphflash, unmorphflash))
+						{
+							changes++;
+						}
+					}
+				}
+				else
+				{
+					FActorIterator iterator (tag);
+					AActor *actor;
+
+					while ( (actor = iterator.Next ()) )
+					{
+						if (actor->player)
+						{
+							if (P_MorphPlayer(activator->player, actor->player, playerclass, duration, style, morphflash, unmorphflash))
+							{
+								changes++;
+							}
+						}
+						else
+						{
+							if (P_MorphMonster(actor, monsterclass, duration, style, morphflash, unmorphflash))
+							{
+								changes++;
+							}
+						}
+					}
+				}
+
+				STACK(7) = changes;
+				sp -= 6;
+			}	
+			break;
+
+		case PCD_UNMORPHACTOR:
+			{
+				int tag = STACK(2);
+				bool force = !!STACK(1);
+				int changes = 0;
+
+				if (tag == 0)
+				{
+					if (activator->player)
+					{
+						if (P_UndoPlayerMorph(activator->player, activator->player, force))
+						{
+							changes++;
+						}
+					}
+					else
+					{
+						if (activator->GetClass()->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster)))
+						{
+							AMorphedMonster *morphed_actor = barrier_cast<AMorphedMonster *>(activator);
+							if (P_UndoMonsterMorph(morphed_actor, force))
+							{
+								changes++;
+							}
+						}
+					}
+				}
+				else
+				{
+					FActorIterator iterator (tag);
+					AActor *actor;
+
+					while ( (actor = iterator.Next ()) )
+					{
+						if (actor->player)
+						{
+							if (P_UndoPlayerMorph(activator->player, actor->player, force))
+							{
+								changes++;
+							}
+						}
+						else
+						{
+							if (actor->GetClass()->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster)))
+							{
+								AMorphedMonster *morphed_actor = static_cast<AMorphedMonster *>(actor);
+								if (P_UndoMonsterMorph(morphed_actor, force))
+								{
+									changes++;
+								}
+							}
+						}
+					}
+				}
+
+				STACK(2) = changes;
+				sp -= 1;
+			}	
+			break;
+ 		}
+ 	}
 
 	if (state == SCRIPT_DivideBy0)
 	{
diff --git a/src/p_acs.h b/src/p_acs.h
index 072d2ffbd..0485a5d67 100644
--- a/src/p_acs.h
+++ b/src/p_acs.h
@@ -551,6 +551,8 @@ public:
 		PCD_THINGCOUNTSECTOR,
 		PCD_THINGCOUNTNAMESECTOR,
 		PCD_CHECKPLAYERCAMERA,		// [TN]
+		PCD_MORPHACTOR,				// [MH]
+		PCD_UNMORPHACTOR,			// [MH]
 
 		PCODE_COMMAND_COUNT
 	};
diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp
index 7a4e22d72..a50f57bb2 100644
--- a/src/p_conversation.cpp
+++ b/src/p_conversation.cpp
@@ -236,6 +236,7 @@ static void LoadScriptFile (const char *name)
 	lump = Wads.ReopenLumpNum (lumpnum);
 
 	LoadScriptFile(lump, Wads.LumpLength(lumpnum));
+	delete lump;
 }
 
 static void LoadScriptFile(FileReader *lump, int numnodes)
@@ -278,7 +279,6 @@ static void LoadScriptFile(FileReader *lump, int numnodes)
 		}
 		StrifeDialogues.Push (node);
 	}
-	delete lump;
 }
 
 //============================================================================
diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp
index 9da7f3dc2..5c55efd65 100644
--- a/src/p_interaction.cpp
+++ b/src/p_interaction.cpp
@@ -317,8 +317,40 @@ void ClientObituary (AActor *self, AActor *inflictor, AActor *attacker)
 //
 EXTERN_CVAR (Int, fraglimit)
 
+static int GibHealth(AActor *actor)
+{
+	return -abs(
+		actor->GetClass()->Meta.GetMetaInt (
+			AMETA_GibHealth,
+			gameinfo.gametype == GAME_Doom ?
+				-actor->GetDefault()->health :
+				-actor->GetDefault()->health/2));
+}
+
 void AActor::Die (AActor *source, AActor *inflictor)
 {
+	// Handle possible unmorph on death
+	bool wasgibbed = (health < GibHealth(this));
+	AActor *realthis = NULL;
+	int realstyle = 0;
+	int realhealth = 0;
+	if (P_MorphedDeath(this, &realthis, &realstyle, &realhealth))
+	{
+		if (!(realstyle & MORPH_UNDOBYDEATHSAVES))
+		{
+			if (wasgibbed)
+			{
+				int realgibhealth = GibHealth(realthis);
+				if (realthis->health >= realgibhealth)
+				{
+					realthis->health = realgibhealth -1; // if morphed was gibbed, so must original be (where allowed)
+				}
+			}
+			realthis->Die(source, inflictor);
+		}
+		return;
+	}
+
 	// [SO] 9/2/02 -- It's rather funny to see an exploded player body with the invuln sparkle active :) 
 	effects &= ~FX_RESPAWNINVUL;
 	//flags &= ~MF_INVINCIBLE;
@@ -634,8 +666,7 @@ void AActor::Die (AActor *source, AActor *inflictor)
 	{
 		int flags4 = inflictor == NULL ? 0 : inflictor->flags4;
 
-		int gibhealth = -abs(GetClass()->Meta.GetMetaInt (AMETA_GibHealth,
-			gameinfo.gametype == GAME_Doom ? -GetDefault()->health : -GetDefault()->health/2));
+		int gibhealth = GibHealth(this);
 		
 		// Don't pass on a damage type this actor cannot handle.
 		// (most importantly, prevent barrels from passing on ice damage.)
diff --git a/src/p_local.h b/src/p_local.h
index 470a0895d..cd30a60ed 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -564,9 +564,6 @@ enum
 	PO_SPAWNHURT_TYPE
 };
 
-#define PO_LINE_START 1 // polyobj line start special
-#define PO_LINE_EXPLICIT 5
-
 extern polyobj_t *polyobjs; // list of all poly-objects on the level
 extern int po_NumPolyobjs;
 extern polyspawns_t *polyspawns;	// [RH] list of polyobject things to spawn
diff --git a/src/p_map.cpp b/src/p_map.cpp
index c4ac9d5f3..3e0ced12e 100644
--- a/src/p_map.cpp
+++ b/src/p_map.cpp
@@ -608,8 +608,7 @@ bool PIT_CheckLine (line_t *ld, const FBoundingBox &box, FCheckPosition &tm)
 		// better than Strife's handling of rails, which lets you jump into rails
 		// from either side. How long until somebody reports this as a bug and I'm
 		// forced to say, "It's not a bug. It's a feature?" Ugh.
-		(gameinfo.gametype != GAME_Strife ||
-		 level.flags & LEVEL_HEXENFORMAT ||
+		(!(level.flags & LEVEL_RAILINGHACK) ||
 		 open.bottom == tm.thing->Sector->floorplane.ZatPoint (sx, sy)))
 	{
 		open.bottom += 32*FRACUNIT;
@@ -3599,6 +3598,7 @@ void P_RadiusAttack (AActor *bombspot, AActor *bombsource, int bombdamage, int b
 
 struct FChangePosition
 {
+	sector_t *sector;
 	int moveamt;
 	int crushchange;
 	bool nofit;
@@ -3953,7 +3953,7 @@ void PIT_FloorDrop (AActor *thing, FChangePosition *cpos)
 			P_CheckFakeFloorTriggers (thing, oldz);
 		}
 		else if ((thing->flags & MF_NOGRAVITY) ||
-			((!(level.flags & LEVEL_HEXENFORMAT) || cpos->moveamt < 9*FRACUNIT)
+			(((cpos->sector->Flags & SECF_FLOORDROP) || cpos->moveamt < 9*FRACUNIT)
 			 && thing->z - thing->floorz <= cpos->moveamt))
 		{
 			thing->z = thing->floorz;
@@ -4104,6 +4104,7 @@ bool P_ChangeSector (sector_t *sector, int crunch, int amt, int floorOrCeil, boo
 	cpos.crushchange = crunch;
 	cpos.moveamt = abs (amt);
 	cpos.movemidtex = false;
+	cpos.sector = sector;
 
 	// [RH] Use different functions for the four different types of sector
 	// movement. Also update the soundorg's z-coordinate for 3D sound.
diff --git a/src/p_setup.cpp b/src/p_setup.cpp
index 254495176..fba52444e 100644
--- a/src/p_setup.cpp
+++ b/src/p_setup.cpp
@@ -370,6 +370,7 @@ MapData *P_OpenMapData(const char * mapname)
 					else if (!stricmp(lumpname, "BEHAVIOR"))
 					{
 						index = ML_BEHAVIOR;
+						map->HasBehavior = true;
 					}
 					else if (!stricmp(lumpname, "ENDMAP"))
 					{
@@ -411,14 +412,16 @@ MapData *P_OpenMapData(const char * mapname)
 		(*map->file) >> numentries >> dirofs;
 
 		map->file->Seek(dirofs, SEEK_SET);
-		for(DWORD i = 0; i < numentries; i++)
+		(*map->file) >> map->MapLumps[0].FilePos >> map->MapLumps[0].Size;
+		map->file->Read(map->MapLumps[0].Name, 8);
+
+		for(DWORD i = 1; i < numentries; i++)
 		{
 			DWORD offset, size;
 			char lumpname[8];
 
 			(*map->file) >> offset >> size;
 			map->file->Read(lumpname, 8);
-
 			if (i == 1 && !strnicmp(lumpname, "TEXTMAP", 8))
 			{
 				map->isText = true;
@@ -432,28 +435,29 @@ MapData *P_OpenMapData(const char * mapname)
 					{
 						I_Error("Invalid map definition for %s", mapname);
 					}
-					else if (!stricmp(lumpname, "ZNODES"))
+					else if (!strnicmp(lumpname, "ZNODES",8))
 					{
 						index = ML_GLZNODES;
 					}
-					else if (!stricmp(lumpname, "BLOCKMAP"))
+					else if (!strnicmp(lumpname, "BLOCKMAP",8))
 					{
 						// there is no real point in creating a blockmap but let's use it anyway
 						index = ML_BLOCKMAP;
 					}
-					else if (!stricmp(lumpname, "REJECT"))
+					else if (!strnicmp(lumpname, "REJECT",8))
 					{
 						index = ML_REJECT;
 					}
-					else if (!stricmp(lumpname, "DIALOGUE"))
+					else if (!strnicmp(lumpname, "DIALOGUE",8))
 					{
 						index = ML_CONVERSATION;
 					}
-					else if (!stricmp(lumpname, "BEHAVIOR"))
+					else if (!strnicmp(lumpname, "BEHAVIOR",8))
 					{
 						index = ML_BEHAVIOR;
+						map->HasBehavior = true;
 					}
-					else if (!stricmp(lumpname, "ENDMAP"))
+					else if (!strnicmp(lumpname, "ENDMAP",8))
 					{
 						return map;
 					}
@@ -1179,6 +1183,7 @@ void P_LoadSectors (MapData * map)
 	for (i = 0; i < numsectors; i++, ss++, ms++)
 	{
 		ss->e = &sectors[0].e[i];
+		if (!map->HasBehavior) ss->Flags |= SECF_FLOORDROP;
 		ss->floortexz = LittleShort(ms->floorheight)<<FRACBITS;
 		ss->floorplane.d = -ss->floortexz;
 		ss->floorplane.c = FRACUNIT;
@@ -3266,6 +3271,16 @@ void P_SetupLevel (char *lumpname, int position)
 			}
 		}
 
+		if (!map->HasBehavior && !map->isText)
+		{
+			// set compatibility flags
+			if (gameinfo.gametype == GAME_Strife)
+			{
+				level.flags |= LEVEL_RAILINGHACK;
+			}
+			level.flags |= LEVEL_DUMMYSWITCHES;
+		}
+
 		FBehavior::StaticLoadDefaultModules ();
 
 		P_LoadStrifeConversations (map, lumpname);
diff --git a/src/p_spec.cpp b/src/p_spec.cpp
index 3980948c6..2f414c4f3 100644
--- a/src/p_spec.cpp
+++ b/src/p_spec.cpp
@@ -229,11 +229,12 @@ bool P_ActivateLine (line_t *line, AActor *mo, int side, int activationType)
 	}
 	// some old WADs use this method to create walls that change the texture when shot.
 	else if (activationType == SPAC_Impact &&					// only for shootable triggers
-		!(level.flags & LEVEL_HEXENFORMAT) &&					// only in Doom-format maps
+		(level.flags & LEVEL_DUMMYSWITCHES) &&					// this is only a compatibility setting for an old hack!
 		!repeat &&												// only non-repeatable triggers
 		(special<Generic_Floor || special>Generic_Crusher) &&	// not for Boom's generalized linedefs
 		special &&												// not for lines without a special
-		line->id &&												// only if there's a tag (which is stored in the id field)
+		line->args[0] == line->id &&							// Safety check: exclude edited UDMF linedefs or ones that don't map the tag to args[0]
+		line->args[0] &&										// only if there's a tag (which is stored in the first arg)
 		P_FindSectorFromTag (line->args[0], -1) == -1)			// only if no sector is tagged to this linedef
 	{
 		P_ChangeSwitchTexture (&sides[line->sidenum[0]], repeat, special);
diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp
index 2273d1e71..fe61061e4 100644
--- a/src/p_udmf.cpp
+++ b/src/p_udmf.cpp
@@ -40,6 +40,67 @@
 #include "p_lnspec.h"
 #include "templates.h"
 #include "i_system.h"
+#include "gi.h"
+
+// These tables define whichline an
+static char HexenLineSpecialOk[]={
+	1,1,1,1,1,1,1,1,1,0,	// 0-9
+	1,1,1,1,0,0,0,0,0,0,	// 10-19
+	1,1,1,1,1,1,1,1,1,1,	// 20-29
+	1,1,1,0,0,1,1,0,0,0,	// 30-39
+	1,1,1,1,1,1,1,0,0,0,	// 40-49
+	0,0,0,0,0,0,0,0,0,0,	// 50-59
+	1,1,1,1,1,1,1,1,1,1,	// 60-69
+	1,1,1,1,1,1,0,0,0,0,	// 70-79
+	1,1,1,1,0,0,0,0,0,0,	// 80-89
+	1,1,1,1,1,1,1,0,0,0,	// 90-99
+	1,1,1,1,0,0,0,0,0,1,	// 100-109
+	1,1,1,1,1,1,1,0,0,0,	// 110-119
+	1,0,0,0,0,0,0,0,0,1,	// 120-129
+	1,1,1,1,1,1,1,1,1,0,	// 130-139
+	1
+	// 140 is the highest valid special in Hexen.
+
+};
+
+static char HexenSectorSpecialOk[256]={
+	1,1,1,1,1,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,1,1,0,0,
+	0,0,0,0,0,0,0,0,0,0,
+	1,1,1,1,1,1,1,1,1,1,
+	1,1,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,1,1,
+	1,1,1,1,1,1,1,1,1,1,
+	1,1,1,1,1,
+};
+
+
+enum
+{
+	Dm=1,
+	Ht=2,
+	Hx=4,
+	St=8,
+	Zd=16,
+	Zdt=32,
+
+	// will be extended later. Unknown namespaces will always be treated like the base
+	// namespace for each game
+};
+
 
 void P_ProcessSideTextures(bool checktranmap, side_t *sd, sector_t *sec, mapsidedef_t *msd, int special, int tag, short *alpha);
 void P_AdjustLine (line_t *ld);
@@ -49,12 +110,17 @@ extern bool		ForceNodeBuild;
 extern TArray<FMapThing> MapThingsConverted;
 extern TArray<int>		linemap;
 
+
+#define CHECK_N(f) if (!(namespace_bits&(f))) break;
+
 struct UDMFParser
 {
 	FScanner sc;
 	FName namespc;
+	int namespace_bits;
 	bool isTranslated;
 	bool isExtended;
+	bool floordrop;
 
 	TArray<line_t> ParsedLines;
 	TArray<side_t> ParsedSides;
@@ -70,60 +136,126 @@ struct UDMFParser
 		fogMap = normMap = NULL;
 	}
 
-	void Flag(DWORD &value, int mask, const FString &svalue)
+	FName ParseKey()
 	{
-		if (!svalue.CompareNoCase("true"))
+		sc.MustGetString();
+		FName key = sc.String;
+		sc.MustGetToken('=');
+
+		sc.Number = 0;
+		sc.Float = 0;
+		sc.MustGetAnyToken();
+
+		if (sc.TokenType == '+' || sc.TokenType == '-')
 		{
-			value |= mask;
+			bool neg = (sc.TokenType == '-');
+			sc.MustGetAnyToken();
+			if (sc.TokenType != TK_IntConst && sc.TokenType != TK_FloatConst)
+			{
+				sc.ScriptMessage("Numeric constant expected");
+			}
+			if (neg)
+			{
+				sc.Number = -sc.Number;
+				sc.Float = -sc.Float;
+			}
 		}
-		else
+		return key;
+	}
+
+	int CheckInt(const char *key)
+	{
+		if (sc.TokenType != TK_IntConst)
 		{
-			value &= ~mask;
+			sc.ScriptMessage("Integer value expected for key '%s'", key);
 		}
+		return sc.Number;
+	}
+
+	double CheckFloat(const char *key)
+	{
+		if (sc.TokenType != TK_IntConst && sc.TokenType != TK_FloatConst)
+		{
+			sc.ScriptMessage("Floatint point value expected for key '%s'", key);
+		}
+		return sc.Float;
+	}
+
+	fixed_t CheckFixed(const char *key)
+	{
+		return FLOAT2FIXED(CheckFloat(key));
+	}
+
+	bool CheckBool(const char *key)
+	{
+		if (sc.TokenType == TK_True) return true;
+		if (sc.TokenType == TK_False) return false;
+		sc.ScriptMessage("Boolean value expected for key '%s'", key);
+		return false;
+	}
+
+	const char *CheckString(const char *key)
+	{
+		if (sc.TokenType != TK_StringConst)
+		{
+			sc.ScriptMessage("String value expected for key '%s'", key);
+		}
+		return sc.String;
+	}
+
+	void Flag(DWORD &value, int mask, const char *key)
+	{
+		if (CheckBool(key))	value |= mask;
+		else value &= ~mask;
 	}
 
 	void ParseThing(FMapThing *th)
 	{
 		memset(th, 0, sizeof(*th));
-		sc.MustGetStringName("{");
-		while (!sc.CheckString("}"))
+		sc.MustGetToken('{');
+		while (!sc.CheckToken('}'))
 		{
-			sc.MustGetString();
-			FName key = sc.String;
-			sc.MustGetStringName("=");
-			sc.MustGetString();
-			FString value = sc.String;
-			sc.MustGetStringName(";");
+			FName key = ParseKey();
 			switch(key)
 			{
-			case NAME_TID:
-				th->thingid = (WORD)strtol(value, NULL, 0);
+			case NAME_Id:
+				th->thingid = CheckInt(key);
 				break;
+
 			case NAME_X:
-				th->x = FLOAT2FIXED(strtod(value, NULL));
+				th->x = CheckFixed(key);
 				break;
+
 			case NAME_Y:
-				th->y = FLOAT2FIXED(strtod(value, NULL));
+				th->y = CheckFixed(key);
 				break;
+
 			case NAME_Height:
-				th->z = FLOAT2FIXED(strtod(value, NULL));
+				th->z = CheckFixed(key);
 				break;
+
 			case NAME_Angle:
-				th->angle = (short)strtol(value, NULL, 0);
+				th->angle = (short)CheckInt(key);
 				break;
+
 			case NAME_Type:
-				th->type = (short)strtol(value, NULL, 0);
+				th->type = (short)CheckInt(key);
 				break;
+
 			case NAME_Special:
-				th->special = (short)strtol(value, NULL, 0);
+				CHECK_N(Hx | Zd | Zdt)
+				th->special = CheckInt(key);
 				break;
+
 			case NAME_Arg0:
 			case NAME_Arg1:
 			case NAME_Arg2:
 			case NAME_Arg3:
 			case NAME_Arg4:
-				th->args[int(key)-int(NAME_Arg0)] = strtol(value, NULL, 0);
+				CHECK_N(Hx | Zd | Zdt)
+				th->args[int(key)-int(NAME_Arg0)] = CheckInt(key);
 				break;
+
 			case NAME_Skill1:
 			case NAME_Skill2:
 			case NAME_Skill3:
@@ -140,11 +272,10 @@ struct UDMFParser
 			case NAME_Skill14:
 			case NAME_Skill15:
 			case NAME_Skill16:
-				if (!value.CompareNoCase("true")) th->SkillFilter |= (1<<(int(key)-NAME_Skill1));
+				if (CheckBool(key)) th->SkillFilter |= (1<<(int(key)-NAME_Skill1));
 				else th->SkillFilter &= ~(1<<(int(key)-NAME_Skill1));
 				break;
 
-			case NAME_Class0:
 			case NAME_Class1:
 			case NAME_Class2:
 			case NAME_Class3:
@@ -161,43 +292,72 @@ struct UDMFParser
 			case NAME_Class14:
 			case NAME_Class15:
 			case NAME_Class16:
-				if (!value.CompareNoCase("true")) th->ClassFilter |= (1<<(int(key)-NAME_Class1));
+				CHECK_N(Hx | Zd | Zdt)
+				if (CheckBool(key)) th->ClassFilter |= (1<<(int(key)-NAME_Class1));
 				else th->SkillFilter &= ~(1<<(int(key)-NAME_Class1));
 				break;
 
 			case NAME_Ambush:
-				Flag(th->flags, MTF_AMBUSH, value); break;
+				Flag(th->flags, MTF_AMBUSH, key); 
+				break;
+
 			case NAME_Dormant:
-				Flag(th->flags, MTF_DORMANT, value); break;
+				CHECK_N(Hx | Zd | Zdt)
+				Flag(th->flags, MTF_DORMANT, key); 
+				break;
+
 			case NAME_Single:
-				Flag(th->flags, MTF_SINGLE, value); break;
+				Flag(th->flags, MTF_SINGLE, key); 
+				break;
+
 			case NAME_Coop:
-				Flag(th->flags, MTF_COOPERATIVE, value); break;
+				Flag(th->flags, MTF_COOPERATIVE, key); 
+				break;
+
 			case NAME_Dm:
-				Flag(th->flags, MTF_DEATHMATCH, value); break;
+				Flag(th->flags, MTF_DEATHMATCH, key); 
+				break;
+
 			case NAME_Translucent:
-				Flag(th->flags, MTF_SHADOW, value); break;
+				CHECK_N(St | Zd | Zdt)
+				Flag(th->flags, MTF_SHADOW, key); 
+				break;
+
 			case NAME_Invisible:
-				Flag(th->flags, MTF_ALTSHADOW, value); break;
-			case NAME_Friend:
+				CHECK_N(St | Zd | Zdt)
+				Flag(th->flags, MTF_ALTSHADOW, key); 
+				break;
+
+			case NAME_Friend:	// This maps to Strife's friendly flag
+				CHECK_N(Dm | Zd | Zdt)
+				Flag(th->flags, MTF_FRIENDLY, key); 
+				break;
+
 			case NAME_Strifeally:
-				Flag(th->flags, MTF_FRIENDLY, value); break;
+				CHECK_N(St | Zd | Zdt)
+				Flag(th->flags, MTF_FRIENDLY, key); 
+				break;
+
 			case NAME_Standing:
-				Flag(th->flags, MTF_STANDSTILL, value); break;
+				CHECK_N(St | Zd | Zdt)
+				Flag(th->flags, MTF_STANDSTILL, key); 
+				break;
 
 			default:
 				break;
 			}
+			sc.MustGetToken(';');
 		}
-		if (isTranslated)
+		// Thing specials are only valid in namespaces with Hexen-type specials
+		// and in ZDoomTranslated - which will use the translator on them.
+		if (namespc == NAME_ZDoomTranslated)
 		{
-			if (isExtended)
-			{
-				// NOTE: Handling of this is undefined in the UDMF spec 
-				// so it is only done for namespace ZDoomTranslated
-				maplinedef_t mld;
-				line_t ld;
+			maplinedef_t mld;
+			line_t ld;
 
+			if (th->special != 0)	// if special is 0, keep the args (e.g. for bridge things)
+			{
+				// The trigger type is ignored here.
 				mld.flags = 0;
 				mld.special = th->special;
 				mld.tag = th->args[0];
@@ -205,17 +365,18 @@ struct UDMFParser
 				th->special = ld.special;
 				memcpy(th->args, ld.args, sizeof (ld.args));
 			}
-			else	// NULL the special
-			{
-				th->special = 0;
-				memset(th->args, 0, sizeof (th->args));
-			}
+		}
+		else if (isTranslated)
+		{
+			th->special = 0;
+			memset(th->args, 0, sizeof (th->args));
 		}
 	}
 
 	void ParseLinedef(line_t *ld)
 	{
 		bool passuse = false;
+		bool strifetrans = false;
 
 		memset(ld, 0, sizeof(*ld));
 		ld->Alpha = FRACUNIT;
@@ -224,72 +385,109 @@ struct UDMFParser
 		if (level.flags & LEVEL_CLIPMIDTEX) ld->flags |= ML_CLIP_MIDTEX;
 		if (level.flags & LEVEL_WRAPMIDTEX) ld->flags |= ML_WRAP_MIDTEX;
 		if (level.flags & LEVEL_CHECKSWITCHRANGE) ld->flags |= ML_CHECKSWITCHRANGE;
-		sc.MustGetStringName("{");
-		while (!sc.CheckString("}"))
+
+		sc.MustGetToken('{');
+		while (!sc.CheckToken('}'))
 		{
-			sc.MustGetString();
-			FName key = sc.String;
-			sc.MustGetStringName("=");
-			sc.MustGetString();
-			FString value = sc.String;
-			sc.MustGetStringName(";");
+			FName key = ParseKey();
 
 			// This switch contains all keys of the UDMF base spec
 			switch(key)
 			{
 			case NAME_V1:
-				ld->v1 = (vertex_t*)(intptr_t)strtol(value, NULL, 0);	// must be relocated later
+				ld->v1 = (vertex_t*)(intptr_t)CheckInt(key);	// must be relocated later
 				break;
+
 			case NAME_V2:
-				ld->v2 = (vertex_t*)(intptr_t)strtol(value, NULL, 0);	// must be relocated later
+				ld->v2 = (vertex_t*)(intptr_t)CheckInt(key);	// must be relocated later
 				break;
+
 			case NAME_Special:
-				ld->special = strtol(value, NULL, 0);
+				ld->special = CheckInt(key);
+				if (namespc == NAME_Hexen)
+				{
+					if (ld->special < 0 || ld->special > 140 || !HexenLineSpecialOk[ld->special])
+						ld->special = 0;	// NULL all specials which don't exist in Hexen
+				}
+
 				break;
+
 			case NAME_Id:
-				ld->id = strtol(value, NULL, 0);
+				ld->id = CheckInt(key);
 				break;
+
 			case NAME_Sidefront:
-				ld->sidenum[0] = strtol(value, NULL, 0);
+				ld->sidenum[0] = CheckInt(key);
 				break;
+
 			case NAME_Sideback:
-				ld->sidenum[1] = strtol(value, NULL, 0);
+				ld->sidenum[1] = CheckInt(key);
 				break;
+
 			case NAME_Arg0:
 			case NAME_Arg1:
 			case NAME_Arg2:
 			case NAME_Arg3:
 			case NAME_Arg4:
-				ld->args[int(key)-int(NAME_Arg0)] = strtol(value, NULL, 0);
+				ld->args[int(key)-int(NAME_Arg0)] = CheckInt(key);
 				break;
+
 			case NAME_Blocking:
-				Flag(ld->flags, ML_BLOCKING, value); break;
+				Flag(ld->flags, ML_BLOCKING, key); 
+				break;
+
 			case NAME_Blockmonsters:
-				Flag(ld->flags, ML_BLOCKMONSTERS, value); break;
+				Flag(ld->flags, ML_BLOCKMONSTERS, key); 
+				break;
+
 			case NAME_Twosided:
-				Flag(ld->flags, ML_TWOSIDED, value); break;
+				Flag(ld->flags, ML_TWOSIDED, key); 
+				break;
+
 			case NAME_Dontpegtop:
-				Flag(ld->flags, ML_DONTPEGTOP, value); break;
+				Flag(ld->flags, ML_DONTPEGTOP, key); 
+				break;
+
 			case NAME_Dontpegbottom:
-				Flag(ld->flags, ML_DONTPEGBOTTOM, value); break;
+				Flag(ld->flags, ML_DONTPEGBOTTOM, key); 
+				break;
+
 			case NAME_Secret:
-				Flag(ld->flags, ML_SECRET, value); break;
-			case NAME_Soundblock:
-				Flag(ld->flags, ML_SOUNDBLOCK, value); break;
+				Flag(ld->flags, ML_SECRET, key); 
+				break;
+
+			case NAME_Blocksound:
+				Flag(ld->flags, ML_SOUNDBLOCK, key); 
+				break;
+
 			case NAME_Dontdraw:
-				Flag(ld->flags, ML_DONTDRAW, value); break;
+				Flag(ld->flags, ML_DONTDRAW, key); 
+				break;
+
 			case NAME_Mapped:
-				Flag(ld->flags, ML_MAPPED, value); break;
-			case NAME_Monsteractivate:
-				Flag(ld->flags, ML_MONSTERSCANACTIVATE, value); break;
+				Flag(ld->flags, ML_MAPPED, key); 
+				break;
+
 			case NAME_Jumpover:
-				Flag(ld->flags, ML_RAILING, value); break;
+				CHECK_N(St | Zd | Zdt)
+				Flag(ld->flags, ML_RAILING, key); 
+				break;
+
 			case NAME_Blockfloating:
-				Flag(ld->flags, ML_BLOCK_FLOATERS, value); break;
+				CHECK_N(St | Zd | Zdt)
+				Flag(ld->flags, ML_BLOCK_FLOATERS, key); 
+				break;
+
 			case NAME_Transparent:	
-				ld->Alpha = !value.CompareNoCase("true")? FRACUNIT*3/4 : FRACUNIT; break;
+				CHECK_N(St | Zd | Zdt)
+				strifetrans = CheckBool(key); 
+				break;
+
 			case NAME_Passuse:
-				passuse = !value.CompareNoCase("true"); break;
+				CHECK_N(Dm | Zd | Zdt)
+				passuse = CheckBool(key); 
+				break;
+
 			default:
 				break;
 			}
@@ -298,48 +496,94 @@ struct UDMFParser
 			if (!isTranslated) switch (key)
 			{
 			case NAME_Playercross:
-				Flag(ld->activation, SPAC_Cross, value); break;
+				Flag(ld->activation, SPAC_Cross, key); 
+				break;
+
 			case NAME_Playeruse:
-				Flag(ld->activation, SPAC_Use, value); break;
+				Flag(ld->activation, SPAC_Use, key); 
+				break;
+
 			case NAME_Monstercross:
-				Flag(ld->activation, SPAC_MCross, value); break;
+				Flag(ld->activation, SPAC_MCross, key); 
+				break;
+
 			case NAME_Impact:
-				Flag(ld->activation, SPAC_Impact, value); break;
+				Flag(ld->activation, SPAC_Impact, key); 
+				break;
+
 			case NAME_Playerpush:
-				Flag(ld->activation, SPAC_Push, value); break;
+				Flag(ld->activation, SPAC_Push, key); 
+				break;
+
 			case NAME_Missilecross:
-				Flag(ld->activation, SPAC_PCross, value); break;
+				Flag(ld->activation, SPAC_PCross, key); 
+				break;
+
 			case NAME_Monsteruse:
-				Flag(ld->activation, SPAC_MUse, value); break;
+				Flag(ld->activation, SPAC_MUse, key); 
+				break;
+
 			case NAME_Monsterpush:
-				Flag(ld->activation, SPAC_MPush, value); break;
+				Flag(ld->activation, SPAC_MPush, key); 
+				break;
+
+			case NAME_Repeatspecial:
+				Flag(ld->flags, ML_REPEAT_SPECIAL, key); 
+				break;
+
 			default:
 				break;
 			}
 
 			// This switch contains all keys which are ZDoom specific
-			if (isExtended) switch(key)
+			if (namespace_bits & (Zd|Zdt)) switch(key)
 			{
+			case NAME_Anycross:
+				Flag(ld->activation, SPAC_AnyCross, key); 
+				break;
+
+			case NAME_Monsteractivate:
+				Flag(ld->flags, ML_MONSTERSCANACTIVATE, key); 
+				break;
+
 			case NAME_Blockplayers:
-				Flag(ld->flags, ML_BLOCK_PLAYERS, value); break;
+				Flag(ld->flags, ML_BLOCK_PLAYERS, key); 
+				break;
+
 			case NAME_Blockeverything:
-				Flag(ld->flags, ML_BLOCKEVERYTHING, value); break;
+				Flag(ld->flags, ML_BLOCKEVERYTHING, key); 
+				break;
+
 			case NAME_Zoneboundary:
-				Flag(ld->flags, ML_ZONEBOUNDARY, value); break;
+				Flag(ld->flags, ML_ZONEBOUNDARY, key); 
+				break;
+
 			case NAME_Clipmidtex:
-				Flag(ld->flags, ML_CLIP_MIDTEX, value); break;
+				Flag(ld->flags, ML_CLIP_MIDTEX, key); 
+				break;
+
 			case NAME_Wrapmidtex:
-				Flag(ld->flags, ML_WRAP_MIDTEX, value); break;
+				Flag(ld->flags, ML_WRAP_MIDTEX, key); 
+				break;
+
 			case NAME_Midtex3d:
-				Flag(ld->flags, ML_3DMIDTEX, value); break;
+				Flag(ld->flags, ML_3DMIDTEX, key); 
+				break;
+
 			case NAME_Checkswitchrange:
-				Flag(ld->flags, ML_CHECKSWITCHRANGE, value); break;
+				Flag(ld->flags, ML_CHECKSWITCHRANGE, key); 
+				break;
+
 			case NAME_Firstsideonly:
-				Flag(ld->flags, ML_FIRSTSIDEONLY, value); break;
+				Flag(ld->flags, ML_FIRSTSIDEONLY, key); 
+				break;
+
 			default:
 				break;
 			}
+			sc.MustGetToken(';');
 		}
+
 		if (isTranslated)
 		{
 			int saved = ld->flags;
@@ -355,6 +599,10 @@ struct UDMFParser
 		{
 			ld->activation = (ld->activation & ~SPAC_Use) | SPAC_UseThrough;
 		}
+		if (strifetrans && ld->Alpha == FRACUNIT)
+		{
+			ld->Alpha = FRACUNIT * 3/4;
+		}
 	}
 
 	void ParseSidedef(side_t *sd, mapsidedef_t *sdt)
@@ -365,44 +613,41 @@ struct UDMFParser
 		strncpy(sdt->bottomtexture, "-", 8);
 		strncpy(sdt->toptexture, "-", 8);
 		strncpy(sdt->midtexture, "-", 8);
-		sc.MustGetStringName("{");
-		while (!sc.CheckString("}"))
+
+		sc.MustGetToken('{');
+		while (!sc.CheckToken('}'))
 		{
-			sc.MustGetString();
-			FName key = sc.String;
-			sc.MustGetStringName("=");
-			sc.MustGetString();
-			FString value = sc.String;
-			sc.MustGetStringName(";");
+			FName key = ParseKey();
 			switch(key)
 			{
 			case NAME_Offsetx:
-				texofs[0] = strtol(value, NULL, 0) << FRACBITS;
+				texofs[0] = CheckInt(key) << FRACBITS;
 				break;
 
 			case NAME_Offsety:
-				texofs[1] = strtol(value, NULL, 0) << FRACBITS;
+				texofs[1] = CheckInt(key) << FRACBITS;
 				break;
 
 			case NAME_Texturetop:
-				strncpy(sdt->toptexture, value, 8);
+				strncpy(sdt->toptexture, CheckString(key), 8);
 				break;
 
 			case NAME_Texturebottom:
-				strncpy(sdt->bottomtexture, value, 8);
+				strncpy(sdt->bottomtexture, CheckString(key), 8);
 				break;
 
 			case NAME_Texturemiddle:
-				strncpy(sdt->midtexture, value, 8);
+				strncpy(sdt->midtexture, CheckString(key), 8);
 				break;
 
 			case NAME_Sector:
-				sd->sector = (sector_t*)(intptr_t)strtol(value, NULL, 0);
+				sd->sector = (sector_t*)(intptr_t)CheckInt(key);
 				break;
 
 			default:
 				break;
 			}
+			sc.MustGetToken(';');
 		}
 		// initialization of these is delayed to allow separate offsets and add them with the global ones.
 		sd->AddTextureXOffset(side_t::top, texofs[0]);
@@ -416,7 +661,7 @@ struct UDMFParser
 	void ParseSector(sector_t *sec)
 	{
 		memset(sec, 0, sizeof(*sec));
-		sec->lightlevel = 255;
+		sec->lightlevel = 160;
 		sec->floor_xscale = FRACUNIT;	// [RH] floor and ceiling scaling
 		sec->floor_yscale = FRACUNIT;
 		sec->ceiling_xscale = FRACUNIT;
@@ -428,6 +673,7 @@ struct UDMFParser
 		sec->nextsec = -1;	//jff 2/26/98 add fields to support locking out
 		sec->prevsec = -1;	// stair retriggering until build completes
 		sec->heightsec = NULL;	// sector used to get floor and ceiling height
+		if (floordrop) sec->Flags = SECF_FLOORDROP;
 		// killough 3/7/98: end changes
 
 		sec->gravity = 1.f;	// [RH] Default sector gravity of 1.0
@@ -437,49 +683,50 @@ struct UDMFParser
 		sec->friction = ORIG_FRICTION;
 		sec->movefactor = ORIG_FRICTION_FACTOR;
 
-		sc.MustGetStringName("{");
-		while (!sc.CheckString("}"))
+		sc.MustGetToken('{');
+		while (!sc.CheckToken('}'))
 		{
-			sc.MustGetString();
-			FName key = sc.String;
-			sc.MustGetStringName("=");
-			sc.MustGetString();
-			FString value = sc.String;
-			sc.MustGetStringName(";");
+			FName key = ParseKey();
 			switch(key)
 			{
 			case NAME_Heightfloor:
-				sec->floortexz = strtol(value, NULL, 0) << FRACBITS;
+				sec->floortexz = CheckInt(key) << FRACBITS;
 				break;
 
 			case NAME_Heightceiling:
-				sec->ceilingtexz = strtol(value, NULL, 0) << FRACBITS;
+				sec->ceilingtexz = CheckInt(key) << FRACBITS;
 				break;
 
 			case NAME_Texturefloor:
-				sec->floorpic = TexMan.GetTexture (value, FTexture::TEX_Flat, FTextureManager::TEXMAN_Overridable);
+				sec->floorpic = TexMan.GetTexture (CheckString(key), FTexture::TEX_Flat, FTextureManager::TEXMAN_Overridable);
 				break;
 
 			case NAME_Textureceiling:
-				sec->ceilingpic = TexMan.GetTexture (value, FTexture::TEX_Flat, FTextureManager::TEXMAN_Overridable);
+				sec->ceilingpic = TexMan.GetTexture (CheckString(key), FTexture::TEX_Flat, FTextureManager::TEXMAN_Overridable);
 				break;
 
 			case NAME_Lightlevel:
-				sec->lightlevel = (BYTE)clamp<int>(strtol(value, NULL, 0), 0, 255);
+				sec->lightlevel = (BYTE)clamp<int>(CheckInt(key), 0, 255);
 				break;
 
 			case NAME_Special:
-				sec->special = (short)strtol(value, NULL, 0);
+				sec->special = (short)CheckInt(key);
 				if (isTranslated) sec->special = P_TranslateSectorSpecial(sec->special);
+				else if (namespc == NAME_Hexen)
+				{
+					if (sec->special < 0 || sec->special > 255 || !HexenSectorSpecialOk[sec->special])
+						sec->special = 0;	// NULL all unknown specials
+				}
 				break;
 
-			case NAME_Tag:
-				sec->tag = (short)strtol(value, NULL, 0);
+			case NAME_Id:
+				sec->tag = (short)CheckInt(key);
 				break;
 
 			default:
 				break;
 			}
+			sc.MustGetToken(';');
 		}
 
 		sec->floorplane.d = -sec->floortexz;
@@ -603,7 +850,6 @@ struct UDMFParser
 		char *buffer = new char[map->Size(ML_TEXTMAP)];
 
 		isTranslated = true;
-		isExtended = false;
 
 		map->Read(ML_TEXTMAP, buffer);
 		sc.OpenMem(Wads.GetLumpFullName(map->lumpnum), buffer, map->Size(ML_TEXTMAP));
@@ -613,34 +859,58 @@ struct UDMFParser
 			sc.MustGetStringName("=");
 			sc.MustGetString();
 			namespc = sc.String;
-			if (namespc == NAME_ZDoom)
+			switch(namespc)
 			{
+			case NAME_ZDoom:
+				namespace_bits = Zd;
 				isTranslated = false;
-				isExtended = true;
-			}
-			else if (namespc == NAME_Hexen)
-			{
+				break;
+			case NAME_ZDoomTranslated:
+				namespace_bits = Zdt;
+				break;
+			case NAME_Hexen:
+				namespace_bits = Hx;
 				isTranslated = false;
-			}
-			else if (namespc == NAME_ZDoomTranslated)
-			{
-				isExtended = true;
-			}
-			else if (namespc == NAME_Doom)
-			{
+				break;
+			case NAME_Doom:
+				namespace_bits = Dm;
 				P_LoadTranslator("xlat/doom_base.txt");
-			}
-			else if (namespc == NAME_Heretic)
-			{
+				level.flags |= LEVEL_DUMMYSWITCHES;
+				floordrop = true;
+				break;
+			case NAME_Heretic:
+				namespace_bits = Ht;
 				P_LoadTranslator("xlat/heretic_base.txt");
-			}
-			else if (namespc == NAME_Strife)
-			{
+				level.flags |= LEVEL_DUMMYSWITCHES;
+				floordrop = true;
+				break;
+			case NAME_Strife:
+				namespace_bits = St;
 				P_LoadTranslator("xlat/strife_base.txt");
-			}
-			else 
-			{
-				Printf("Unknown namespace %s\n", sc.String);
+				level.flags |= LEVEL_DUMMYSWITCHES|LEVEL_RAILINGHACK;
+				floordrop = true;
+				break;
+			default:
+				Printf("Unknown namespace %s. Using defaults for %s\n", sc.String, GameNames[gameinfo.gametype]);
+				switch (gameinfo.gametype)
+				{
+				case GAME_Doom:
+					namespace_bits = Dm;
+					P_LoadTranslator("xlat/doom_base.txt");
+					break;
+				case GAME_Heretic:
+					namespace_bits = Ht;
+					P_LoadTranslator("xlat/heretic_base.txt");
+					break;
+				case GAME_Strife:
+					namespace_bits = St;
+					P_LoadTranslator("xlat/strife_base.txt");
+					break;
+				case GAME_Hexen:
+					namespace_bits = Hx;
+					isTranslated = false;
+					break;
+				}
 			}
 			sc.MustGetStringName(";");
 		}
@@ -693,8 +963,8 @@ struct UDMFParser
 		// Create the real sectors
 		numsectors = ParsedSectors.Size();
 		sectors = new sector_t[numsectors];
-		sectors[0].e = new extsector_t[numsectors];
 		memcpy(sectors, &ParsedSectors[0], numsectors * sizeof(*sectors));
+		sectors[0].e = new extsector_t[numsectors];
 		for(int i = 0; i < numsectors; i++)
 		{
 			sectors[i].e = &sectors[0].e[i];
diff --git a/src/p_user.cpp b/src/p_user.cpp
index 634092d51..3c1d48b87 100644
--- a/src/p_user.cpp
+++ b/src/p_user.cpp
@@ -2202,7 +2202,7 @@ void P_PlayerThink (player_t *player)
 			}
 			if (!--player->morphTics)
 			{ // Attempt to undo the chicken/pig
-				P_UndoPlayerMorph (player);
+				P_UndoPlayerMorph (player, player);
 			}
 		}
 		// Cycle psprites
diff --git a/src/po_man.cpp b/src/po_man.cpp
index 8c4181823..4ac6a12c8 100644
--- a/src/po_man.cpp
+++ b/src/po_man.cpp
@@ -23,6 +23,7 @@
 #include "s_sndseq.h"
 #include "a_sharedglobal.h"
 #include "r_main.h"
+#include "p_lnspec.h"
 
 // MACROS ------------------------------------------------------------------
 
@@ -1172,8 +1173,8 @@ static void InitSegLists ()
 		if (segs[i].linedef != NULL)
 		{
 			SegListHead[segs[i].v1 - vertexes] = i;
-			if ((segs[i].linedef->special == PO_LINE_START ||
-				segs[i].linedef->special == PO_LINE_EXPLICIT))
+			if ((segs[i].linedef->special == Polyobj_StartLine ||
+				segs[i].linedef->special == Polyobj_ExplicitLine))
 			{
 				KnownPolySegs.Push (i);
 			}
@@ -1265,7 +1266,7 @@ static void SpawnPolyobj (int index, int tag, int type)
 			continue;
 		}
 		
-		if (segs[i].linedef->special == PO_LINE_START &&
+		if (segs[i].linedef->special == Polyobj_StartLine &&
 			segs[i].linedef->args[0] == tag)
 		{
 			if (polyobjs[index].segs)
@@ -1306,7 +1307,7 @@ static void SpawnPolyobj (int index, int tag, int type)
 				i = KnownPolySegs[ii];
 
 				if (i >= 0 &&
-					segs[i].linedef->special == PO_LINE_EXPLICIT &&
+					segs[i].linedef->special == Polyobj_ExplicitLine &&
 					segs[i].linedef->args[0] == tag)
 				{
 					if (!segs[i].linedef->args[1])
@@ -1327,7 +1328,7 @@ static void SpawnPolyobj (int index, int tag, int type)
 			{
 				i = KnownPolySegs[ii];
 				if (i >= 0 &&
-					segs[i].linedef->special == PO_LINE_EXPLICIT &&
+					segs[i].linedef->special == Polyobj_ExplicitLine &&
 					segs[i].linedef->args[0] == tag && segs[i].linedef->args[1] == j)
 				{
 					segs[i].linedef->special = 0;
@@ -1344,7 +1345,7 @@ static void SpawnPolyobj (int index, int tag, int type)
 				{
 					i = KnownPolySegs[ii];
 					if (i >= 0 &&
-						segs[i].linedef->special == PO_LINE_EXPLICIT &&
+						segs[i].linedef->special == Polyobj_ExplicitLine &&
 						segs[i].linedef->args[0] == tag)
 					{
 						I_Error ("SpawnPolyobj: Missing explicit line %d for poly %d\n",
diff --git a/src/r_defs.h b/src/r_defs.h
index 7e61c1c33..01df40b65 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -256,6 +256,7 @@ enum
 {
 	SECF_SILENT			= 1,	// actors in sector make no noise
 	SECF_NOFALLINGDAMAGE= 2,	// No falling damage in this sector
+	SECF_FLOORDROP		= 4,	// all actors standing on this floor will remain on it when it lowers very fast.
 };
 
 struct FDynamicColormap;
diff --git a/src/sc_man.cpp b/src/sc_man.cpp
index 7c590abc0..590cc26b3 100644
--- a/src/sc_man.cpp
+++ b/src/sc_man.cpp
@@ -982,6 +982,32 @@ void STACK_ARGS FScanner::ScriptError (const char *message, ...)
 		AlreadyGot? AlreadyGotLine : Line, composed.GetChars());
 }
 
+//==========================================================================
+//
+// FScanner::ScriptError
+//
+//==========================================================================
+
+void STACK_ARGS FScanner::ScriptMessage (const char *message, ...)
+{
+	FString composed;
+
+	if (message == NULL)
+	{
+		composed = "Bad syntax.";
+	}
+	else
+	{
+		va_list arglist;
+		va_start (arglist, message);
+		composed.VFormat (message, arglist);
+		va_end (arglist);
+	}
+
+	Printf ("Script error, \"%s\" line %d:\n%s\n", ScriptName.GetChars(),
+		AlreadyGot? AlreadyGotLine : Line, composed.GetChars());
+}
+
 //==========================================================================
 //
 // FScanner :: CheckOpen
diff --git a/src/sc_man.h b/src/sc_man.h
index cbfc00096..a7ed75f02 100644
--- a/src/sc_man.h
+++ b/src/sc_man.h
@@ -58,6 +58,7 @@ public:
 	int MustMatchString(const char **strings);
 
 	void ScriptError(const char *message, ...);
+	void ScriptMessage(const char *message, ...);
 
 	// Members ------------------------------------------------------
 	char *String;
diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp
index 255e71dc6..16cc6ecc5 100644
--- a/src/textures/texturemanager.cpp
+++ b/src/textures/texturemanager.cpp
@@ -147,7 +147,7 @@ int FTextureManager::CheckForTexture (const char *name, int usetype, BITFIELD fl
 	if ((flags & TEXMAN_TryAny) && usetype != FTexture::TEX_Any)
 	{
 		// Never return the index of NULL textures.
-		if (firsttype == FTexture::TEX_Null) return 0;
+		if (firstfound != -1 && firsttype == FTexture::TEX_Null) return 0;
 		return firstfound;
 	}
 
diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp
index f3c5c2fc7..3bd5e2071 100644
--- a/src/thingdef/thingdef_properties.cpp
+++ b/src/thingdef/thingdef_properties.cpp
@@ -756,11 +756,13 @@ static int ParseMorphStyle (FScanner &sc)
 {
  	static const char * morphstyles[]={
 		"MRF_ADDSTAMINA", "MRF_FULLHEALTH", "MRF_UNDOBYTOMEOFPOWER", "MRF_UNDOBYCHAOSDEVICE",
-		"MRF_FAILNOTELEFRAG", "MRF_FAILNOLAUGH", "MRF_WHENINVULNERABLE", "MRF_LOSEACTUALWEAPON", NULL};
- 
+		"MRF_FAILNOTELEFRAG", "MRF_FAILNOLAUGH", "MRF_WHENINVULNERABLE", "MRF_LOSEACTUALWEAPON",
+		"MRF_NEWTIDBEHAVIOUR", "MRF_UNDOBYDEATH", "MRF_UNDOBYDEATHFORCED", "MRF_UNDOBYDEATHSAVES", NULL};
+
  	static const int morphstyle_values[]={
 		MORPH_ADDSTAMINA, MORPH_FULLHEALTH, MORPH_UNDOBYTOMEOFPOWER, MORPH_UNDOBYCHAOSDEVICE,
-		MORPH_FAILNOTELEFRAG, MORPH_FAILNOLAUGH, MORPH_WHENINVULNERABLE, MORPH_LOSEACTUALWEAPON};
+		MORPH_FAILNOTELEFRAG, MORPH_FAILNOLAUGH, MORPH_WHENINVULNERABLE, MORPH_LOSEACTUALWEAPON,
+		MORPH_NEWTIDBEHAVIOUR, MORPH_UNDOBYDEATH, MORPH_UNDOBYDEATHFORCED, MORPH_UNDOBYDEATHSAVES};
 
 	// May be given flags by number...
 	if (sc.CheckNumber())
diff --git a/src/xlat/parse_xlat.cpp b/src/xlat/parse_xlat.cpp
index 904b1727c..c1c71a28b 100644
--- a/src/xlat/parse_xlat.cpp
+++ b/src/xlat/parse_xlat.cpp
@@ -115,13 +115,13 @@ struct XlatParseContext : public FParseContext
 		{
 			"arg2", "arg3", "arg4", "arg5", "bitmask", "clear",
 			"define", "enum", "flags", "include", "lineid", 
-			"nobitmask", "sector", "tag", "maxlinespecial"
+			"maxlinespecial", "nobitmask", "sector", "tag"
 		};
 		static const short types[] =
 		{
 			XLAT_ARG2, XLAT_ARG3, XLAT_ARG4, XLAT_ARG5, XLAT_BITMASK, XLAT_CLEAR,
 			XLAT_DEFINE, XLAT_ENUM, XLAT_FLAGS, XLAT_INCLUDE, XLAT_TAG,
-			XLAT_NOBITMASK, XLAT_SECTOR, XLAT_TAG, XLAT_MAXLINESPECIAL
+			XLAT_MAXLINESPECIAL, XLAT_NOBITMASK, XLAT_SECTOR, XLAT_TAG
 		};
 
 		int min = 0, max = countof(tokens) - 1;