From 3c9b55d1db437404752bea2be3fde461ce557861 Mon Sep 17 00:00:00 2001
From: Randy Heit <rheit@zdoom.fake>
Date: Sat, 6 May 2006 03:25:12 +0000
Subject: [PATCH] SVN r81 (trunk)

---
 docs/rh-log.txt                     |   8 +
 src/g_doom/a_cacodemon.cpp          |  32 ----
 src/g_doom/a_demon.cpp              |  32 ----
 src/g_doom/a_doomimp.cpp            |  33 ----
 src/g_doom/a_doomplayer.cpp         |  42 -----
 src/g_doom/a_lostsoul.cpp           |  40 ----
 src/g_doom/a_possessed.cpp          |  70 -------
 src/info.cpp                        |  88 ++++++---
 src/info.h                          |   3 +
 src/r_bsp.cpp                       |   2 +-
 src/thingdef.cpp                    | 271 +++++++++++++++++++++-------
 wadsrc/decorate/decorate.txt        |   1 +
 wadsrc/decorate/doom/deadthings.txt | 123 +++++++++++++
 wadsrc/wadsrc.vcproj                |   3 +
 wadsrc/zdoom.lst                    |   1 +
 15 files changed, 400 insertions(+), 349 deletions(-)
 create mode 100644 wadsrc/decorate/doom/deadthings.txt

diff --git a/docs/rh-log.txt b/docs/rh-log.txt
index f212cccb1..3ef2785a4 100644
--- a/docs/rh-log.txt
+++ b/docs/rh-log.txt
@@ -1,6 +1,14 @@
 May 5, 2006
+- Added "DropItem None" as a way for inherited actors to avoid dropping items if
+  their superclass normally does. This is only needed if somebody dehacks
+  DeadZombieMan or DeadShotgunGuy.
+- Moved Doom's dead body actors into DECORATE so that I can finish and verify the
+  implementation of "States { StateLabel: goto SomeOtherLabel }" constructs. The
+  alternate method of overriding inherited states is now deprecated, since it's
+  incompatible with the coming custom state labels.
 - Changed TAG_MORE to pass a va_list pointer instead of a va_list because it's a
   non-POD type when targeting several non-ix86 targets with GCC. Hopefully this works.
+  (Guess it didn't. Boohoo.)
 
 May 4, 2006
 - Rewrote FName to use only POD types for its static data so that it can be used
diff --git a/src/g_doom/a_cacodemon.cpp b/src/g_doom/a_cacodemon.cpp
index 31fb63d05..29367b965 100644
--- a/src/g_doom/a_cacodemon.cpp
+++ b/src/g_doom/a_cacodemon.cpp
@@ -149,35 +149,3 @@ void A_HeadAttack (AActor *self)
 	// launch a missile
 	P_SpawnMissile (self, self->target, RUNTIME_CLASS(ACacodemonBall));
 }
-
-// Dead cacodemon ----------------------------------------------------------
-
-class ADeadCacodemon : public ACacodemon
-{
-	DECLARE_STATELESS_ACTOR (ADeadCacodemon, ACacodemon)
-};
-
-IMPLEMENT_STATELESS_ACTOR (ADeadCacodemon, Doom, 22, 0)
-	PROP_SpawnState (S_HEAD_DIE+5)
-
-	// Undo all the changes to default Actor properties that ACacodemon made
-	PROP_SpawnHealth (1000)
-	PROP_RadiusFixed (20)
-	PROP_HeightFixed (16)
-	PROP_Mass (100)
-	PROP_SpeedFixed (0)
-	PROP_PainChance (0)
-	PROP_Flags (0)
-	PROP_Flags2 (0)
-	PROP_Flags3 (0)
-	PROP_SeeState (255)
-	PROP_PainState (255)
-	PROP_MissileState (255)
-	PROP_DeathState (255)
-	PROP_RaiseState (255)
-	PROP_SeeSound ("")
-	PROP_PainSound ("")
-	PROP_DeathSound ("")
-	PROP_ActiveSound ("")
-	PROP_AttackSound ("")
-END_DEFAULTS
diff --git a/src/g_doom/a_demon.cpp b/src/g_doom/a_demon.cpp
index b7035ffe4..b45dc0062 100644
--- a/src/g_doom/a_demon.cpp
+++ b/src/g_doom/a_demon.cpp
@@ -130,35 +130,3 @@ void A_SargAttack (AActor *self)
 		P_TraceBleed (damage, self->target, self);
 	}
 }
-
-// Dead demon --------------------------------------------------------------
-
-class ADeadDemon : public ADemon
-{
-	DECLARE_STATELESS_ACTOR (ADeadDemon, ADemon)
-};
-
-IMPLEMENT_STATELESS_ACTOR (ADeadDemon, Doom, 21, 0)
-	PROP_SpawnState (S_SARG_DIE+5)
-
-	// Undo all the changes to default Actor properties that ADemon made
-	PROP_SpawnHealth (1000)
-	PROP_RadiusFixed (20)
-	PROP_HeightFixed (16)
-	PROP_Mass (100)
-	PROP_SpeedFixed (0)
-	PROP_PainChance (0)
-	PROP_Flags (0)
-	PROP_Flags2 (0)
-	PROP_Flags3 (0)
-	PROP_SeeState (255)
-	PROP_PainState (255)
-	PROP_MissileState (255)
-	PROP_DeathState (255)
-	PROP_RaiseState (255)
-	PROP_SeeSound ("")
-	PROP_PainSound ("")
-	PROP_DeathSound ("")
-	PROP_ActiveSound ("")
-	PROP_AttackSound ("")
-END_DEFAULTS
diff --git a/src/g_doom/a_doomimp.cpp b/src/g_doom/a_doomimp.cpp
index 21b34bbb5..530bf927a 100644
--- a/src/g_doom/a_doomimp.cpp
+++ b/src/g_doom/a_doomimp.cpp
@@ -168,36 +168,3 @@ void A_TroopAttack (AActor *self)
 	// launch a missile
 	P_SpawnMissile (self, self->target, RUNTIME_CLASS(ADoomImpBall));
 }
-
-// Dead imp ----------------------------------------------------------------
-
-class ADeadDoomImp : public ADoomImp
-{
-	DECLARE_STATELESS_ACTOR (ADeadDoomImp, ADoomImp)
-};
-
-IMPLEMENT_STATELESS_ACTOR (ADeadDoomImp, Doom, 20, 0)
-	PROP_SpawnState (S_TROO_DIE+4)
-
-	// Undo all the changes to default Actor properties that ADoomImp made
-	PROP_SpawnHealth (1000)
-	PROP_RadiusFixed (20)
-	PROP_HeightFixed (16)
-	PROP_Mass (100)
-	PROP_SpeedFixed (0)
-	PROP_PainChance (0)
-	PROP_Flags (0)
-	PROP_Flags2 (0)
-	PROP_Flags3 (0)
-	PROP_SeeState (255)
-	PROP_PainState (255)
-	PROP_MissileState (255)
-	PROP_DeathState (255)
-	PROP_XDeathState (255)
-	PROP_RaiseState (255)
-	PROP_SeeSound ("")
-	PROP_PainSound ("")
-	PROP_DeathSound ("")
-	PROP_ActiveSound ("")
-	PROP_AttackSound ("")
-END_DEFAULTS
diff --git a/src/g_doom/a_doomplayer.cpp b/src/g_doom/a_doomplayer.cpp
index 1bc256141..e58acfa16 100644
--- a/src/g_doom/a_doomplayer.cpp
+++ b/src/g_doom/a_doomplayer.cpp
@@ -207,45 +207,3 @@ void A_DoomSkinCheck2 (AActor *actor)
 		actor->SetState (&ADoomPlayer::States[S_HTIC_XDIE]);
 	}
 }
-
-// Dead marine -------------------------------------------------------------
-
-class ADeadMarine : public AActor
-{
-	DECLARE_ACTOR (ADeadMarine, AActor)
-};
-
-FState ADeadMarine::States[] =
-{
-	S_NORMAL (PLAY, 'N',   -1, NULL							, NULL)
-};
-
-IMPLEMENT_ACTOR (ADeadMarine, Doom, 15, 0)
-	PROP_SpawnState (0)
-END_DEFAULTS
-
-// Gibbed marine -----------------------------------------------------------
-
-class AGibbedMarine : public AActor
-{
-	DECLARE_ACTOR (AGibbedMarine, AActor)
-};
-
-FState AGibbedMarine::States[] =
-{
-	S_NORMAL (PLAY, 'W',   -1, NULL							, NULL)
-};
-
-IMPLEMENT_ACTOR (AGibbedMarine, Doom, 10, 145)
-	PROP_SpawnState (0)
-END_DEFAULTS
-
-// Gibbed marine (extra copy) ----------------------------------------------
-
-class AGibbedMarineExtra : public AGibbedMarine
-{
-	DECLARE_STATELESS_ACTOR (AGibbedMarineExtra, AGibbedMarine)
-};
-
-IMPLEMENT_STATELESS_ACTOR (AGibbedMarineExtra, Doom, 12, 0)
-END_DEFAULTS
diff --git a/src/g_doom/a_lostsoul.cpp b/src/g_doom/a_lostsoul.cpp
index 95fb595e8..dce9d8cbe 100644
--- a/src/g_doom/a_lostsoul.cpp
+++ b/src/g_doom/a_lostsoul.cpp
@@ -104,46 +104,6 @@ void A_SkullAttack (AActor *self)
 	self->momz = (dest->z+(dest->height>>1) - self->z) / dist;
 }
 
-// Dead lost soul ----------------------------------------------------------
-
-/* [RH] Considering that the lost soul removes itself when it dies, there
- * really wasn't much point in id including this thing, but they did anyway.
- * (There was probably a time when it stayed around after death, and this is
- * a holdover from that.)
- */
-
-class ADeadLostSoul : public ALostSoul
-{
-	DECLARE_STATELESS_ACTOR (ADeadLostSoul, ALostSoul)
-};
-
-IMPLEMENT_STATELESS_ACTOR (ADeadLostSoul, Doom, 23, 0)
-	PROP_SpawnState (S_SKULL_DIE+5)
-
-	// Undo all the changes to default Actor properties that ALostSoul made
-	PROP_SpawnHealth (1000)
-	PROP_RadiusFixed (20)
-	PROP_HeightFixed (16)
-	PROP_Mass (100)
-	PROP_SpeedFixed (0)
-	PROP_Damage (0)
-	PROP_PainChance (0)
-	PROP_Flags (0)
-	PROP_Flags2 (0)
-	PROP_Flags3 (0)
-	PROP_Flags4 (0)
-	PROP_RenderStyle (STYLE_Normal)
-	PROP_SeeState (255)
-	PROP_PainState (255)
-	PROP_MissileState (255)
-	PROP_DeathState (255)
-	PROP_RaiseState (255)
-	PROP_PainSound ("")
-	PROP_DeathSound ("")
-	PROP_ActiveSound ("")
-	PROP_AttackSound ("")
-END_DEFAULTS
-
 //==========================================================================
 //
 // CVAR transsouls
diff --git a/src/g_doom/a_possessed.cpp b/src/g_doom/a_possessed.cpp
index 94bea8993..4a9ac9f2e 100644
--- a/src/g_doom/a_possessed.cpp
+++ b/src/g_doom/a_possessed.cpp
@@ -143,41 +143,6 @@ void A_PosAttack (AActor *self)
 	P_LineAttack (self, angle, MISSILERANGE, slope, damage, MOD_UNKNOWN, RUNTIME_CLASS(ABulletPuff));
 }
 
-// Dead zombie man ---------------------------------------------------------
-
-class ADeadZombieMan : public AZombieMan
-{
-	DECLARE_STATELESS_ACTOR (ADeadZombieMan, AZombieMan)
-public:
-	void NoBlockingSet () {}
-};
-
-IMPLEMENT_STATELESS_ACTOR (ADeadZombieMan, Doom, 18, 0)
-	PROP_SpawnState (S_POSS_DIE+4)
-
-	// Undo all the changes to default Actor properties that AZombieMan made
-	PROP_SpawnHealth (1000)
-	PROP_RadiusFixed (20)
-	PROP_HeightFixed (16)
-	PROP_Mass (100)
-	PROP_SpeedFixed (0)
-	PROP_PainChance (0)
-	PROP_Flags (0)
-	PROP_Flags2 (0)
-	PROP_Flags3 (0)
-	PROP_SeeState (255)
-	PROP_PainState (255)
-	PROP_MissileState (255)
-	PROP_DeathState (255)
-	PROP_XDeathState (255)
-	PROP_RaiseState (255)
-	PROP_SeeSound ("")
-	PROP_PainSound ("")
-	PROP_DeathSound ("")
-	PROP_ActiveSound ("")
-	PROP_AttackSound ("")
-END_DEFAULTS
-
 // Shotgun guy -------------------------------------------------------------
 
 class AShotgunGuy : public AActor
@@ -321,41 +286,6 @@ void A_SPosAttack (AActor *self)
 	A_SPosAttack2 (self);
 }
 
-// Dead shotgun guy --------------------------------------------------------
-
-class ADeadShotgunGuy : public AShotgunGuy
-{
-	DECLARE_STATELESS_ACTOR (ADeadShotgunGuy, AShotgunGuy)
-public:
-	void NoBlockingSet () {}
-};
-
-IMPLEMENT_STATELESS_ACTOR (ADeadShotgunGuy, Doom, 19, 0)
-	PROP_SpawnState (S_SPOS_DIE+4)
-
-	// Undo all the changes to default Actor properties that AShotgunGuy made
-	PROP_SpawnHealth (1000)
-	PROP_RadiusFixed (20)
-	PROP_HeightFixed (16)
-	PROP_Mass (100)
-	PROP_SpeedFixed (0)
-	PROP_PainChance (0)
-	PROP_Flags (0)
-	PROP_Flags2 (0)
-	PROP_Flags3 (0)
-	PROP_SeeState (255)
-	PROP_PainState (255)
-	PROP_MissileState (255)
-	PROP_DeathState (255)
-	PROP_XDeathState (255)
-	PROP_RaiseState (255)
-	PROP_SeeSound ("")
-	PROP_PainSound ("")
-	PROP_DeathSound ("")
-	PROP_ActiveSound ("")
-	PROP_AttackSound ("")
-END_DEFAULTS
-
 // Chaingun guy ------------------------------------------------------------
 
 class AChaingunGuy : public AActor
diff --git a/src/info.cpp b/src/info.cpp
index 3cd092e84..eadb3df45 100644
--- a/src/info.cpp
+++ b/src/info.cpp
@@ -66,6 +66,8 @@ extern void LoadDecorations (void (*process)(FState *, int));
 
 FArchive &operator<< (FArchive &arc, FState *&state)
 {
+	const TypeInfo *info;
+
 	if (arc.IsStoring ())
 	{
 		if (state == NULL)
@@ -75,41 +77,17 @@ FArchive &operator<< (FArchive &arc, FState *&state)
 			return arc;
 		}
 
-		FActorInfo *info = RUNTIME_CLASS(AActor)->ActorInfo;
+		info = FState::StaticFindStateOwner (state);
 
-		if (state >= info->OwnedStates &&
-			state < info->OwnedStates + info->NumOwnedStates)
+		if (info != NULL)
 		{
-			arc.UserWriteClass (RUNTIME_CLASS(AActor));
-			arc.WriteCount ((DWORD)(state - info->OwnedStates));
-			return arc;
+			arc.UserWriteClass (info);
+			arc.WriteCount ((DWORD)(state - info->ActorInfo->OwnedStates));
 		}
-
-		TAutoSegIterator<FActorInfo *, &ARegHead, &ARegTail> reg;
-		while (++reg != NULL)
+		else
 		{
-			if (state >= reg->OwnedStates &&
-				state <  reg->OwnedStates + reg->NumOwnedStates)
-			{
-				arc.UserWriteClass (reg->Class);
-				arc.WriteCount ((DWORD)(state - reg->OwnedStates));
-				return arc;
-			}
+			I_Error ("Cannot find owner for state %p\n", state);
 		}
-
-		for (unsigned int i = 0; i < TypeInfo::m_RuntimeActors.Size(); ++i)
-		{
-			FActorInfo *info = TypeInfo::m_RuntimeActors[i]->ActorInfo;
-			if (state >= info->OwnedStates &&
-				state <  info->OwnedStates + info->NumOwnedStates)
-			{
-				arc.UserWriteClass (info->Class);
-				arc.WriteCount ((DWORD)(state - info->OwnedStates));
-				return arc;
-			}
-		}
-
-		I_Error ("Cannot find owner for state %p\n", state);
 	}
 	else
 	{
@@ -134,6 +112,56 @@ FArchive &operator<< (FArchive &arc, FState *&state)
 	return arc;
 }
 
+// Find the actor that a state belongs to.
+const TypeInfo *FState::StaticFindStateOwner (const FState *state)
+{
+	const FActorInfo *info = RUNTIME_CLASS(AActor)->ActorInfo;
+
+	if (state >= info->OwnedStates &&
+		state < info->OwnedStates + info->NumOwnedStates)
+	{
+		return RUNTIME_CLASS(AActor);
+	}
+
+	TAutoSegIterator<FActorInfo *, &ARegHead, &ARegTail> reg;
+	while (++reg != NULL)
+	{
+		if (state >= reg->OwnedStates &&
+			state <  reg->OwnedStates + reg->NumOwnedStates)
+		{
+			return reg->Class;
+		}
+	}
+
+	for (unsigned int i = 0; i < TypeInfo::m_RuntimeActors.Size(); ++i)
+	{
+		info = TypeInfo::m_RuntimeActors[i]->ActorInfo;
+		if (state >= info->OwnedStates &&
+			state <  info->OwnedStates + info->NumOwnedStates)
+		{
+			return info->Class;
+		}
+	}
+
+	return NULL;
+}
+
+// Find the actor that a state belongs to, but restrict the search to
+// the specified type and its ancestors.
+const TypeInfo *FState::StaticFindStateOwner (const FState *state, const FActorInfo *info)
+{
+	while (info != NULL)
+	{
+		if (state >= info->OwnedStates &&
+			state <  info->OwnedStates + info->NumOwnedStates)
+		{
+			return info->Class;
+		}
+		info = info->Class->ParentType->ActorInfo;
+	}
+	return NULL;
+}
+
 // Change sprite names to indices
 static void ProcessStates (FState *states, int numstates)
 {
diff --git a/src/info.h b/src/info.h
index 02cb5a3f0..bc8638df0 100644
--- a/src/info.h
+++ b/src/info.h
@@ -161,6 +161,9 @@ struct FState
 	{
 		Frame = (Frame & (SF_FULLBRIGHT|SF_BIGTIC)) | (frame-'A');
 	}
+
+	static const TypeInfo *StaticFindStateOwner (const FState *state);
+	static const TypeInfo *StaticFindStateOwner (const FState *state, const FActorInfo *info);
 };
 
 // A truly awful hack to get to the state that called an action function
diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp
index 8bfc1782b..5a94b2696 100644
--- a/src/r_bsp.cpp
+++ b/src/r_bsp.cpp
@@ -78,7 +78,7 @@ fixed_t			rw_frontcz1, rw_frontcz2;
 fixed_t			rw_frontfz1, rw_frontfz2;
 
 
-unsigned int	MaxDrawSegs;
+size_t			MaxDrawSegs;
 drawseg_t		*drawsegs;
 drawseg_t*		firstdrawseg;
 drawseg_t*		ds_p;
diff --git a/src/thingdef.cpp b/src/thingdef.cpp
index 3734831c5..65d50fbc4 100644
--- a/src/thingdef.cpp
+++ b/src/thingdef.cpp
@@ -65,6 +65,7 @@
 #include "a_hexenglobal.h"
 #include "a_weaponpiece.h"
 #include "p_conversation.h"
+#include "v_text.h"
 #include "thingdef.h"
 
 
@@ -74,7 +75,7 @@ extern TArray<FActorInfo *> Decorations;
 // allow decal specifications in DECORATE. Decals are loaded after DECORATE so the names must be stored here.
 TArray<char*> DecalNames;
 // all state parameters
-TArray<int> StateParameters;
+TArray<intptr_t> StateParameters;
 
 //==========================================================================
 //
@@ -989,8 +990,11 @@ void A_NoBlocking (AActor *actor)
 
 		while (di != NULL)
 		{
-			const TypeInfo *ti = TypeInfo::FindType(di->Name);
-			if (ti) P_DropItem (actor, ti, di->amount, di->probability);
+			if (stricmp (di->Name, "None") != 0)
+			{
+				const TypeInfo *ti = TypeInfo::FindType(di->Name);
+				if (ti) P_DropItem (actor, ti, di->amount, di->probability);
+			}
 			di = di->Next;
 		}
 	}
@@ -1422,7 +1426,7 @@ static int ProcessStates(FActorInfo * actor, AActor * defaults, Baggage &bag)
 	FState * laststate = NULL;
 	intptr_t lastlabel = -1;
 	FState ** stp;
-	int minrequiredstate = 0;
+	int minrequiredstate = -1;
 
 	statestring[255] = 0;
 
@@ -1433,8 +1437,14 @@ static int ProcessStates(FActorInfo * actor, AActor * defaults, Baggage &bag)
 		SC_MustGetString();
 		if (SC_Compare("GOTO"))
 		{
-			SC_MustGetString();
+do_goto:	SC_MustGetString();
 			strncpy (statestring, sc_String, 255);
+			if (SC_CheckString ("."))
+			{
+				SC_MustGetString ();
+				strcat (statestring, ".");
+				strcat (statestring, sc_String);
+			}
 			if (SC_CheckString ("+"))
 			{
 				SC_MustGetNumber ();
@@ -1493,6 +1503,7 @@ static int ProcessStates(FActorInfo * actor, AActor * defaults, Baggage &bag)
 
 			if (SC_Compare (":"))
 			{
+				laststate = NULL;
 				do
 				{
 					lastlabel = count;
@@ -1501,10 +1512,13 @@ static int ProcessStates(FActorInfo * actor, AActor * defaults, Baggage &bag)
 					else 
 						SC_ScriptError("Unknown state label %s", statestring);
 					SC_MustGetString ();
+					if (SC_Compare ("Goto"))
+					{
+						goto do_goto;
+					}
 					strncpy(statestring, sc_String, 255);
 					SC_MustGetString ();
 				} while (SC_Compare (":"));
-				laststate = NULL;
 //				continue;
 			}
 
@@ -1745,6 +1759,88 @@ endofstate:
 	return count;
 }
 
+//==========================================================================
+//
+// ResolveGotoLabel
+//
+//==========================================================================
+
+static FState *ResolveGotoLabel (AActor *actor, const TypeInfo *type, char *name)
+{
+	FState **stp, *state;
+	char *namestart = name;
+	char *label, *offset, *pt;
+	int v;
+
+	// Check for classname
+	if ((pt = strchr (name, '.')) != NULL)
+	{
+		const char *classname = name;
+		*pt = '\0';
+		name = pt + 1;
+
+		// The classname may either be "Super" to identify this class's immediate
+		// superclass, or it may the name of any class that this one derives from.
+		if (stricmp (classname, "Super") == 0)
+		{
+			type = type->ParentType;
+			actor = GetDefaultByType (type);
+		}
+		else
+		{
+			const TypeInfo *stype = TypeInfo::IFindType (classname);
+			if (stype == NULL)
+			{
+				SC_ScriptError ("%s is an unknown class.", classname);
+			}
+			if (!stype->IsDescendantOf (RUNTIME_CLASS(AActor)))
+			{
+				SC_ScriptError ("%s is not an actor class, so it has no states.", stype->Name+1);
+			}
+			if (!stype->IsAncestorOf (type))
+			{
+				SC_ScriptError ("%s is not derived from %s so cannot access its states.",
+					type->Name+1, stype->Name+1);
+			}
+			if (type != stype)
+			{
+				type = stype;
+				actor = GetDefaultByType (type);
+			}
+		}
+	}
+	label = name;
+	// Check for offset
+	offset = NULL;
+	if ((pt = strchr (name, '+')) != NULL)
+	{
+		*pt = '\0';
+		offset = pt + 1;
+	}
+	v = offset ? strtol (offset, NULL, 0) : 0;
+
+	// Calculate the state's address.
+	stp = FindState (actor, type, label);
+	state = NULL;
+	if (stp != NULL)
+	{
+		if (*stp != NULL)
+		{
+			state = *stp + v;
+		}
+		else if (v != 0)
+		{
+			SC_ScriptError ("Attempt to get invalid state from actor %s.", type->Name+1);
+		}
+	}
+	else
+	{
+		SC_ScriptError("Unknown state label %s", label);
+	}
+	free(namestart);		// free the allocated string buffer
+	return state;
+}
+
 //==========================================================================
 //
 // FixStatePointers
@@ -1776,14 +1872,15 @@ static void FixStatePointers (FActorInfo *actor, FState **start, FState **stop)
 //
 //==========================================================================
 
-static void FixStatePointersAgain (FActorInfo *actor, FState **start, FState **stop)
+static void FixStatePointersAgain (FActorInfo *actor, AActor *defaults, FState **start, FState **stop)
 {
 	FState **stp;
 
 	for (stp = start; stp <= stop; ++stp)
 	{
-		if (*stp != NULL && (*stp < actor->OwnedStates || *stp >= actor->OwnedStates + actor->NumOwnedStates))
-		{ // It doesn't point into this actor's own states, so it must be a label string. Resolve it.
+		if (*stp != NULL && FState::StaticFindStateOwner (*stp, actor) == NULL)
+		{ // It's not a valid state, so it must be a label string. Resolve it.
+			*stp = ResolveGotoLabel (defaults, actor->Class, (char *)*stp);
 		}
 	}
 }
@@ -1796,82 +1893,79 @@ static void FixStatePointersAgain (FActorInfo *actor, FState **start, FState **s
 //==========================================================================
 static int FinishStates (FActorInfo *actor, AActor *defaults, Baggage &bag)
 {
-	FState **stp;
 	int count = StateArray.Size();
-	FState * realstates=new FState[count];
+	FState *realstates = new FState[count];
 	int i;
 	int currange;
 
-	memcpy(realstates,&StateArray[0],count*sizeof(FState));
-	actor->OwnedStates=realstates;
-	actor->NumOwnedStates=count;
-
-	// adjust the state pointers
-	// In the case new states are added these must be adjusted, too!
-	FixStatePointers (actor, &defaults->SpawnState, &defaults->GreetingsState);
-	if (bag.Info->Class->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
+	if (count > 0)
 	{
-		AWeapon * weapon=(AWeapon*)defaults;
+		memcpy(realstates, &StateArray[0], count*sizeof(FState));
+		actor->OwnedStates = realstates;
+		actor->NumOwnedStates = count;
 
-		FixStatePointers (actor, &weapon->UpState, &weapon->FlashState);
-	}
-	if (bag.Info->Class->IsDescendantOf(RUNTIME_CLASS(ACustomInventory)))
-	{
-		ACustomInventory * item=(ACustomInventory*)defaults;
-
-		FixStatePointers (actor, &item->UseState, &item->DropState);
-	}
-
-	for(i = currange = 0; i < count; i++)
-	{
-		// resolve labels and jumps
-		switch((ptrdiff_t)realstates[i].NextState)
+		// adjust the state pointers
+		// In the case new states are added these must be adjusted, too!
+		FixStatePointers (actor, &defaults->SpawnState, &defaults->GreetingsState);
+		if (bag.Info->Class->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
 		{
-		case 0:		// next
-			realstates[i].NextState=(i<count-1? &realstates[i+1]:&realstates[0]);
-			break;
+			AWeapon *weapon = (AWeapon*)defaults;
 
-		case -1:	// stop
-			realstates[i].NextState=NULL;
-			break;
+			FixStatePointers (actor, &weapon->UpState, &weapon->FlashState);
+		}
+		if (bag.Info->Class->IsDescendantOf(RUNTIME_CLASS(ACustomInventory)))
+		{
+			ACustomInventory *item = (ACustomInventory*)defaults;
 
-		case -2:	// wait
-			realstates[i].NextState=&realstates[i];
-			break;
+			FixStatePointers (actor, &item->UseState, &item->DropState);
+		}
 
-		default:	// loop
-			if ((size_t)realstates[i].NextState < 0x10000)
+		for(i = currange = 0; i < count; i++)
+		{
+			// resolve labels and jumps
+			switch((ptrdiff_t)realstates[i].NextState)
 			{
-				realstates[i].NextState=&realstates[(size_t)realstates[i].NextState-1];
-			}
-			else	// goto
-			{
-				char *label = strtok ((char*)realstates[i].NextState, "+");
-				char *labelpt = label;
-				char *offset = strtok (NULL, "+");
-				int v = offset ? strtol (offset, NULL, 0) : 0;
+			case 0:		// next
+				realstates[i].NextState = (i < count-1 ? &realstates[i+1] : &realstates[0]);
+				break;
 
-				stp = FindState (defaults, bag.Info->Class, label);
-				if (stp)
+			case -1:	// stop
+				realstates[i].NextState = NULL;
+				break;
+
+			case -2:	// wait
+				realstates[i].NextState = &realstates[i];
+				break;
+
+			default:	// loop
+				if ((size_t)realstates[i].NextState < 0x10000)
 				{
-					if (*stp) realstates[i].NextState=*stp+v;
-					else 
-					{
-						realstates[i].NextState=NULL;
-						if (v)
-						{
-							SC_ScriptError("Attempt to get invalid state from actor %s\n", actor->Class->Name);
-							return 0;
-						}
-					}
+					realstates[i].NextState = &realstates[(size_t)realstates[i].NextState-1];
+				}
+				else	// goto
+				{
+					realstates[i].NextState = ResolveGotoLabel (defaults, bag.Info->Class, (char *)realstates[i].NextState);
 				}
-				else 
-					SC_ScriptError("Unknown state label %s", label);
-				free(labelpt);		// free the allocated string buffer
 			}
 		}
 	}
 	StateArray.Clear ();
+
+	// Fix state pointers that are gotos
+	FixStatePointersAgain (actor, defaults, &defaults->SpawnState, &defaults->GreetingsState);
+	if (bag.Info->Class->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
+	{
+		AWeapon *weapon = (AWeapon*)defaults;
+
+		FixStatePointersAgain (actor, defaults, &weapon->UpState, &weapon->FlashState);
+	}
+	if (bag.Info->Class->IsDescendantOf(RUNTIME_CLASS(ACustomInventory)))
+	{
+		ACustomInventory *item = (ACustomInventory*)defaults;
+
+		FixStatePointersAgain (actor, defaults, &item->UseState, &item->DropState);
+	}
+
 	return count;
 }
 
@@ -2063,6 +2157,29 @@ void ProcessActor(void (*process)(FState *, int))
 	SC_SetCMode (false);
 }
 
+//==========================================================================
+//
+// StatePropertyIsDeprecated
+//
+// Deprecated means it will be removed in a future version.
+//
+//==========================================================================
+
+static void StatePropertyIsDeprecated (const char *actorname, const char *prop)
+{
+	static bool warned = false;
+
+	Printf (TEXTCOLOR_YELLOW "In actor %s, the %s property is deprecated.\n",
+		actorname, prop);
+	if (!warned)
+	{
+		warned = true;
+		Printf (TEXTCOLOR_YELLOW "Instead of \"%s <state>\", add this to the actor's States block:\n"
+				TEXTCOLOR_YELLOW "    %s:\n"
+				TEXTCOLOR_YELLOW "        Goto <state>\n", prop, prop);
+	}
+}
+
 //==========================================================================
 //
 // Property parsers
@@ -2336,6 +2453,7 @@ static void ActorDropItem (AActor *defaults, Baggage &bag)
 //==========================================================================
 static void ActorSpawnState (AActor *defaults, Baggage &bag)
 {
+	StatePropertyIsDeprecated (bag.Info->Class->Name+1, "Spawn");
 	defaults->SpawnState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
@@ -2344,6 +2462,7 @@ static void ActorSpawnState (AActor *defaults, Baggage &bag)
 //==========================================================================
 static void ActorSeeState (AActor *defaults, Baggage &bag)
 {
+	StatePropertyIsDeprecated (bag.Info->Class->Name+1, "See");
 	defaults->SeeState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
@@ -2352,6 +2471,7 @@ static void ActorSeeState (AActor *defaults, Baggage &bag)
 //==========================================================================
 static void ActorMeleeState (AActor *defaults, Baggage &bag)
 {
+	StatePropertyIsDeprecated (bag.Info->Class->Name+1, "Melee");
 	defaults->MeleeState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
@@ -2360,6 +2480,7 @@ static void ActorMeleeState (AActor *defaults, Baggage &bag)
 //==========================================================================
 static void ActorMissileState (AActor *defaults, Baggage &bag)
 {
+	StatePropertyIsDeprecated (bag.Info->Class->Name+1, "Missile");
 	defaults->MissileState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
@@ -2368,6 +2489,7 @@ static void ActorMissileState (AActor *defaults, Baggage &bag)
 //==========================================================================
 static void ActorPainState (AActor *defaults, Baggage &bag)
 {
+	StatePropertyIsDeprecated (bag.Info->Class->Name+1, "Pain");
 	defaults->PainState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
@@ -2376,6 +2498,7 @@ static void ActorPainState (AActor *defaults, Baggage &bag)
 //==========================================================================
 static void ActorDeathState (AActor *defaults, Baggage &bag)
 {
+	StatePropertyIsDeprecated (bag.Info->Class->Name+1, "Death");
 	defaults->DeathState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
@@ -2384,6 +2507,7 @@ static void ActorDeathState (AActor *defaults, Baggage &bag)
 //==========================================================================
 static void ActorXDeathState (AActor *defaults, Baggage &bag)
 {
+	StatePropertyIsDeprecated (bag.Info->Class->Name+1, "XDeath");
 	defaults->XDeathState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
@@ -2392,6 +2516,7 @@ static void ActorXDeathState (AActor *defaults, Baggage &bag)
 //==========================================================================
 static void ActorBurnState (AActor *defaults, Baggage &bag)
 {
+	StatePropertyIsDeprecated (bag.Info->Class->Name+1, "Burn");
 	defaults->BDeathState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
@@ -2400,6 +2525,7 @@ static void ActorBurnState (AActor *defaults, Baggage &bag)
 //==========================================================================
 static void ActorIceState (AActor *defaults, Baggage &bag)
 {
+	StatePropertyIsDeprecated (bag.Info->Class->Name+1, "Ice");
 	defaults->IDeathState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
@@ -2408,6 +2534,7 @@ static void ActorIceState (AActor *defaults, Baggage &bag)
 //==========================================================================
 static void ActorRaiseState (AActor *defaults, Baggage &bag)
 {
+	StatePropertyIsDeprecated (bag.Info->Class->Name+1, "Raise");
 	defaults->RaiseState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
@@ -2416,6 +2543,7 @@ static void ActorRaiseState (AActor *defaults, Baggage &bag)
 //==========================================================================
 static void ActorCrashState (AActor *defaults, Baggage &bag)
 {
+	StatePropertyIsDeprecated (bag.Info->Class->Name+1, "Crash");
 	defaults->CrashState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
@@ -2424,6 +2552,7 @@ static void ActorCrashState (AActor *defaults, Baggage &bag)
 //==========================================================================
 static void ActorCrushState (AActor *defaults, Baggage &bag)
 {
+	StatePropertyIsDeprecated (bag.Info->Class->Name+1, "Crush");
 	defaults->CrushState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
@@ -2432,6 +2561,7 @@ static void ActorCrushState (AActor *defaults, Baggage &bag)
 //==========================================================================
 static void ActorWoundState (AActor *defaults, Baggage &bag)
 {
+	StatePropertyIsDeprecated (bag.Info->Class->Name+1, "Wound");
 	defaults->WoundState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
@@ -2440,6 +2570,7 @@ static void ActorWoundState (AActor *defaults, Baggage &bag)
 //==========================================================================
 static void ActorDisintegrateState (AActor *defaults, Baggage &bag)
 {
+	StatePropertyIsDeprecated (bag.Info->Class->Name+1, "Disintegrate");
 	defaults->EDeathState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
@@ -2448,6 +2579,7 @@ static void ActorDisintegrateState (AActor *defaults, Baggage &bag)
 //==========================================================================
 static void ActorHealState (AActor *defaults, Baggage &bag)
 {
+	StatePropertyIsDeprecated (bag.Info->Class->Name+1, "Heal");
 	defaults->HealState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
@@ -2456,6 +2588,7 @@ static void ActorHealState (AActor *defaults, Baggage &bag)
 //==========================================================================
 static void ActorYesState (AActor *defaults, Baggage &bag)
 {
+	StatePropertyIsDeprecated (bag.Info->Class->Name+1, "Yes");
 	defaults->YesState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
@@ -2464,6 +2597,7 @@ static void ActorYesState (AActor *defaults, Baggage &bag)
 //==========================================================================
 static void ActorNoState (AActor *defaults, Baggage &bag)
 {
+	StatePropertyIsDeprecated (bag.Info->Class->Name+1, "No");
 	defaults->NoState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
@@ -2472,6 +2606,7 @@ static void ActorNoState (AActor *defaults, Baggage &bag)
 //==========================================================================
 static void ActorGreetingsState (AActor *defaults, Baggage &bag)
 {
+	StatePropertyIsDeprecated (bag.Info->Class->Name+1, "Greetings");
 	defaults->GreetingsState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
@@ -3492,5 +3627,3 @@ void FinishThingdef()
 		}
 	}
 }
-
-
diff --git a/wadsrc/decorate/decorate.txt b/wadsrc/decorate/decorate.txt
index a4b997a2d..4ee7eee48 100644
--- a/wadsrc/decorate/decorate.txt
+++ b/wadsrc/decorate/decorate.txt
@@ -1,6 +1,7 @@
 #include "actors/shared/debris.txt"
 #include "actors/shared/splashes.txt"
 
+#include "actors/doom/deadthings.txt"
 #include "actors/doom/doomarmor.txt"
 #include "actors/doom/doomartifacts.txt"
 #include "actors/doom/doomkeys.txt"
diff --git a/wadsrc/decorate/doom/deadthings.txt b/wadsrc/decorate/doom/deadthings.txt
new file mode 100644
index 000000000..435be5d43
--- /dev/null
+++ b/wadsrc/decorate/doom/deadthings.txt
@@ -0,0 +1,123 @@
+// Gibbed marine -----------------------------------------------------------
+
+actor GibbedMarine 10
+{
+	Game Doom
+	States
+	{
+	Spawn:
+		PLAY W -1
+		Stop
+	}
+}
+
+// Gibbed marine (extra copy) ----------------------------------------------
+
+actor GibbedMarineExtra : GibbedMarine 12
+{
+	Game Doom
+}
+
+// Dead marine -------------------------------------------------------------
+
+actor DeadMarine 15
+{
+	Game Doom
+	States
+	{
+	Spawn:
+		PLAY N -1
+		Stop
+	}
+}
+
+/* If it wasn't for Dehacked compatibility, the rest of these would be
+ * better defined as single frame states. But since Doom reused the
+ * dead state from the original monsters, we need to do the same.
+ */
+
+// Dead zombie man ---------------------------------------------------------
+
+actor DeadZombieMan : ZombieMan 18
+{
+	Game Doom
+	DropItem None
+	Skip_Super
+	States
+	{
+	Spawn:
+		Goto Super.Death+4
+	}
+}
+
+// Dead shotgun guy --------------------------------------------------------
+
+actor DeadShotgunGuy : ShotgunGuy 19
+{
+	Game Doom
+	DropItem None
+	Skip_Super
+	States
+	{
+	Spawn:
+		Goto Super.Death+4
+	}
+};
+
+// Dead imp ----------------------------------------------------------------
+
+actor DeadDoomImp : DoomImp 20
+{
+	Game Doom
+	Skip_Super
+	States
+	{
+	Spawn:
+		Goto Super.Death+4
+	}
+}
+
+// Dead demon --------------------------------------------------------------
+
+actor DeadDemon : Demon 21
+{
+	Game Doom
+	Skip_Super
+	States
+	{
+	Spawn:
+		Goto Super.Death+5
+	}
+}
+
+// Dead cacodemon ----------------------------------------------------------
+
+actor DeadCacodemon : Cacodemon 22
+{
+	Game Doom
+	Skip_Super
+	States
+	{
+	Spawn:
+		Goto Super.Death+5
+	}
+}
+
+// Dead lost soul ----------------------------------------------------------
+
+/* [RH] Considering that the lost soul removes itself when it dies, there
+ * really wasn't much point in id including this thing, but they did anyway.
+ * (There was probably a time when it stayed around after death, and this is
+ * a holdover from that.)
+ */
+
+actor DeadLostSoul : LostSoul 23
+{
+	Game Doom
+	Skip_Super
+	States
+	{
+	Spawn:
+		Goto Super.Death+5
+	}
+}
diff --git a/wadsrc/wadsrc.vcproj b/wadsrc/wadsrc.vcproj
index e5566e5aa..781e005e3 100644
--- a/wadsrc/wadsrc.vcproj
+++ b/wadsrc/wadsrc.vcproj
@@ -238,6 +238,9 @@
 				<Filter
 					Name="Doom"
 					Filter="">
+					<File
+						RelativePath=".\decorate\doom\deadthings.txt">
+					</File>
 					<File
 						RelativePath=".\decorate\doom\doomarmor.txt">
 					</File>
diff --git a/wadsrc/zdoom.lst b/wadsrc/zdoom.lst
index 8d0062f8f..8724a6d3e 100644
--- a/wadsrc/zdoom.lst
+++ b/wadsrc/zdoom.lst
@@ -187,6 +187,7 @@ decorate.txt				decorate/decorate.txt
 actors/shared/debris.txt					decorate/shared/debris.txt
 actors/shared/splashes.txt					decorate/shared/splashes.txt
 
+actors/doom/deadthings.txt					decorate/doom/deadthings.txt
 actors/doom/doomarmor.txt					decorate/doom/doomarmor.txt
 actors/doom/doomartifacts.txt				decorate/doom/doomartifacts.txt
 actors/doom/doomkeys.txt					decorate/doom/doomkeys.txt