diff -durN 96/src/actor.h 96x/src/actor.h
--- 96/src/actor.h	2004-11-24 14:12:53.000000000 +0100
+++ 96x/src/actor.h	2005-07-25 13:15:29.000000000 +0200
@@ -262,6 +262,14 @@
 	MF4_FIRERESIST		= 0x00080000,	// Actor takes half damage from fire
 	MF4_NOSPLASHALERT	= 0x00100000,	// Splashes don't alert this monster
 	MF4_SYNCHRONIZED	= 0x00200000,	// For actors spawned at load-time only: Do not randomize tics
+	MF4_NOTARGETSWITCH	= 0x00400000,	// monster never switches target until current one is dead
+	MF4_VFRICTION		= 0x00800000,	// Internal flag used by A_PainAttack to push a monster down
+	MF4_DONTHURTSPECIES	= 0x01000000,	// Don't hurt one's own kind with explosions (hitscans, too?)
+	MF4_SHIELDREFLECT	= 0x02000000,
+	MF4_DEFLECT			= 0x04000000,	// different projectile reflection styles
+	MF4_ALLOWPARTICLES	= 0x08000000,	// this puff type can be replaced by particles
+	MF4_NOEXTREMEDEATH	= 0x10000000,	// this projectile or weapon never gibs its victim
+	MF4_EXTREMEDEATH	= 0x20000000,	// this projectile or weapon always gibs its victim
 
 // --- mobj.renderflags ---
 
@@ -378,6 +386,9 @@
 	AMETA_DeathHeight,		// fixed (height on normal death)
 	AMETA_BurnHeight,		// fixed (height on burning death)
 	AMETA_StrifeName,		// string (for named Strife objects)
+	AMETA_BloodColor,		// colorized blood
+	AMETA_GibHealth,		// negative health below which this monster dies an extreme death
+	AMETA_WoundHealth,		// health needed to enter wound state
 };
 
 // Map Object definition.
@@ -572,6 +583,7 @@
 	int				special1;		// Special info
 	int				special2;		// Special info
 	int 			health;
+	int				user[8];		// 8 User variables
 	BYTE			movedir;		// 0-7
 	SBYTE			visdir;
 	SWORD			movecount;		// when 0, select a new dir
@@ -592,6 +604,7 @@
 	WORD			SpawnPoint[3]; 	// For nightmare respawn
 	WORD			SpawnAngle;
 	AActor			*tracer;		// Thing being chased/attacked for tracers
+	AActor			*master;		// Thing which spawned this one (prevents mutual attacks)
 	fixed_t			floorclip;		// value to use for floor clipping
 	SWORD			tid;			// thing identifier
 	BYTE			special;		// special
@@ -602,6 +615,7 @@
 	BYTE			waterlevel;		// 0=none, 1=feet, 2=waist, 3=eyes
 	BYTE			MinMissileChance;// [RH] If a random # is > than this, then missile attack.
 	WORD			SpawnFlags;
+	fixed_t			meleerange;
 
 	// a linked list of sectors where this object appears
 	struct msecnode_s	*touching_sectorlist;				// phares 3/14/98
@@ -642,6 +656,11 @@
 	FState *EDeathState;
 	FState *RaiseState;
 	FState *WoundState;
+	FState *HealState;
+	FState *CrushState;
+	FState *YesState;
+	FState *NoState;
+	FState *GreetingsState;
 
 	// [RH] The dialogue to show when this actor is "used."
 	FStrifeDialogueNode *Conversation;
diff -durN 96/src/c_bind.cpp 96x/src/c_bind.cpp
--- 96/src/c_bind.cpp	2004-10-20 19:14:43.000000000 +0200
+++ 96x/src/c_bind.cpp	2005-07-24 20:12:08.000000000 +0200
@@ -41,6 +41,7 @@
 #include "hu_stuff.h"
 #include "gi.h"
 #include "configfile.h"
+#include "i_system.h"
 
 #include <math.h>
 #include <stdlib.h>
@@ -164,7 +165,7 @@
 	"o",		"p",		"[",		"]",		"enter",	"ctrl",		"a",		"s",		//18
 	"d",		"f",		"g",		"h",		"j",		"k",		"l",		";",		//20
 	"'",		"`",		"shift",	"\\",		"z",		"x",		"c",		"v",		//28
-	"b",		"n",		"m",		",",		".",		"/",		NULL,		"kp*",		//30
+	"b",		"n",		"m",		",",		".",		"/",		"rshift",	"kp*",		//30
 	"alt",		"space",	"capslock",	"f1",		"f2",		"f3",		"f4",		"f5",		//38
 	"f6",		"f7",		"f8",		"f9",		"f10",		"numlock",	"scroll",	"kp7",		//40
 	"kp8",		"kp9",		"kp-",		"kp4",		"kp5",		"kp6",		"kp+",		"kp1",		//48
@@ -177,11 +178,11 @@
 	NULL,		NULL,		NULL,		NULL,		NULL,		NULL,		NULL,		NULL,		//80
 	NULL,		NULL,		NULL,		NULL,		NULL,		"kp=",		NULL,		NULL,		//88
 	"circumflex","@",		":",		"_",		"kanji",	"stop",		"ax",		"unlabeled",//90
-	NULL,		"prevtrack",NULL,		NULL,		NULL,		NULL,		NULL,		NULL,		//98
+	NULL,		"prevtrack",NULL,		NULL,		"kp-enter",	"rcontrol",	NULL,		NULL,		//98
 	"mute",		"calculator","play",	NULL,		"stop",		NULL,		NULL,		NULL,		//A0
 	NULL,		NULL,		NULL,		NULL,		NULL,		NULL,		"voldown",	NULL,		//A8
 	"volup",	NULL,		"webhome",	"kp,",		NULL,		"kp/",		NULL,		"sysrq",	//B0
-	NULL,		NULL,		NULL,		NULL,		NULL,		NULL,		NULL,		NULL,		//B8
+	"ralt",		NULL,		NULL,		NULL,		NULL,		NULL,		NULL,		NULL,		//B8
 	NULL,		NULL,		NULL,		NULL,		NULL,		NULL,		NULL,		"home",		//C0
 	"uparrow",	"pgup",		NULL,		"leftarrow",NULL,		"rightarrow",NULL,		"end",		//C8
 	"downarrow","pgdn",		"ins",		"del",		NULL,		NULL,		NULL,		NULL,		//D0
@@ -528,7 +529,8 @@
 	dclickmask = 1 << (ev->data1 & 7);
 	dclick = false;
 
-	if (DClickTime[ev->data1] > level.time && ev->type == EV_KeyDown)
+	// This used level.time which didn't work outside a level.
+	if (DClickTime[ev->data1] > I_MSTime() && ev->type == EV_KeyDown)
 	{
 		// Key pressed for a double click
 		binding = DoubleBindings[ev->data1];
@@ -540,7 +542,7 @@
 		if (ev->type == EV_KeyDown)
 		{ // Key pressed for a normal press
 			binding = Bindings[ev->data1];
-			DClickTime[ev->data1] = level.time + 20;
+			DClickTime[ev->data1] = I_MSTime() + 571;
 		}
 		else if (DClicked[dclickspot] & dclickmask)
 		{ // Key released from a double click
diff -durN 96/src/c_cmds.cpp 96x/src/c_cmds.cpp
--- 96/src/c_cmds.cpp	2004-12-18 19:37:43.000000000 +0100
+++ 96x/src/c_cmds.cpp	2005-07-29 12:26:05.000000000 +0200
@@ -185,6 +185,16 @@
 	Net_WriteByte (CHT_ANUBIS);
 }
 
+// [GRB]
+CCMD (resurrect)
+{
+	if (CheckCheatmode ())
+		return;
+
+	Net_WriteByte (DEM_GENERICCHEAT);
+	Net_WriteByte (CHT_RESSURECT);
+}
+
 EXTERN_CVAR (Bool, chasedemo)
 
 CCMD (chase)
diff -durN 96/src/c_console.cpp 96x/src/c_console.cpp
--- 96/src/c_console.cpp	2004-12-24 16:39:50.000000000 +0100
+++ 96x/src/c_console.cpp	2005-06-22 15:18:31.000000000 +0200
@@ -133,7 +133,11 @@
 
 CVAR (Float, con_notifytime, 3.f, CVAR_ARCHIVE)
 CVAR (Bool, con_centernotify, false, CVAR_ARCHIVE)
-CVAR (Bool, con_scaletext, false, CVAR_ARCHIVE)		// Scale notify text at high resolutions?
+CUSTOM_CVAR (Int, con_scaletext, 0, CVAR_ARCHIVE)		// Scale notify text at high resolutions?
+{
+	if (self<0) self=0;
+	if (self>2) self=2;
+}
 
 // Command to run when Ctrl-D is pressed at start of line
 CVAR (String, con_ctrl_d, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
@@ -390,7 +394,7 @@
 		gamestate == GS_DEMOSCREEN)
 		return;
 
-	width = con_scaletext ? DisplayWidth / CleanXfac : DisplayWidth;
+	width = con_scaletext > 1 ? DisplayWidth/2 : con_scaletext==1? DisplayWidth / CleanXfac : DisplayWidth;
 
 	if (addtype == APPENDLINE && NotifyStrings[NUMNOTIFIES-1].printlevel == printlevel
 		&& (work = (char *)malloc (strlen ((char *)NotifyStrings[NUMNOTIFIES-1].text)
@@ -784,7 +788,7 @@
 	canskip = true;
 
 	lineadv = SmallFont->GetHeight ();
-	if (con_scaletext)
+	if (con_scaletext==1)
 	{
 		lineadv *= CleanYfac;
 	}
@@ -818,7 +822,7 @@
 			else
 				color = PrintColors[NotifyStrings[i].printlevel];
 
-			if (con_scaletext)
+			if (con_scaletext==1)
 			{
 				if (!center)
 					screen->DrawText (color, 0, line, (char *)NotifyStrings[i].text,
@@ -829,7 +833,7 @@
 						line, (char *)NotifyStrings[i].text, DTA_CleanNoMove, true,
 						DTA_Alpha, alpha, TAG_DONE);
 			}
-			else
+			else if (con_scaletext==0)
 			{
 				if (!center)
 					screen->DrawText (color, 0, line, (char *)NotifyStrings[i].text,
@@ -840,6 +844,23 @@
 						line, (char *)NotifyStrings[i].text,
 						DTA_Alpha, alpha, TAG_DONE);
 			}
+			else
+			{
+				if (!center)
+					screen->DrawText (color, 0, line, (char *)NotifyStrings[i].text,
+						DTA_VirtualWidth, screen->GetWidth() / 2, 
+						DTA_VirtualHeight, screen->GetHeight() / 2,
+						DTA_KeepRatio, true,
+						DTA_Alpha, alpha, TAG_DONE);
+				else
+					screen->DrawText (color, (screen->GetWidth() / 2 -
+						SmallFont->StringWidth (NotifyStrings[i].text))/2,
+						line, (char *)NotifyStrings[i].text,
+						DTA_VirtualWidth, screen->GetWidth() / 2, 
+						DTA_VirtualHeight, screen->GetHeight() / 2,
+						DTA_KeepRatio, true,
+						DTA_Alpha, alpha, TAG_DONE);
+			}
 			line += lineadv;
 			canskip = false;
 		}
diff -durN 96/src/c_dispatch.cpp 96x/src/c_dispatch.cpp
--- 96/src/c_dispatch.cpp	2004-10-04 21:07:01.000000000 +0200
+++ 96x/src/c_dispatch.cpp	2005-06-22 15:18:31.000000000 +0200
@@ -113,7 +113,7 @@
 
 FConsoleCommand *Commands[FConsoleCommand::HASH_SIZE];
 
-FButtonStatus Button_Mlook, Button_Klook, Button_Use,
+FButtonStatus Button_Mlook, Button_Klook, Button_Use, Button_Altattack,
 	Button_Attack, Button_Speed, Button_MoveRight, Button_MoveLeft,
 	Button_Strafe, Button_LookDown, Button_LookUp, Button_Back,
 	Button_Forward, Button_Right, Button_Left, Button_MoveDown,
@@ -142,6 +142,7 @@
 	{ 0xb4ca7514, &Button_Right,		"right" },
 	{ 0xb563e265, &Button_LookDown,		"lookdown" },
 	{ 0xb67a0835, &Button_Strafe,		"strafe" },
+	{ 0xc4704c1d, &Button_Altattack,	"altattack" },
 	{ 0xe2200fc9, &Button_MoveDown,		"movedown" },
 	{ 0xe78739bb, &Button_MoveRight,	"moveright" },
 	{ 0xe7912f86, &Button_Forward,		"forward" },
diff -durN 96/src/c_dispatch.h 96x/src/c_dispatch.h
--- 96/src/c_dispatch.h	2003-06-20 19:02:50.000000000 +0200
+++ 96x/src/c_dispatch.h	2005-06-22 15:18:31.000000000 +0200
@@ -141,7 +141,7 @@
 	void ResetTriggers () { bWentDown = bWentUp = false; }
 };
 
-extern FButtonStatus Button_Mlook, Button_Klook, Button_Use,
+extern FButtonStatus Button_Mlook, Button_Klook, Button_Use, Button_Altattack,
 	Button_Attack, Button_Speed, Button_MoveRight, Button_MoveLeft,
 	Button_Strafe, Button_LookDown, Button_LookUp, Button_Back,
 	Button_Forward, Button_Right, Button_Left, Button_MoveDown,
diff -durN 96/src/configfile.cpp 96x/src/configfile.cpp
--- 96/src/configfile.cpp	2004-09-27 22:56:38.000000000 +0200
+++ 96x/src/configfile.cpp	2005-06-22 15:18:31.000000000 +0200
@@ -57,6 +57,11 @@
 	void (*nosechandler)(const char *pathname, FConfigFile *config, void *userdata),
 	void *userdata)
 {
+	Sections = CurrentSection = NULL;
+	LastSectionPtr = &Sections;
+	CurrentEntry = NULL;
+	PathName = NULL;
+
 	ChangePathName (pathname);
 	LoadConfigFile (nosechandler, userdata);
 }
diff -durN 96/src/ct_chat.cpp 96x/src/ct_chat.cpp
--- 96/src/ct_chat.cpp	2004-10-27 12:03:16.000000000 +0200
+++ 96x/src/ct_chat.cpp	2005-06-22 15:18:31.000000000 +0200
@@ -35,7 +35,7 @@
 #define HU_INPUTX		0
 #define HU_INPUTY		(0 + (screen->Font->GetHeight () + 1))
 
-EXTERN_CVAR (Bool, con_scaletext)
+EXTERN_CVAR (Int, con_scaletext)
 
 // Public data
 
@@ -189,7 +189,8 @@
 		int i, x, scalex, y, promptwidth;
 
 		y = (viewactive || gamestate != GS_LEVEL) ? -10 : -30;
-		if (con_scaletext)
+
+		if (con_scaletext==1)
 		{
 			scalex = CleanXfac;
 			y *= CleanYfac;
@@ -199,14 +200,19 @@
 			scalex = 1;
 		}
 
-		y += ((SCREENHEIGHT == realviewheight && viewactive) || gamestate != GS_LEVEL) ? SCREENHEIGHT : ST_Y;
+		int screen_width = con_scaletext > 1? SCREENWIDTH/2 : SCREENWIDTH;
+		int screen_height = con_scaletext > 1? SCREENHEIGHT/2 : SCREENHEIGHT;
+		int st_y = con_scaletext > 1?  ST_Y/2 : ST_Y;
+
+		y += ((SCREENHEIGHT == realviewheight && viewactive) || gamestate != GS_LEVEL) ? screen_height : st_y;
 
 		promptwidth = SmallFont->StringWidth (prompt) * scalex;
 		x = screen->Font->GetCharWidth ('_') * scalex * 2 + promptwidth;
 
 		// figure out if the text is wider than the screen->
 		// if so, only draw the right-most portion of it.
-		for (i = len - 1; i >= 0 && x < SCREENWIDTH; i--)
+
+		for (i = len - 1; i >= 0 && x < screen_width; i--)
 		{
 			x += screen->Font->GetCharWidth (ChatQueue[i] & 0x7f) * scalex;
 		}
@@ -223,8 +229,19 @@
 		// draw the prompt, text, and cursor
 		ChatQueue[len] = gameinfo.gametype == GAME_Doom ? '_' : '[';
 		ChatQueue[len+1] = '\0';
-		screen->DrawText (CR_GREEN, 0, y, prompt, DTA_CleanNoMove, *con_scaletext, TAG_DONE);
-		screen->DrawText (CR_GREY, promptwidth, y, (char *)(ChatQueue + i), DTA_CleanNoMove, *con_scaletext, TAG_DONE);
+		if (con_scaletext<2)
+		{
+			screen->DrawText (CR_GREEN, 0, y, prompt, DTA_CleanNoMove, *con_scaletext, TAG_DONE);
+			screen->DrawText (CR_GREY, promptwidth, y, (char *)(ChatQueue + i), DTA_CleanNoMove, *con_scaletext, TAG_DONE);
+		}
+		else
+		{
+			screen->DrawText (CR_GREEN, 0, y, prompt, 
+				DTA_VirtualWidth, screen_width, DTA_VirtualHeight, screen_height, DTA_KeepRatio, true, TAG_DONE);
+			screen->DrawText (CR_GREY, promptwidth, y, (char *)(ChatQueue + i), 
+				DTA_VirtualWidth, screen_width, DTA_VirtualHeight, screen_height, DTA_KeepRatio, true, TAG_DONE);
+		}
+
 		ChatQueue[len] = '\0';
 
 		BorderTopRefresh = screen->GetPageCount ();
diff -durN 96/src/d_dehacked.cpp 96x/src/d_dehacked.cpp
--- 96/src/d_dehacked.cpp	2004-11-13 23:28:27.000000000 +0100
+++ 96x/src/d_dehacked.cpp	2005-06-22 15:18:31.000000000 +0200
@@ -2583,7 +2583,7 @@
 		{
 			RealPickup->flags |= MF_DROPPED;
 		}
-		if (!RealPickup->TryPickup (toucher))
+		if (!RealPickup->DoTryPickup (toucher))
 		{
 			RealPickup->Destroy ();
 			RealPickup = NULL;
diff -durN 96/src/d_event.h 96x/src/d_event.h
--- 96/src/d_event.h	2004-12-10 21:32:10.000000000 +0100
+++ 96x/src/d_event.h	2005-06-22 15:18:31.000000000 +0200
@@ -82,7 +82,7 @@
 	BT_ATTACK		= 1,	// Press "Fire".
 	BT_USE			= 2,	// Use button, to open doors, activate switches.
     BT_JUMP			= 4,
-    BT_DUCK			= 8,	// Unimplemented
+    BT_ALTATTACK	= 8,
 	BT_TURN180		= 16,
 } buttoncode_t;
 
diff -durN 96/src/d_main.cpp 96x/src/d_main.cpp
--- 96/src/d_main.cpp	2004-12-24 21:05:35.000000000 +0100
+++ 96x/src/d_main.cpp	2005-06-22 15:18:31.000000000 +0200
@@ -385,6 +385,7 @@
 CVAR (Flag, compat_notossdrops,	compatflags, COMPATF_NOTOSSDROPS);
 CVAR (Flag, compat_useblocking, compatflags, COMPATF_USEBLOCKING);
 CVAR (Flag, compat_nodoorlight,	compatflags, COMPATF_NODOORLIGHT);
+CVAR (Flag, compat_ravenscroll,	compatflags, COMPATF_RAVENSCROLL);
 
 //==========================================================================
 //
diff -durN 96/src/d_net.cpp 96x/src/d_net.cpp
--- 96/src/d_net.cpp	2004-12-24 16:55:34.000000000 +0100
+++ 96x/src/d_net.cpp	2005-06-22 15:18:31.000000000 +0200
@@ -2006,7 +2006,7 @@
 			int x, y;
 			x = ReadWord (stream);
 			y = ReadWord (stream);
-			P_TeleportMove (players[consoleplayer].mo, x * 65536, y * 65536, ONFLOORZ, true);
+			P_TeleportMove (players[player].mo, x * 65536, y * 65536, ONFLOORZ, true);
 		}
 		break;
 
diff -durN 96/src/d_player.h 96x/src/d_player.h
--- 96/src/d_player.h	2004-11-01 23:24:17.000000000 +0100
+++ 96x/src/d_player.h	2005-06-22 16:35:14.000000000 +0200
@@ -51,6 +51,8 @@
 {
 	DECLARE_STATELESS_ACTOR (APlayerPawn, AActor)
 public:
+	virtual void Serialize (FArchive &arc);
+
 	virtual void AddInventory (AInventory *item);
 	virtual void RemoveInventory (AInventory *item);
 	virtual bool UseInventory (AInventory *item);
@@ -81,6 +83,8 @@
 
 	void BeginPlay ();
 	void Die (AActor *source, AActor *inflictor);
+
+	fixed_t		JumpZ;					// [GRB] Variable JumpZ
 };
 
 class APlayerChunk : APlayerPawn
diff -durN 96/src/d_protocol.h 96x/src/d_protocol.h
--- 96/src/d_protocol.h	2004-12-10 21:32:10.000000000 +0100
+++ 96x/src/d_protocol.h	2005-06-22 15:18:31.000000000 +0200
@@ -176,6 +176,7 @@
 	CHT_NOMOMENTUM,
 	CHT_DONNYTRUMP,
 	CHT_LEGO,
+	CHT_RESSURECT,		// [GRB]
 };
 
 void StartChunk (int id, byte **stream);
diff -durN 96/src/decallib.cpp 96x/src/decallib.cpp
--- 96/src/decallib.cpp	2004-10-27 12:22:05.000000000 +0200
+++ 96x/src/decallib.cpp	2005-06-22 15:18:31.000000000 +0200
@@ -48,6 +48,7 @@
 
 static int ReadScale ();
 static TArray<BYTE> DecalTranslations;
+extern TArray<char*> DecalNames;
 
 // A decal group holds multiple decals and returns one randomly
 // when GetDecal() is called.
@@ -325,7 +326,7 @@
 
 void FDecalLib::ReadAllDecals ()
 {
-	int lump, lastlump = 0;
+	int i, lump, lastlump = 0;
 
 	while ((lump = Wads.FindLump ("DECALDEF", &lastlump)) != -1)
 	{
@@ -333,6 +334,26 @@
 		ReadDecals ();
 		SC_Close ();
 	}
+
+	// Supporting code to allow specifying decals directly in the DECORATE lump
+	for (i = 0;i < TypeInfo::m_RuntimeActors.Size(); i++)
+	{
+		AActor * def=(AActor*)GetDefaultByType(TypeInfo::m_RuntimeActors[i]);
+
+		int v=(int)def->DecalGenerator;
+		if (v>0 && v<=DecalNames.Size())
+		{
+			def->DecalGenerator = ScanTreeForName (DecalNames[v-1], Root);
+		}
+	}
+	// Free the array which is no longer needed!
+	for (i=0;i<DecalNames.Size();i++)
+	{
+		delete[] DecalNames[i];
+	}
+	DecalNames.Clear();
+	DecalNames.ShrinkToFit();
+
 }
 
 void FDecalLib::ReadDecals ()
diff -durN 96/src/decorations.cpp 96x/src/decorations.cpp
--- 96/src/decorations.cpp	2004-11-02 11:57:41.000000000 +0100
+++ 96x/src/decorations.cpp	2005-07-25 18:42:15.000000000 +0200
@@ -49,6 +49,7 @@
 #include "p_enemy.h"
 #include "a_action.h"
 #include "decallib.h"
+#include "i_system.h"
 
 // MACROS ------------------------------------------------------------------
 
@@ -193,6 +194,9 @@
 void ProcessActor(void (*process)(FState *, int));
 void ProcessWeapon(void (*process)(FState *, int));
 void FinishThingdef();
+void InitDecorateTranslations();
+void InitExpressions ();
+void STACK_ARGS ClearExpressions ();
 
 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
 
@@ -334,6 +338,9 @@
 	int lastlump, lump;
 
 	lastlump = 0;
+	InitDecorateTranslations();
+	InitExpressions ();
+	atterm (ClearExpressions);
 	while ((lump = Wads.FindLump ("DECORATE", &lastlump)) != -1)
 	{
 		SC_OpenLumpNum (lump, "DECORATE");
diff -durN 96/src/doomdef.h 96x/src/doomdef.h
--- 96/src/doomdef.h	2004-12-24 16:39:18.000000000 +0100
+++ 96x/src/doomdef.h	2005-06-22 15:18:31.000000000 +0200
@@ -260,6 +260,7 @@
 	COMPATF_NOTOSSDROPS		= 1 << 7,	// Spawn dropped items directly on the floor?
 	COMPATF_USEBLOCKING		= 1 << 8,	// Any special line can block a use line
 	COMPATF_NODOORLIGHT		= 1 << 9,	// Don't do the BOOM local door light effect
+	COMPATF_RAVENSCROLL		= 1 << 10,	// Raven's scrollers use their original carrying speed
 };
 
 // phares 3/20/98:
diff -durN 96/src/f_finale.cpp 96x/src/f_finale.cpp
--- 96/src/f_finale.cpp	2004-12-23 12:02:51.000000000 +0100
+++ 96x/src/f_finale.cpp	2005-06-29 15:24:56.000000000 +0200
@@ -1202,6 +1202,7 @@
 			break;
 		case END_Pic:
 			picname = EndSequences[FinaleSequence].PicName;
+			TexMan.AddPatch(picname);	// make sure it exists!
 			break;
 		case END_Bunny:
 		case END_BuyStrife:
diff -durN 96/src/g_doom/a_archvile.cpp 96x/src/g_doom/a_archvile.cpp
--- 96/src/g_doom/a_archvile.cpp	2004-11-18 18:43:49.000000000 +0100
+++ 96x/src/g_doom/a_archvile.cpp	2005-06-22 15:18:31.000000000 +0200
@@ -258,9 +258,19 @@
 					self->target = corpsehit;
 					A_FaceTarget (self);
 					self->target = temp;
-										
-					self->SetState (&AArchvile::States[S_VILE_HEAL]);
+								
+					// Make the state the monster enters customizable - but leave the
+					// default for Dehacked compatibility!
+					if (self->HealState)
+					{
+						self->SetState(self->HealState);
+					}
+					else
+					{
+						self->SetState (&AArchvile::States[S_VILE_HEAL]);
+					}
 					S_Sound (corpsehit, CHAN_BODY, "vile/raise", 1, ATTN_IDLE);
+
 					info = corpsehit->GetDefault ();
 					
 					corpsehit->SetState (info->RaiseState);
diff -durN 96/src/g_doom/a_doomglobal.h 96x/src/g_doom/a_doomglobal.h
--- 96/src/g_doom/a_doomglobal.h	2004-06-01 18:23:53.000000000 +0200
+++ 96x/src/g_doom/a_doomglobal.h	2005-06-22 15:18:31.000000000 +0200
@@ -42,8 +42,6 @@
 class APainElemental : public AActor
 {
 	DECLARE_ACTOR (APainElemental, AActor)
-public:
-	void Tick ();
 };
 
 class ALostSoul : public AActor
diff -durN 96/src/g_doom/a_doommisc.cpp 96x/src/g_doom/a_doommisc.cpp
--- 96/src/g_doom/a_doommisc.cpp	2003-04-05 22:26:38.000000000 +0200
+++ 96x/src/g_doom/a_doommisc.cpp	2005-06-22 15:18:31.000000000 +0200
@@ -97,10 +97,12 @@
 
 IMPLEMENT_ACTOR (ABulletPuff, Doom, -1, 131)
 	PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY)
+	PROP_Flags4(MF4_ALLOWPARTICLES)
 	PROP_RenderStyle (STYLE_Translucent)
 	PROP_Alpha (TRANSLUC50)
 
 	PROP_SpawnState (0)
+	PROP_MeleeState (2)
 END_DEFAULTS
 
 void ABulletPuff::BeginPlay ()
diff -durN 96/src/g_doom/a_doomweaps.cpp 96x/src/g_doom/a_doomweaps.cpp
--- 96/src/g_doom/a_doomweaps.cpp	2004-12-09 19:40:30.000000000 +0100
+++ 96x/src/g_doom/a_doomweaps.cpp	2005-07-25 11:06:53.000000000 +0200
@@ -1150,7 +1150,8 @@
 
 	damage = deathmatch ? 100 : 150;
 
-	P_RailAttack (actor, damage, RailOffset);
+	P_RailAttack (actor, damage, RailOffset,
+		ColorMatcher.Pick (0, 0, 255), ColorMatcher.Pick (255, 255, 255));
 	RailOffset = 0;
 }
 
@@ -1323,6 +1324,7 @@
 }
 
 bool thebfugu;
+int EvalExpressionI (int id, AActor *self);
 //
 // A_BFGSpray
 // Spawn a BFG explosion on every monster in view
@@ -1334,15 +1336,27 @@
 	int 				damage;
 	angle_t 			an;
 	AActor				*thingToHit;
+	const TypeInfo * spraytype = NULL;
+	int numrays = 40;
+
+	int index=CheckIndex(2, NULL);
+	if (index>=0) 
+	{
+		spraytype = TypeInfo::FindType((const char *)StateParameters[index]);
+		numrays = EvalExpressionI (StateParameters[index+1], mo);
+		if (!numrays)
+			numrays = 40;
+	}
+	if (spraytype == NULL) spraytype = RUNTIME_CLASS(ABFGExtra);
 
 	// [RH] Don't crash if no target
 	if (!mo->target)
 		return;
 
 	// offset angles from its attack angle
-	for (i = 0; i < 40; i++)
+	for (i = 0; i < numrays; i++)
 	{
-		an = mo->angle - ANG90/2 + ANG90/40*i;
+		an = mo->angle - ANG90/2 + ANG90/numrays*i;
 
 		// mo->target is the originator (player) of the missile
 		P_AimLineAttack (mo->target, an, 16*64*FRACUNIT, ANGLE_1*32);
@@ -1350,7 +1364,7 @@
 		if (!linetarget)
 			continue;
 
-		Spawn<ABFGExtra> (linetarget->x, linetarget->y,
+		Spawn (spraytype, linetarget->x, linetarget->y,
 			linetarget->z + (linetarget->height>>2));
 		
 		damage = 0;
diff -durN 96/src/g_doom/a_fatso.cpp 96x/src/g_doom/a_fatso.cpp
--- 96/src/g_doom/a_fatso.cpp	2003-04-07 14:17:04.000000000 +0200
+++ 96x/src/g_doom/a_fatso.cpp	2005-07-25 11:07:35.000000000 +0200
@@ -170,12 +170,17 @@
 	if (!self->target)
 		return;
 
+	const TypeInfo * spawntype = NULL;
+	int index=CheckIndex(1, NULL);
+	if (index>=0) spawntype = TypeInfo::FindType((const char *)StateParameters[index]);
+	if (spawntype == NULL) spawntype = RUNTIME_CLASS(AFatShot);
+
 	A_FaceTarget (self);
 	// Change direction  to ...
 	self->angle += FATSPREAD;
-	P_SpawnMissile (self, self->target, RUNTIME_CLASS(AFatShot));
+	P_SpawnMissile (self, self->target, spawntype);
 
-	missile = P_SpawnMissile (self, self->target, RUNTIME_CLASS(AFatShot));
+	missile = P_SpawnMissile (self, self->target, spawntype);
 	if (missile != NULL)
 	{
 		missile->angle += FATSPREAD;
@@ -193,12 +198,17 @@
 	if (!self->target)
 		return;
 
+	const TypeInfo * spawntype = NULL;
+	int index=CheckIndex(1, NULL);
+	if (index>=0) spawntype = TypeInfo::FindType((const char *)StateParameters[index]);
+	if (spawntype == NULL) spawntype = RUNTIME_CLASS(AFatShot);
+
 	A_FaceTarget (self);
 	// Now here choose opposite deviation.
 	self->angle -= FATSPREAD;
-	P_SpawnMissile (self, self->target, RUNTIME_CLASS(AFatShot));
+	P_SpawnMissile (self, self->target, spawntype);
 
-	missile = P_SpawnMissile (self, self->target, RUNTIME_CLASS(AFatShot));
+	missile = P_SpawnMissile (self, self->target, spawntype);
 	if (missile != NULL)
 	{
 		missile->angle -= FATSPREAD*2;
@@ -216,9 +226,14 @@
 	if (!self->target)
 		return;
 
+	const TypeInfo * spawntype = NULL;
+	int index=CheckIndex(1, NULL);
+	if (index>=0) spawntype = TypeInfo::FindType((const char *)StateParameters[index]);
+	if (spawntype == NULL) spawntype = RUNTIME_CLASS(AFatShot);
+
 	A_FaceTarget (self);
 	
-	missile = P_SpawnMissile (self, self->target, RUNTIME_CLASS(AFatShot));
+	missile = P_SpawnMissile (self, self->target, spawntype);
 	if (missile != NULL)
 	{
 		missile->angle -= FATSPREAD/2;
@@ -227,7 +242,7 @@
 		missile->momy = FixedMul (missile->Speed, finesine[an]);
 	}
 
-	missile = P_SpawnMissile (self, self->target, RUNTIME_CLASS(AFatShot));
+	missile = P_SpawnMissile (self, self->target, spawntype);
 	if (missile != NULL)
 	{
 		missile->angle += FATSPREAD/2;
@@ -237,6 +252,7 @@
 	}
 }
 
+int EvalExpressionI (int id, AActor *self);
 //
 // killough 9/98: a mushroom explosion effect, sorta :)
 // Original idea: Linguica
@@ -246,6 +262,17 @@
 {
 	int i, j, n = actor->damage;
 
+	const TypeInfo * spawntype = NULL;
+	int index=CheckIndex(1, NULL);
+	if (index>=0) 
+	{
+		spawntype = TypeInfo::FindType((const char *)StateParameters[index]);
+		n = EvalExpressionI (StateParameters[index+1], actor);
+		if (!n)
+			n = actor->damage;
+	}
+	if (spawntype == NULL) spawntype = RUNTIME_CLASS(AFatShot);
+
 	A_Explode (actor);	// First make normal explosion
 
 	// Now launch mushroom cloud
@@ -257,7 +284,7 @@
 			target.x += i << FRACBITS; // Aim in many directions from source
 			target.y += j << FRACBITS;
 			target.z += P_AproxDistance(i,j) << (FRACBITS+2); // Aim up fairly high
-			mo = P_SpawnMissile (actor, &target, RUNTIME_CLASS(AFatShot)); // Launch fireball
+			mo = P_SpawnMissile (actor, &target, spawntype); // Launch fireball
 			if (mo != NULL)
 			{
 				mo->momx >>= 1;
diff -durN 96/src/g_doom/a_painelemental.cpp 96x/src/g_doom/a_painelemental.cpp
--- 96/src/g_doom/a_painelemental.cpp	2004-01-12 14:38:20.000000000 +0100
+++ 96x/src/g_doom/a_painelemental.cpp	2005-06-22 15:18:31.000000000 +0200
@@ -73,22 +73,6 @@
 	PROP_ActiveSound ("pain/active")
 END_DEFAULTS
 
-void APainElemental::Tick ()
-{
-	// [RH] Give the pain elemental vertical friction
-	if (flags & MF_FLOAT)
-	{
-		if (abs (momz) < FRACUNIT/4)
-		{
-			momz = 0;
-		}
-		else
-		{
-			momz = FixedMul (momz, 0xe800);
-		}
-	}
-	Super::Tick ();
-}
 
 //
 // A_PainShootSkull
@@ -102,12 +86,26 @@
 	angle_t an;
 	int prestep;
 
+	const TypeInfo * spawntype = NULL;
+
+	int index=CheckIndex(1, NULL);
+	if (index>=0) 
+	{
+		spawntype = TypeInfo::FindType((const char *)StateParameters[index]);
+	}
+	if (spawntype == NULL) spawntype = RUNTIME_CLASS(ALostSoul);
+
+
 	// [RH] check to make sure it's not too close to the ceiling
 	if (self->z + self->height + 8*FRACUNIT > self->ceilingz)
 	{
-		self->momz -= 2*FRACUNIT;
-		self->flags |= MF_INFLOAT;
-		return;
+		if (self->flags & MF_FLOAT)	// doesn't make sense otherwise!
+		{
+			self->momz -= 2*FRACUNIT;
+			self->flags |= MF_INFLOAT;
+			self->flags4 |= MF4_VFRICTION;
+			return;
+		}
 	}
 
 	// [RH] make this optional
@@ -116,9 +114,9 @@
 		// count total number of skulls currently on the level
 		// if there are already 20 skulls on the level, don't spit another one
 		int count = 20;
-		TThinkerIterator<ALostSoul> iterator;
+		TThinkerIterator<AActor> iterator;
 
-		while ( (other = iterator.Next ()) )
+		while ( (other = iterator.Next ()) && other->IsA(spawntype))
 		{
 			if (--count == 0)
 				return;
@@ -129,7 +127,7 @@
 	an = angle >> ANGLETOFINESHIFT;
 	
 	prestep = 4*FRACUNIT +
-		3*(self->radius + GetDefault<ALostSoul>()->radius)/2;
+		3*(self->radius + ((AActor*)(spawntype->ActorInfo->Defaults)) ->radius)/2;
 	
 	x = self->x + FixedMul (prestep, finecosine[an]);
 	y = self->y + FixedMul (prestep, finesine[an]);
@@ -144,7 +142,7 @@
 		return;
 	}
 
-	other = Spawn<ALostSoul> (x, y, z);
+	other = Spawn (spawntype, x, y, z);
 
 
 	// Check to see if the new Lost Soul's z value is above the
diff -durN 96/src/g_doom/a_possessed.cpp 96x/src/g_doom/a_possessed.cpp
--- 96/src/g_doom/a_possessed.cpp	2004-11-02 12:32:17.000000000 +0100
+++ 96x/src/g_doom/a_possessed.cpp	2005-06-22 15:18:31.000000000 +0200
@@ -548,7 +548,8 @@
 	if (pr_cposrefire() < 40)
 		return;
 
-	if (!self->target
+	if (!self->target 
+		|| P_HitFriend(self)
 		|| self->target->health <= 0
 		|| !P_CheckSight (self, self->target, 0) )
 	{
diff -durN 96/src/g_doom/a_spidermaster.cpp 96x/src/g_doom/a_spidermaster.cpp
--- 96/src/g_doom/a_spidermaster.cpp	2004-06-30 11:43:46.000000000 +0200
+++ 96x/src/g_doom/a_spidermaster.cpp	2005-06-22 15:18:31.000000000 +0200
@@ -98,7 +98,8 @@
 	if (pr_spidrefire() < 10)
 		return;
 
-	if (!self->target
+	if (!self->target 
+		|| P_HitFriend(self)
 		|| self->target->health <= 0
 		|| !P_CheckSight (self, self->target, 0) )
 	{
diff -durN 96/src/g_game.cpp 96x/src/g_game.cpp
--- 96/src/g_game.cpp	2004-12-24 17:15:23.000000000 +0100
+++ 96x/src/g_game.cpp	2005-07-24 20:12:09.000000000 +0200
@@ -494,6 +494,9 @@
 	if (Button_Attack.bDown)
 		cmd->ucmd.buttons |= BT_ATTACK;
 
+	if (Button_Altattack.bDown)
+		cmd->ucmd.buttons |= BT_ALTATTACK;
+
 	if (Button_Use.bDown)
 		cmd->ucmd.buttons |= BT_USE;
 
@@ -592,6 +595,9 @@
 	cmd->ucmd.sidemove <<= 8;
 }
 
+//[Graf Zahl] This really helps if the mouse update rate can't be increased!
+CVAR (Bool,		smooth_mouse,	false,	CVAR_GLOBALCONFIG|CVAR_ARCHIVE)
+
 void G_AddViewPitch (int look)
 {
 	if (gamestate == GS_TITLELEVEL)
@@ -608,7 +614,7 @@
 	}
 	if (look != 0)
 	{
-		LocalKeyboardTurner = false;
+		LocalKeyboardTurner = smooth_mouse;
 	}
 }
 
@@ -621,7 +627,7 @@
 	LocalViewAngle -= yaw << 16;
 	if (yaw != 0)
 	{
-		LocalKeyboardTurner = false;
+		LocalKeyboardTurner = smooth_mouse;
 	}
 }
 
@@ -1036,7 +1042,7 @@
 		}
 	}
 
-	if (mode == FINISH_NoHub)
+	if (mode == FINISH_NoHub && !(level.flags & LEVEL_KEEPFULLINVENTORY))
 	{ // Reduce all owned (visible) inventory to 1 item each
 		for (item = p->mo->Inventory; item != NULL; item = item->Inventory)
 		{
@@ -1715,6 +1721,13 @@
 
 CVAR (Int, autosavenum, 0, CVAR_NOSET|CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
 CVAR (Int, disableautosave, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
+CUSTOM_CVAR (Int, autosavecount, 4, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
+{
+	if (self < 0)
+		self = 0;
+	if (self > 20)
+		self = 20;
+}
 
 extern void P_CalcHeight (player_t *);
 
@@ -1735,7 +1748,7 @@
 	char name[PATH_MAX];
 	char *readableTime;
 	
-	num.Int = (autosavenum + 1) & 3;
+	num.Int = (autosavenum + 1) % autosavecount;
 	autosavenum.ForceSet (num, CVAR_Int);
 
 	G_BuildSaveName (name, "auto", num.Int);
diff -durN 96/src/g_heretic/a_dsparil.cpp 96x/src/g_heretic/a_dsparil.cpp
--- 96/src/g_heretic/a_dsparil.cpp	2004-11-18 18:29:50.000000000 +0100
+++ 96x/src/g_heretic/a_dsparil.cpp	2005-06-22 15:18:31.000000000 +0200
@@ -295,20 +295,6 @@
 	}
 }
 
-bool ASorcerer2::OkayToSwitchTarget (AActor *other)
-{
-	if (!Super::OkayToSwitchTarget (other))
-	{
-		return false;
-	}
-	if (other->TIDtoHate == TIDtoHate &&
-		other->IsKindOf (RUNTIME_CLASS(AWizard)))
-	{
-		return false;
-	}
-	return true;
-}
-
 const char *ASorcerer2::GetObituary ()
 {
 	return GStrings(OB_DSPARIL2);
@@ -716,6 +702,7 @@
 			mo->flags3 |= master->flags3 & (MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS);
 			mo->flags4 |= master->flags4 & MF4_NOHATEPLAYERS;
 			mo->flags = (mo->flags & ~MF_FRIENDLY) | (master->flags & MF_FRIENDLY);
+			mo->master = master;
 
 			actor->momx = actor->momy = actor->momz = 0;
 			actor->SetState (actor->DeathState);
diff -durN 96/src/g_heretic/a_hereticglobal.h 96x/src/g_heretic/a_hereticglobal.h
--- 96/src/g_heretic/a_hereticglobal.h	2003-01-23 23:42:08.000000000 +0100
+++ 96x/src/g_heretic/a_hereticglobal.h	2005-06-22 15:18:31.000000000 +0200
@@ -27,7 +27,6 @@
 public:
 	void Serialize (FArchive &arc);
 	void BeginPlay ();
-	bool OkayToSwitchTarget (AActor *other);
 	const char *GetObituary ();
 	const char *GetHitObituary ();
 
@@ -40,7 +39,6 @@
 	DECLARE_ACTOR (AWizard, AActor)
 public:
 	void NoBlockingSet ();
-	bool OkayToSwitchTarget (AActor *other);
 	const char *GetObituary ();
 	const char *GetHitObituary ();
 };
diff -durN 96/src/g_heretic/a_wizard.cpp 96x/src/g_heretic/a_wizard.cpp
--- 96/src/g_heretic/a_wizard.cpp	2004-04-10 23:42:24.000000000 +0200
+++ 96x/src/g_heretic/a_wizard.cpp	2005-06-22 15:18:31.000000000 +0200
@@ -89,20 +89,6 @@
 	P_DropItem (this, "ArtiTomeOfPower", 0, 4);
 }
 
-bool AWizard::OkayToSwitchTarget (AActor *other)
-{
-	if (!Super::OkayToSwitchTarget (other))
-	{
-		return false;
-	}
-	if (other->TIDtoHate == TIDtoHate &&
-		other->IsKindOf (RUNTIME_CLASS(ASorcerer2)))
-	{
-		return false;
-	}
-	return true;
-}
-
 const char *AWizard::GetObituary ()
 {
 	return GStrings (OB_WIZARD);
diff -durN 96/src/g_hexen/a_bats.cpp 96x/src/g_hexen/a_bats.cpp
--- 96/src/g_hexen/a_bats.cpp	2003-01-02 14:33:54.000000000 +0100
+++ 96x/src/g_hexen/a_bats.cpp	2005-06-22 15:18:31.000000000 +0200
@@ -24,12 +24,12 @@
 FState ABatSpawner::States[] =
 {
 #define S_SPAWNBATS1 0
-	S_NORMAL (MAN1, 'A',	2, NULL					    , &States[S_SPAWNBATS1+1]),
-	S_NORMAL (MAN1, 'A',	2, A_BatSpawnInit		    , &States[S_SPAWNBATS1+2]),
-	S_NORMAL (MAN1, 'A',	2, A_BatSpawn			    , &States[S_SPAWNBATS1+2]),
+	S_NORMAL (TNT1, 'A',	2, NULL					    , &States[S_SPAWNBATS1+1]),
+	S_NORMAL (TNT1, 'A',	2, A_BatSpawnInit		    , &States[S_SPAWNBATS1+2]),
+	S_NORMAL (TNT1, 'A',	2, A_BatSpawn			    , &States[S_SPAWNBATS1+2]),
 
 #define S_SPAWNBATS_OFF (S_SPAWNBATS1+3)
-	S_NORMAL (MAN1, 'A',   -1, NULL					    , NULL)
+	S_NORMAL (TNT1, 'A',   -1, NULL					    , NULL)
 };
 
 IMPLEMENT_ACTOR (ABatSpawner, Hexen, 10225, 0)
diff -durN 96/src/g_hexen/a_bishop.cpp 96x/src/g_hexen/a_bishop.cpp
--- 96/src/g_hexen/a_bishop.cpp	2004-11-02 12:21:25.000000000 +0100
+++ 96x/src/g_hexen/a_bishop.cpp	2005-06-22 15:18:31.000000000 +0200
@@ -88,6 +88,7 @@
 	PROP_Flags (MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_FLOAT|MF_NOGRAVITY|MF_NOBLOOD)
 	PROP_Flags2 (MF2_PASSMOBJ|MF2_PUSHWALL|MF2_TELESTOMP)
 	PROP_Flags3 (MF3_DONTOVERLAP)
+	PROP_Flags4 (MF4_NOTARGETSWITCH)
 
 	PROP_SpawnState (S_BISHOP_LOOK)
 	PROP_SeeState (S_BISHOP_WALK)
@@ -108,24 +109,6 @@
 	damage = 25 + (pr_boom() & 15);
 }
 
-bool ABishop::OkayToSwitchTarget (AActor *other)
-{
-	if (target != NULL)
-	{ // Bishops never change their target until they've killed their current one.
-		return false;
-	}
-	if (!Super::OkayToSwitchTarget (other))
-	{
-		return false;
-	}
-	if (other->TIDtoHate == TIDtoHate &&
-		other->IsKindOf (RUNTIME_CLASS(AHeresiarch)))
-	{
-		return false;
-	}
-	return true;
-}
-
 // Bishop puff --------------------------------------------------------------
 
 class ABishopPuff : public AActor
diff -durN 96/src/g_hexen/a_centaur.cpp 96x/src/g_hexen/a_centaur.cpp
--- 96/src/g_hexen/a_centaur.cpp	2004-11-02 12:21:25.000000000 +0100
+++ 96x/src/g_hexen/a_centaur.cpp	2005-06-22 15:18:31.000000000 +0200
@@ -90,6 +90,7 @@
 	PROP_Mass (120)
 	PROP_Flags (MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL)
 	PROP_Flags2 (MF2_FLOORCLIP|MF2_PASSMOBJ|MF2_TELESTOMP|MF2_PUSHWALL|MF2_MCROSS)
+	PROP_Flags4 (MF4_SHIELDREFLECT)
 
 	PROP_SpawnState (S_CENTAUR_LOOK1)
 	PROP_SeeState (S_CENTAUR_WALK1)
@@ -115,22 +116,6 @@
 	}
 }
 
-bool ACentaur::AdjustReflectionAngle (AActor *thing, angle_t &angle)
-{
-	if (abs (angle-BlockingMobj->angle)>>24 > 45)
-		return true;	// Let missile explode
-
-	if (thing->IsKindOf (RUNTIME_CLASS(AHolySpirit)))
-		return true;
-
-	if (pr_reflect () < 128)
-		angle += ANGLE_45;
-	else
-		angle -= ANGLE_45;
-
-	return false;
-}
-
 // Centaur Leader -----------------------------------------------------------
 
 class ACentaurLeader : public ACentaur
@@ -346,7 +331,9 @@
 	A_FaceTarget (actor);
 	if (actor->CheckMeleeRange() && pr_centaurdefend() < 32)
 	{
-		A_UnSetInvulnerable (actor);
+		// This should unset REFLECTIVE as well 
+		// (unless you want the Centaur to relect projectiles forever!)
+		A_UnSetReflectiveInvulnerable (actor);
 		actor->SetState (actor->MeleeState);
 	}
 }
diff -durN 96/src/g_hexen/a_clericplayer.cpp 96x/src/g_hexen/a_clericplayer.cpp
--- 96/src/g_hexen/a_clericplayer.cpp	2004-12-24 15:55:30.000000000 +0100
+++ 96x/src/g_hexen/a_clericplayer.cpp	2005-06-22 15:18:31.000000000 +0200
@@ -59,11 +59,11 @@
 	S_NORMAL (CLER, '\\',	1, A_FreezeDeathChunks	    , &States[S_CPLAY_ICE+1]),
 
 #define S_PLAY_C_FDTH (S_CPLAY_ICE+2)
-	S_BRIGHT (FDTH, 'C',	5, NULL					    , &States[S_PLAY_C_FDTH+1]),
+	S_BRIGHT (FDTH, 'C',	5, A_FireScream			    , &States[S_PLAY_C_FDTH+1]),
 	S_BRIGHT (FDTH, 'D',	4, NULL					    , &States[S_PLAY_C_FDTH+2]),
 
 #define S_PLAY_FDTH (S_PLAY_C_FDTH+2)
-	S_BRIGHT (FDTH, 'G',	5, A_FireScream				, &States[S_PLAY_FDTH+1]),
+	S_BRIGHT (FDTH, 'G',	5, NULL						, &States[S_PLAY_FDTH+1]),
 	S_BRIGHT (FDTH, 'H',	4, A_PlayerScream 			, &States[S_PLAY_FDTH+2]),
 	S_BRIGHT (FDTH, 'I',	5, NULL 					, &States[S_PLAY_FDTH+3]),
 	S_BRIGHT (FDTH, 'J',	4, NULL 					, &States[S_PLAY_FDTH+4]),
diff -durN 96/src/g_hexen/a_demons.cpp 96x/src/g_hexen/a_demons.cpp
--- 96/src/g_hexen/a_demons.cpp	2004-11-02 12:21:25.000000000 +0100
+++ 96x/src/g_hexen/a_demons.cpp	2005-06-22 15:18:31.000000000 +0200
@@ -565,10 +565,12 @@
 {
 	AActor *mo;
 
+	// Improve the aiming of this code!
+	actor->z += 30*FRACUNIT;
 	mo = P_SpawnMissile (actor, actor->target, RUNTIME_CLASS(ADemon1FX1));
+	actor->z -= 30*FRACUNIT;
 	if (mo)
 	{
-		mo->z += 30*FRACUNIT;
 		S_Sound (actor, CHAN_BODY, "DemonMissileFire", 1, ATTN_NORM);
 	}
 }
@@ -583,10 +585,12 @@
 {
 	AActor *mo;
 
+	// Improve the aiming of this code!
+	actor->z += 30*FRACUNIT;
 	mo = P_SpawnMissile (actor, actor->target, RUNTIME_CLASS(ADemon2FX1));
+	actor->z -= 30*FRACUNIT;
 	if (mo)
 	{
-		mo->z += 30*FRACUNIT;
 		S_Sound (actor, CHAN_BODY, "DemonMissileFire", 1, ATTN_NORM);
 	}
 }
diff -durN 96/src/g_hexen/a_fighteraxe.cpp 96x/src/g_hexen/a_fighteraxe.cpp
--- 96/src/g_hexen/a_fighteraxe.cpp	2004-11-02 12:32:17.000000000 +0100
+++ 96x/src/g_hexen/a_fighteraxe.cpp	2005-06-22 15:18:31.000000000 +0200
@@ -448,6 +448,8 @@
 
 void P_BloodSplatter2 (fixed_t x, fixed_t y, fixed_t z, AActor *originator)
 {
+	PalEntry bloodcolor = 0;//(PalEntry)originator->GetClass()->Meta.GetMetaInt(AMETA_BloodColor);
+
 	if (cl_bloodtype <= 1)
 	{
 		AActor *mo;
@@ -457,9 +459,12 @@
 
 		mo = Spawn<AAxeBlood> (x, y, z);
 		mo->target = originator;
+
+		// colorize the blood!
+//		if (bloodcolor!=0) mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
 	}
 	if (cl_bloodtype >= 1)
 	{
-		P_DrawSplash2 (100, x, y, z, R_PointToAngle (originator->x - x, originator->y - y), 2, 0);
+		P_DrawSplash2 (100, x, y, z, R_PointToAngle2 (0, 0, originator->x - x, originator->y - y), 2, bloodcolor);
 	}
 }
diff -durN 96/src/g_hexen/a_fighterplayer.cpp 96x/src/g_hexen/a_fighterplayer.cpp
--- 96/src/g_hexen/a_fighterplayer.cpp	2004-12-24 15:55:30.000000000 +0100
+++ 96x/src/g_hexen/a_fighterplayer.cpp	2005-06-22 15:18:32.000000000 +0200
@@ -58,7 +58,7 @@
 	S_NORMAL (PLAY, 'X',	1, A_FreezeDeathChunks		, &States[S_FPLAY_ICE+1]),
 
 #define S_PLAY_FDTH (S_FPLAY_ICE+2)
-	S_BRIGHT (FDTH, 'G',	5, A_FireScream				, &States[S_PLAY_FDTH+1]),
+	S_BRIGHT (FDTH, 'G',	5, NULL						, &States[S_PLAY_FDTH+1]),
 	S_BRIGHT (FDTH, 'H',	4, A_PlayerScream 			, &States[S_PLAY_FDTH+2]),
 	S_BRIGHT (FDTH, 'I',	5, NULL 					, &States[S_PLAY_FDTH+3]),
 	S_BRIGHT (FDTH, 'J',	4, NULL 					, &States[S_PLAY_FDTH+4]),
@@ -69,7 +69,7 @@
 	S_BRIGHT (FDTH, 'O',	5, NULL 					, &States[S_PLAY_FDTH+9]),
 	S_BRIGHT (FDTH, 'P',	4, NULL 					, &States[S_PLAY_FDTH+10]),
 	S_BRIGHT (FDTH, 'Q',	5, NULL 					, &States[S_PLAY_FDTH+11]),
-	S_BRIGHT (FDTH, 'R',	4, NULL 					, &States[S_PLAY_FDTH+12]),
+	S_BRIGHT (FDTH, 'R',	4, NULL 					, &States[S_PLAY_FDTH+12]), 
 	S_BRIGHT (FDTH, 'S',	5, A_NoBlocking 			, &States[S_PLAY_FDTH+13]),
 	S_BRIGHT (FDTH, 'T',	4, NULL 					, &States[S_PLAY_FDTH+14]),
 	S_BRIGHT (FDTH, 'U',	5, NULL 					, &States[S_PLAY_FDTH+15]),
@@ -78,7 +78,7 @@
 	S_NORMAL (ACLO, 'E',	8, NULL 					, NULL),
 
 #define S_PLAY_F_FDTH (S_PLAY_FDTH+18)
-	S_BRIGHT (FDTH, 'A',	5, NULL 					, &States[S_PLAY_F_FDTH+1]),
+	S_BRIGHT (FDTH, 'A',	5, A_FireScream				, &States[S_PLAY_F_FDTH+1]),
 	S_BRIGHT (FDTH, 'B',	4, NULL 					, &States[S_PLAY_FDTH]),
 };
 
diff -durN 96/src/g_hexen/a_fog.cpp 96x/src/g_hexen/a_fog.cpp
--- 96/src/g_hexen/a_fog.cpp	2002-03-05 13:35:40.000000000 +0100
+++ 96x/src/g_hexen/a_fog.cpp	2005-06-22 15:18:32.000000000 +0200
@@ -18,7 +18,7 @@
 FState AFogSpawner::States[] =
 {
 #define S_SPAWNFOG1 0
-	S_NORMAL (MAN1, 'A',   20, A_FogSpawn			    , &States[S_SPAWNFOG1]),
+	S_NORMAL (TNT1, 'A',   20, A_FogSpawn			    , &States[S_SPAWNFOG1]),
 };
 
 IMPLEMENT_ACTOR (AFogSpawner, Hexen, 10000, 0)
diff -durN 96/src/g_hexen/a_heresiarch.cpp 96x/src/g_hexen/a_heresiarch.cpp
--- 96/src/g_hexen/a_heresiarch.cpp	2004-11-18 18:29:08.000000000 +0100
+++ 96x/src/g_hexen/a_heresiarch.cpp	2005-06-22 15:18:32.000000000 +0200
@@ -133,7 +133,7 @@
 	PROP_Flags (MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD|MF_COUNTKILL)
 	PROP_Flags2 (MF2_FLOORCLIP|MF2_PASSMOBJ|MF2_BOSS|MF2_PUSHWALL|MF2_MCROSS)
 	PROP_Flags3 (MF3_DONTMORPH)
-	PROP_Flags4 (MF4_NOICEDEATH)
+	PROP_Flags4 (MF4_NOICEDEATH|MF4_DEFLECT)
 
 	PROP_SpawnState (S_SORC_SPAWN1)
 	PROP_SeeState (S_SORC_WALK1)
@@ -174,31 +174,6 @@
 	}
 }
 
-bool AHeresiarch::AdjustReflectionAngle (AActor *thing, angle_t &angle)
-{
-	// Deflection
-	if (pr_heresiarch() < 128)
-		angle += ANGLE_45;
-	else
-		angle -= ANGLE_45;
-
-	return false;
-}
-
-bool AHeresiarch::OkayToSwitchTarget (AActor *other)
-{
-	if (!Super::OkayToSwitchTarget (other))
-	{
-		return false;
-	}
-	if (other->TIDtoHate == TIDtoHate &&
-		other->IsKindOf (RUNTIME_CLASS(ABishop)))
-	{
-		return false;
-	}
-	return true;
-}
-
 // Base class for the balls flying around the Heresiarch's head -------------
 
 class ASorcBall : public AActor
@@ -1327,6 +1302,7 @@
 			mo->flags3 |= master->flags3 & (MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS);
 			mo->flags4 |= master->flags4 & MF4_NOHATEPLAYERS;
 			mo->flags = (mo->flags & ~MF_FRIENDLY) | (actor->flags & MF_FRIENDLY);
+			mo->master = master;
 		}
 	}
 	actor->Destroy ();
diff -durN 96/src/g_hexen/a_hexenglobal.h 96x/src/g_hexen/a_hexenglobal.h
--- 96/src/g_hexen/a_hexenglobal.h	2004-11-29 20:20:42.000000000 +0100
+++ 96x/src/g_hexen/a_hexenglobal.h	2005-06-22 15:18:32.000000000 +0200
@@ -24,7 +24,6 @@
 	DECLARE_ACTOR (ABishop, AActor)
 public:
 	void GetExplodeParms (int &damage, int &distance, bool &hurtSource);
-	bool OkayToSwitchTarget (AActor *other);
 };
 
 class AHeresiarch : public AActor
@@ -35,8 +34,6 @@
 
 	void Serialize (FArchive &arc);
 	void Die (AActor *source, AActor *inflictor);
-	bool AdjustReflectionAngle (AActor *thing, angle_t &angle);
-	bool OkayToSwitchTarget (AActor *other);
 };
 
 class AHolySpirit : public AActor
@@ -60,7 +57,6 @@
 	DECLARE_ACTOR (ACentaur, AActor)
 public:
 	void Howl ();
-	bool AdjustReflectionAngle (AActor *thing, angle_t &angle);
 };
 
 class AFourthWeaponPiece : public AInventory
diff -durN 96/src/g_hexen/a_hexenspecialdecs.cpp 96x/src/g_hexen/a_hexenspecialdecs.cpp
--- 96/src/g_hexen/a_hexenspecialdecs.cpp	2004-11-02 12:21:25.000000000 +0100
+++ 96x/src/g_hexen/a_hexenspecialdecs.cpp	2005-06-22 15:18:32.000000000 +0200
@@ -550,7 +550,7 @@
 
 FState ALeafSpawner::States[] =
 {
-	S_NORMAL (MAN1, 'A',   20, A_LeafSpawn				, &States[0])
+	S_NORMAL (TNT1, 'A',   20, A_LeafSpawn				, &States[0])
 };
 
 IMPLEMENT_ACTOR (ALeafSpawner, Hexen, 113, 0)
diff -durN 96/src/g_hexen/a_mageplayer.cpp 96x/src/g_hexen/a_mageplayer.cpp
--- 96/src/g_hexen/a_mageplayer.cpp	2004-12-24 15:55:30.000000000 +0100
+++ 96x/src/g_hexen/a_mageplayer.cpp	2005-06-22 15:18:32.000000000 +0200
@@ -57,11 +57,11 @@
 	S_NORMAL (MAGE, 'Y',	1, A_FreezeDeathChunks	    , &States[S_MPLAY_ICE+1]),
 
 #define S_PLAY_M_FDTH (S_MPLAY_ICE+2)
-	S_BRIGHT (FDTH, 'E',	5, NULL					    , &States[S_PLAY_M_FDTH+1]),
+	S_BRIGHT (FDTH, 'E',	5, A_FireScream			    , &States[S_PLAY_M_FDTH+1]),
 	S_BRIGHT (FDTH, 'F',	4, NULL					    , &States[S_PLAY_M_FDTH+2]),
 
 #define S_PLAY_FDTH (S_PLAY_M_FDTH+2)
-	S_BRIGHT (FDTH, 'G',	5, A_FireScream				, &States[S_PLAY_FDTH+1]),
+	S_BRIGHT (FDTH, 'G',	5, NULL						, &States[S_PLAY_FDTH+1]),
 	S_BRIGHT (FDTH, 'H',	4, A_PlayerScream 			, &States[S_PLAY_FDTH+2]),
 	S_BRIGHT (FDTH, 'I',	5, NULL 					, &States[S_PLAY_FDTH+3]),
 	S_BRIGHT (FDTH, 'J',	4, NULL 					, &States[S_PLAY_FDTH+4]),
diff -durN 96/src/g_hexen/a_serpent.cpp 96x/src/g_hexen/a_serpent.cpp
--- 96/src/g_hexen/a_serpent.cpp	2004-11-02 12:21:25.000000000 +0100
+++ 96x/src/g_hexen/a_serpent.cpp	2005-06-22 15:18:32.000000000 +0200
@@ -14,6 +14,8 @@
 static FRandom pr_serpentgibs ("SerpentGibs");
 static FRandom pr_delaygib ("DelayGib");
 
+void A_DoChase(AActor * actor, bool fastchase, FState * meleestate, FState * missilestate, bool playactive, bool nightmarefast);
+
 void A_SerpentChase (AActor *);
 void A_SerpentHumpDecide (AActor *);
 void A_SerpentDiveSound (AActor *);
@@ -399,128 +401,7 @@
 
 void A_SerpentChase (AActor *actor)
 {
-	int delta;
-	int oldX, oldY, oldFloor;
-
-	if (actor->reactiontime)
-	{
-		actor->reactiontime--;
-	}
-
-	// Modify target threshold
-	if (actor->threshold)
-	{
-		actor->threshold--;
-	}
-
-	if (gameskill == sk_nightmare)
-	{ // Monsters move faster in nightmare mode
-		actor->tics -= actor->tics/2;
-		if (actor->tics < 3)
-		{
-			actor->tics = 3;
-		}
-	}
-
-//
-// turn towards movement direction if not there yet
-//
-	if (actor->movedir < 8)
-	{
-		actor->angle &= (7<<29);
-		delta = actor->angle - (actor->movedir << 29);
-		if (delta > 0)
-		{
-			actor->angle -= ANG90/2;
-		}
-		else if (delta < 0)
-		{
-			actor->angle += ANG90/2;
-		}
-	}
-
-	if (!actor->target || !(actor->target->flags&MF_SHOOTABLE))
-	{ // look for a new target
-		if (P_LookForPlayers (actor, true))
-		{ // got a new target
-			return;
-		}
-		actor->SetState (actor->SpawnState);
-		return;
-	}
-
-//
-// don't attack twice in a row
-//
-	if (actor->flags & MF_JUSTATTACKED)
-	{
-		actor->flags &= ~MF_JUSTATTACKED;
-		if (gameskill != sk_nightmare)
-			P_NewChaseDir (actor);
-		return;
-	}
-
-//
-// check for melee attack
-//
-	if (actor->MeleeState && actor->CheckMeleeRange ())
-	{
-		if (actor->AttackSound)
-		{
-			S_SoundID (actor, CHAN_BODY, actor->AttackSound, 1, ATTN_NORM);
-		}
-		actor->SetState (actor->MeleeState);
-		return;
-	}
-
-//
-// possibly choose another target
-//
-	if ((multiplayer || actor->TIDtoHate)
-		&& !actor->threshold
-		&& !P_CheckSight (actor, actor->target, 0) )
-	{
-		bool lookForBetter = false;
-		BOOL gotNew;
-		if (actor->flags3 & MF3_NOSIGHTCHECK)
-		{
-			actor->flags3 &= ~MF3_NOSIGHTCHECK;
-			lookForBetter = true;
-		}
-		gotNew = P_LookForPlayers (actor, true);
-		if (lookForBetter)
-		{
-			actor->flags3 |= MF3_NOSIGHTCHECK;
-		}
-		if (gotNew)
-		{
-			return; 	// got a new target
-		}
-	}
-
-//
-// chase towards player
-//
-	oldX = actor->x;
-	oldY = actor->y;
-	oldFloor = actor->Sector->floorpic;
-	if (--actor->movecount<0 || !P_Move (actor))
-	{
-		P_NewChaseDir (actor);
-	}
-	if (actor->Sector->floorpic != oldFloor)
-	{
-		P_TryMove (actor, oldX, oldY, false);
-		P_NewChaseDir (actor);
-	}
-
-//
-// make active sound
-//
-	if (pr_serpentchase() < 3)
-	{
-		actor->PlayActiveSound ();
-	}
+	A_DoChase(actor, false, actor->MeleeState, NULL, false, true);
 }
 
 //============================================================================
@@ -618,110 +499,7 @@
 
 void A_SerpentWalk (AActor *actor)
 {
-	int delta;
-
-	if (actor->reactiontime)
-	{
-		actor->reactiontime--;
-	}
-
-	// Modify target threshold
-	if (actor->threshold)
-	{
-		actor->threshold--;
-	}
-
-	if (gameskill == sk_nightmare)
-	{ // Monsters move faster in nightmare mode
-		actor->tics -= actor->tics/2;
-		if (actor->tics < 3)
-		{
-			actor->tics = 3;
-		}
-	}
-
-//
-// turn towards movement direction if not there yet
-//
-	if (actor->movedir < 8)
-	{
-		actor->angle &= (7<<29);
-		delta = actor->angle-(actor->movedir << 29);
-		if(delta > 0)
-		{
-			actor->angle -= ANG90/2;
-		}
-		else if(delta < 0)
-		{
-			actor->angle += ANG90/2;
-		}
-	}
-
-	if (!actor->target || !(actor->target->flags&MF_SHOOTABLE))
-	{ // look for a new target
-		if (P_LookForPlayers(actor, true))
-		{ // got a new target
-			return;
-		}
-		actor->SetState (actor->SpawnState);
-		return;
-	}
-
-//
-// don't attack twice in a row
-//
-	if (actor->flags & MF_JUSTATTACKED)
-	{
-		actor->flags &= ~MF_JUSTATTACKED;
-		if (gameskill != sk_nightmare)
-			P_NewChaseDir (actor);
-		return;
-	}
-
-//
-// check for melee attack
-//
-	if (actor->MeleeState && actor->CheckMeleeRange ())
-	{
-		if (actor->AttackSound)
-		{
-			S_SoundID (actor, CHAN_BODY, actor->AttackSound, 1, ATTN_NORM);
-		}
-		actor->SetState (&ASerpent::States[S_SERPENT_ATK1]);
-		return;
-	}
-//
-// possibly choose another target
-//
-	if ((multiplayer || actor->TIDtoHate)
-		&& !actor->threshold
-		&& !P_CheckSight (actor, actor->target, 0) )
-	{
-		bool lookForBetter = false;
-		BOOL gotNew;
-		if (actor->flags3 & MF3_NOSIGHTCHECK)
-		{
-			actor->flags3 &= ~MF3_NOSIGHTCHECK;
-			lookForBetter = true;
-		}
-		gotNew = P_LookForPlayers (actor, true);
-		if (lookForBetter)
-		{
-			actor->flags3 |= MF3_NOSIGHTCHECK;
-		}
-		if (gotNew)
-		{
-			return; 	// got a new target
-		}
-	}
-
-//
-// chase towards player
-//
-	if (--actor->movecount<0 || !P_Move (actor))
-	{
-		P_NewChaseDir (actor);
-	}
+	A_DoChase(actor, false, &ASerpent::States[S_SERPENT_ATK1], NULL, true, true);
 }
 
 //============================================================================
diff -durN 96/src/g_level.cpp 96x/src/g_level.cpp
--- 96/src/g_level.cpp	2004-12-24 20:43:15.000000000 +0100
+++ 96x/src/g_level.cpp	2005-07-24 20:12:09.000000000 +0200
@@ -224,6 +224,15 @@
 	"redirect",
 	"strictmonsteractivation",
 	"laxmonsteractivation",
+	"additive_scrollers",
+	"cod_level",
+	"interpic",
+	"exitpic",
+	"enterpic",
+	"intermusic",
+	"airsupply",
+	"specialaction",
+	"keepfullinventory",
 	NULL
 };
 
@@ -246,7 +255,8 @@
 	MITYPE_MUSIC,
 	MITYPE_RELLIGHT,
 	MITYPE_CLRBYTES,
-	MITYPE_REDIRECT
+	MITYPE_REDIRECT,
+	MITYPE_SPECIALACTION
 };
 
 struct MapInfoHandler
@@ -319,6 +329,15 @@
 	{ MITYPE_REDIRECT,	lioffset(RedirectMap), 0 },
 	{ MITYPE_CLRFLAG,	LEVEL_LAXMONSTERACTIVATION, LEVEL_LAXACTIVATIONMAPINFO },
 	{ MITYPE_SETFLAG,	LEVEL_LAXMONSTERACTIVATION, LEVEL_LAXACTIVATIONMAPINFO },
+	{ MITYPE_SETFLAG,	LEVEL_ADDITIVE_SCROLLERS, 0 },
+	{ MITYPE_SETFLAG,	LEVEL_CAVERNS_OF_DARKNESS, 0 },
+	{ MITYPE_LUMPNAME,	lioffset(exitpic), 0 },
+	{ MITYPE_LUMPNAME,	lioffset(exitpic), 0 },
+	{ MITYPE_LUMPNAME,	lioffset(enterpic), 0 },
+	{ MITYPE_LUMPNAME,	lioffset(intermusic), 0 },
+	{ MITYPE_INT,		lioffset(airsupply), 0 },
+	{ MITYPE_SPECIALACTION, lioffset(specialactions), 0 },
+	{ MITYPE_SETFLAG,	LEVEL_KEEPFULLINVENTORY, 0 },
 };
 
 static const char *MapInfoClusterLevel[] =
@@ -393,6 +412,7 @@
 		// For maps without a BEHAVIOR, this will be cleared.
 		levelinfo->flags |= LEVEL_LAXMONSTERACTIVATION;
 	}
+	levelinfo->airsupply = 10*FRACUNIT;
 }
 
 //
@@ -839,6 +859,31 @@
 			*((BYTE *)(info + handler->data1)) = 0;
 			*((BYTE *)(info + handler->data2)) = 0;
 			break;
+
+		case MITYPE_SPECIALACTION:
+			{
+				int FindLineSpecial(const char * string);
+
+				FSpecialAction ** so= (FSpecialAction**)(info+handler->data1);
+				FSpecialAction * sa= new FSpecialAction;
+				sa->next= *so;
+				*so = sa;
+				SC_SetCMode(true);
+				SC_MustGetString();
+				sa->type = copystring(sc_String);
+				SC_CheckString(",");
+				SC_MustGetString();
+				strlwr(sc_String);
+				sa->action=FindLineSpecial(sc_String);
+				int j=0;
+				while (j<5 && SC_CheckString(","))
+				{
+					SC_MustGetNumber();
+					sa->args[j++]=sc_Number;
+				}
+				SC_SetCMode(false);
+			}
+			break;
 		}
 	}
 	if (levelinfo)
@@ -1012,6 +1057,8 @@
 
 void G_SetForEndGame (char *nextmap)
 {
+	if (!strncmp(nextmap, "enDSeQ",6)) return;	// If there is already an end sequence please leave it alone!!!
+
 	if (gameinfo.gametype == GAME_Strife)
 	{
 		SetEndSequence (nextmap, gameinfo.flags & GI_SHAREWARE ? END_BuyStrife : END_Strife);
@@ -1237,6 +1284,10 @@
 	{
 		respawnmonsters = TICRATE;
 	}
+	else 
+	{
+		respawnmonsters = 0;
+	}
 	// Monsters wait longer before respawning in Strife.
 	respawnmonsters *= gameinfo.gametype != GAME_Strife ? 12 : 16;
 
@@ -1776,11 +1827,13 @@
 void G_FinishTravel ()
 {
 	TThinkerIterator<APlayerPawn> it (STAT_TRAVELLING);
-	APlayerPawn *pawn, *pawndup;
+	APlayerPawn *pawn, *pawndup, *nextpawn;
 	AInventory *inv;
 
-	while ( (pawn = it.Next()) != NULL)
+	pawn = it.Next ();
+	while (pawn)
 	{
+		nextpawn = it.Next ();
 		pawn->ChangeStatNum (STAT_PLAYER);
 		pawndup = pawn->player->mo;
 		assert (pawn != pawndup);
@@ -1825,6 +1878,7 @@
 				inv->LinkToWorld ();
 			}
 		}
+		pawn = nextpawn;
 	}
 }
  
@@ -1864,6 +1918,7 @@
 		NormalLight.ChangeFade (level.fadeto);
 		*/
 	}
+	level.airsupply = info->airsupply ? info->airsupply*TICRATE : 10*TICRATE;
 	level.outsidefog = info->outsidefog;
 	level.WallVertLight = info->WallVertLight;
 	level.WallHorizLight = info->WallHorizLight;
diff -durN 96/src/g_level.h 96x/src/g_level.h
--- 96/src/g_level.h	2004-12-24 17:15:23.000000000 +0100
+++ 96x/src/g_level.h	2005-07-24 20:12:09.000000000 +0200
@@ -92,9 +92,22 @@
 #define LEVEL_LAXMONSTERACTIVATION	UCONST64(0x400000000)		// Monsters can open doors depending on the door speed
 #define LEVEL_LAXACTIVATIONMAPINFO	UCONST64(0x800000000)		// LEVEL_LAXMONSTERACTIVATION is not a default.
 
+#define LEVEL_ADDITIVE_SCROLLERS	UCONST64(0x1000000000)		// scrollers add their momentum instead of averaging it
+#define LEVEL_CAVERNS_OF_DARKNESS	UCONST64(0x2000000000)		// to translate the special sector types of CoD.
+
+#define LEVEL_KEEPFULLINVENTORY		UCONST64(0x4000000000)		// doesn't reduce the amount of inventory items to 1
+
 struct acsdefered_s;
 class FBehavior;
 
+struct FSpecialAction
+{
+	char * type;					// this is initialized before the actors...
+	unsigned char action;
+	unsigned short args[5];			// must allow 16 bit tags for 666 & 667!
+	FSpecialAction * next;
+};
+
 struct level_info_s
 {
 	char		mapname[9];
@@ -114,7 +127,7 @@
 	FCompressedMemFile	*snapshot;
 	DWORD		snapshotVer;
 	struct acsdefered_s *defered;
-	char		skypic2[8];
+	char		skypic2[9];
 	fixed_t		skyspeed1;
 	fixed_t		skyspeed2;
 	DWORD		fadeto;
@@ -124,11 +137,18 @@
 	float		gravity;
 	float		aircontrol;
 	int			WarpTrans;
+	int			airsupply;
 
 	// Redirection: If any player is carrying the specified item, then
 	// you go to the RedirectMap instead of this one.
 	const TypeInfo *RedirectType;
 	char		RedirectMap[9];
+
+	char		enterpic[9];
+	char		exitpic[9];
+	char		intermusic[9];
+
+	FSpecialAction * specialactions;
 };
 typedef struct level_info_s level_info_t;
 
@@ -184,6 +204,7 @@
 	float		gravity;
 	fixed_t		aircontrol;
 	fixed_t		airfriction;
+	int			airsupply;
 
 	FBehavior	*behavior;
 
diff -durN 96/src/g_raven/a_minotaur.cpp 96x/src/g_raven/a_minotaur.cpp
--- 96/src/g_raven/a_minotaur.cpp	2004-12-20 17:14:13.000000000 +0100
+++ 96x/src/g_raven/a_minotaur.cpp	2005-06-22 15:18:32.000000000 +0200
@@ -291,7 +291,7 @@
 	PROP_Alpha (OPAQUE/3)
 
 	PROP_SpawnHealth (2500)
-	PROP_FlagsClear (MF_DROPOFF)
+	PROP_FlagsClear (MF_DROPOFF|MF_COUNTKILL)
 	PROP_FlagsSet (MF_FRIENDLY)
 	PROP_Flags2Clear (MF2_BOSS)
 	PROP_Flags2Set (MF2_TELESTOMP)
diff -durN 96/src/g_shared/a_action.cpp 96x/src/g_shared/a_action.cpp
--- 96/src/g_shared/a_action.cpp	2004-11-18 16:40:44.000000000 +0100
+++ 96x/src/g_shared/a_action.cpp	2005-06-22 15:18:32.000000000 +0200
@@ -120,8 +120,9 @@
 {
 	int t = pr_freezedeath();
 	actor->tics = 75+t+pr_freezedeath();
-	actor->flags |= MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD;
+	actor->flags |= MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD|MF_ICECORPSE;
 	actor->flags2 |= MF2_PUSHABLE|MF2_TELESTOMP|MF2_PASSMOBJ|MF2_SLIDE;
+	actor->flags3 |= MF3_CRASHED;
 	actor->height = actor->GetDefault()->height;
 	S_Sound (actor, CHAN_BODY, "misc/freeze", 1, ATTN_NORM);
 
diff -durN 96/src/g_shared/a_artifacts.cpp 96x/src/g_shared/a_artifacts.cpp
--- 96/src/g_shared/a_artifacts.cpp	2004-12-18 19:00:11.000000000 +0100
+++ 96x/src/g_shared/a_artifacts.cpp	2005-06-22 15:18:32.000000000 +0200
@@ -487,7 +487,7 @@
 		newdamage = 0;
 		if (Owner->player != NULL)
 		{
-			Owner->player->air_finished = level.time + 10*TICRATE;
+			Owner->player->air_finished = level.time + level.airsupply;
 		}
 	}
 	else if (Inventory != NULL)
diff -durN 96/src/g_shared/a_decals.cpp 96x/src/g_shared/a_decals.cpp
--- 96/src/g_shared/a_decals.cpp	2004-02-05 15:15:56.000000000 +0100
+++ 96x/src/g_shared/a_decals.cpp	2005-07-01 20:06:31.000000000 +0200
@@ -461,7 +461,7 @@
 	LastImpact = this;
 }
 
-AImpactDecal *AImpactDecal::StaticCreate (const char *name, fixed_t x, fixed_t y, fixed_t z, side_t *wall)
+AImpactDecal *AImpactDecal::StaticCreate (const char *name, fixed_t x, fixed_t y, fixed_t z, side_t *wall, PalEntry color)
 {
 	if (cl_maxdecals > 0)
 	{
@@ -469,7 +469,7 @@
 
 		if (decal != NULL && (decal = decal->GetDecal()) != NULL)
 		{
-			return StaticCreate (decal, x, y, z, wall);
+			return StaticCreate (decal, x, y, z, wall, color);
 		}
 	}
 	return NULL;
@@ -606,7 +606,7 @@
 	}
 }
 
-AImpactDecal *AImpactDecal::StaticCreate (const FDecal *decal, fixed_t x, fixed_t y, fixed_t z, side_t *wall)
+AImpactDecal *AImpactDecal::StaticCreate (const FDecal *decal, fixed_t x, fixed_t y, fixed_t z, side_t *wall, PalEntry color)
 {
 	AImpactDecal *actor = NULL;
 	if (decal != NULL && cl_maxdecals > 0 &&
@@ -636,6 +636,7 @@
 		}
 
 		decal->ApplyToActor (actor);
+		if (color!=0) actor->SetShade(color.r, color.g, color.b);
 
 		if (!cl_spreaddecals || actor->picnum == 0xffff)
 			return actor;
@@ -690,6 +691,7 @@
 	{
 		actor->StickToWall (wall);
 		decal->ApplyToActor (actor);
+		actor->alphacolor = alphacolor;
 		actor->renderflags = (actor->renderflags & RF_DECALMASK) |
 							 (this->renderflags & ~RF_DECALMASK);
 	}
diff -durN 96/src/g_shared/a_keys.cpp 96x/src/g_shared/a_keys.cpp
--- 96/src/g_shared/a_keys.cpp	2004-11-29 20:16:08.000000000 +0100
+++ 96x/src/g_shared/a_keys.cpp	2005-06-23 13:41:39.000000000 +0200
@@ -5,214 +5,416 @@
 #include "d_player.h"
 #include "c_console.h"
 #include "s_sound.h"
+#include "sc_man.h"
+#include "v_palette.h"
+#include "w_wad.h"
 
-// Special "keys"
-enum
+
+
+struct OneKey
 {
-	AnyKey = 100,
-	AllKeys = 101,
-	ThatDoesntWork = 102,
-	OnlyInRetail = 103,
-	All3Keys = AllKeys + 128
+	const TypeInfo * key;
+	int count;
+
+	bool check(AActor * owner)
+	{
+		return !!owner->FindInventory(key);
+	}
 };
 
-static TArray<const TypeInfo *> KeyTypes;
+struct Keygroup
+{
+	TArray<OneKey> anykeylist;
+
+	bool check(AActor * owner)
+	{
+		for(int i=0;i<anykeylist.Size();i++)
+		{
+			if (anykeylist[i].check(owner)) return true;
+		}
+		return false;
+	}
+};
+
+struct Lock
+{
+	TArray<Keygroup *> keylist;
+	char * message;
+	char * remotemsg;
+	int locksound;
+	int	rgb;
+
+	Lock()
+	{
+		message=remotemsg=NULL;
+		rgb=0;
+	}
+
+	~Lock()
+	{
+		for(int i=0;i<keylist.Size();i++) delete keylist[i];
+		keylist.Clear();
+		if (message) delete [] message;
+		if (remotemsg) delete [] remotemsg;
+	}
+
+	bool check(AActor * owner)
+	{
+		// An empty key list means that any key will do
+		if (!keylist.Size())
+		{
+			for (AInventory * item = owner->Inventory; item != NULL; item = item->Inventory)
+			{
+				if (item->IsKindOf (RUNTIME_CLASS(AKey)))
+				{
+					return true;
+				}
+			}
+		}
+		else for(int i=0;i<keylist.Size();i++)
+		{
+			if (!keylist[i]->check(owner)) return false;
+		}
+		return true;
+	}
+};
+
+
+static Lock * locks[256];		// all valid locks
+static bool keysdone=false;		// have the locks been initialized?
+static int currentnumber;		// number to be assigned to next key
+static bool ignorekey;			// set to true when the current lock is not being used
+
 
+static const char * keywords_lock[]={
+	"ANY",
+	"MESSAGE",
+	"REMOTEMESSAGE",
+	"MAPCOLOR",
+	"LOCKEDSOUND",
+	NULL
+};
+
+//===========================================================================
 //
-// Keys
 //
+//===========================================================================
+
+static void AddOneKey(Keygroup * keygroup, const TypeInfo * mi)
+{
+	if (mi)
+	{
+		// Any inventory item can be used to unlock a door
+		if (mi->IsDescendantOf(RUNTIME_CLASS(AInventory)))
+		{
+			OneKey k = {mi,1};
+			keygroup->anykeylist.Push (k);
+
+			//... but only keys get key numbers!
+			if (mi->IsDescendantOf(RUNTIME_CLASS(AKey)))
+			{
+				if (!ignorekey &&
+					static_cast<AKey*>(GetDefaultByType(mi))->KeyNumber == 0)
+				{
+					static_cast<AKey*>(GetDefaultByType(mi))->KeyNumber=++currentnumber;
+				}
+			}
+		}
+		else
+		{
+			SC_ScriptError("'%s' is not an inventory item", sc_String);
+		}
+	}
+	else
+	{
+		SC_ScriptError("Unknown item '%s'", sc_String);
+	}
+}
 
-// [RH] P_CheckKeys
-//
-//	Returns true if the player has the desired key,
-//	false otherwise.
 
 //===========================================================================
 //
-// FindValidKeys
-//
-// Records all the keys valid for the current game type. This is called the
-// first time P_CheckKeys needs to print a messsage for a player who needs a
-// key.
 //
 //===========================================================================
 
-static void FindValidKeys ()
+static Keygroup * ParseKeygroup()
 {
-	const TypeInfo *basetype;
-	int i;
+	Keygroup * keygroup;
+	const TypeInfo * mi;
 
-	switch (gameinfo.gametype)
+	SC_MustGetStringName("{");
+	keygroup=new Keygroup;
+	while (!SC_CheckString("}"))
 	{
-	default:
-	case GAME_Doom:		basetype = RUNTIME_CLASS(ADoomKey);		break;
-	case GAME_Heretic:	basetype = RUNTIME_CLASS(AHereticKey);	break;
-	case GAME_Hexen:	basetype = RUNTIME_CLASS(AHexenKey);	break;
-	case GAME_Strife:	basetype = RUNTIME_CLASS(AStrifeKey);	break;
+		SC_MustGetString();
+		mi=TypeInfo::FindType(sc_String);
+		AddOneKey(keygroup, mi);
 	}
+	if (keygroup->anykeylist.Size()==0)
+	{
+		delete keygroup;
+		return NULL;
+	}
+	keygroup->anykeylist.ShrinkToFit();
+	return keygroup;
+}
 
-	for (i = 0; i < TypeInfo::m_NumTypes; ++i)
+//===========================================================================
+//
+//
+//===========================================================================
+
+static void PrintMessage(const char * string)
+{
+	char temp[512];
+	char name[100];
+	if (string)
 	{
-		if (TypeInfo::m_Types[i]->IsDescendantOf (basetype))
+		if (strchr(string,'$'))
 		{
-			KeyTypes.Push (TypeInfo::m_Types[i]);
+			// The message or part of it is from the LANGUAGE lump
+			int part1=strcspn(string,"$");
+			strncpy(temp,string, part1);
+			temp[part1]=0;
+			int part2=strcspn(string+part1+1,"$");
+			strncpy(name,string+part1+1, part2);
+			name[part2]=0;
+			int index=GStrings.FindString(name);
+			if (index==-1) Printf(PRINT_HIGH, "Unknown string name %s",name); 
+			else strcat(temp,GStrings(index));
+			if (string[part1+1+part2]=='$') strcat(temp, string+part1+part2+2);
+			string=temp;
 		}
+		C_MidPrint(string);
 	}
 }
 
 //===========================================================================
 //
-// P_CheckKeys
-//
-// Returns true if the actor has the required key. If not, a message is
-// shown if the actor is also the consoleplayer's camarea, and false is
-// returned.
 //
 //===========================================================================
 
-bool P_CheckKeys (AActor *owner, int keynum, bool remote)
+static void ParseLock()
 {
-	const char *failtext;
-	const char *failsound;
-	DWORD keymask;
-	AInventory *item;
+	int i,r,g,b;
+	int keynum;
+	Lock sink;
+	Lock * lock=&sink;
+	Keygroup * keygroup;
+	const TypeInfo * mi;
 
-	failtext = NULL;
-	failsound = NULL;
+	SC_MustGetNumber();
+	keynum=sc_Number;
 
-	switch (keynum)
+	SC_MustGetString();
+	if (SC_Compare("DOOM"))
 	{
-	case 0:
-		// Key 0 is like a psuedo-key. Everything pretends to have it, so it
-		// can act as a no-key-needed flag.
-		return true;
-
-	case OnlyInRetail:
-		failtext = "THIS AREA IS ONLY AVAILABLE IN THE RETAIL VERSION OF STRIFE";
-		break;
+		if (gameinfo.gametype != GAME_Doom) keynum=-1;
+	}
+	else if (SC_Compare("HERETIC"))
+	{
+		if (gameinfo.gametype != GAME_Heretic) keynum=-1;
+	}
+	else if (SC_Compare("HEXEN"))
+	{
+		if (gameinfo.gametype != GAME_Hexen) keynum=-1;
+	}
+	else if (SC_Compare("STRIFE"))
+	{
+		if (gameinfo.gametype != GAME_Strife) keynum=-1;
+	}
+	else SC_UnGet();
 
-	case ThatDoesntWork:
-		failtext = "That doesn't seem to work";
-		break;
+	ignorekey=true;
+	if (keynum>0 && keynum<255) 
+	{
+		lock=new Lock;
+		if (locks[keynum]) delete locks[keynum];
+		locks[keynum]=lock;
+		locks[keynum]->locksound = S_FindSound("misc/keytry");
+		ignorekey=false;
+	}
+	else if (keynum!=-1)
+	{
+		SC_ScriptError("Lock index %d out of range", keynum);
+	}
 
-	case AllKeys:
-	case All3Keys:
-		if (gameinfo.gametype == GAME_Strife)
+	SC_MustGetStringName("{");
+	while (!SC_CheckString("}"))
+	{
+		SC_MustGetString();
+		switch(i=SC_MatchString(keywords_lock))
 		{
-			// Strife has 26 (actually 27) different keys, so I am not
-			// going to implement the AllKeys check for Strife.
-			failtext = "That doesn't seem to work";
+		case 0:	// Any
+			keygroup=ParseKeygroup();
+			if (keygroup) lock->keylist.Push(keygroup);
 			break;
-		}
-		// Intentional fall-through
 
-	default:
-		keymask = 0;
-
-		// See if the owner is holding a matching key.
-		for (item = owner->Inventory; item != NULL; item = item->Inventory)
-		{
-			if (item->IsKindOf (RUNTIME_CLASS(AKey)))
-			{
-				AKey *key = static_cast<AKey *> (item);
+		case 1:	// message
+			SC_MustGetString();
+			lock->message=copystring(sc_String);
+			break;
 
-				if (keynum == AnyKey)
-				{
-					return true;
-				}
-				else if ((keynum & 0x7f) == AllKeys)
-				{
-					keymask |= 1 << (key->KeyNumber - 1);
-				}
-				else if (key->KeyNumber == keynum || key->AltKeyNumber == keynum)
-				{
-					return true;
-				}
-			}
-		}
+		case 2: // remotemsg
+			SC_MustGetString();
+			lock->remotemsg=copystring(sc_String);
+			break;
 
-		if ((keynum & 0x7f) == AllKeys)
-		{
-			// The check for holding "all keys" is only made against the
-			// standard keys and not any custom user-added keys.
-			switch (gameinfo.gametype)
-			{
-			case GAME_Doom:
-				if (keynum & 0x80)
-				{ // Cards and skulls are treated the same
-					keymask |= keymask >> 3;
-					keymask |= keymask << 3;
-				}
-				if ((keymask & (1|2|4|8|16|32)) == (1|2|4|8|16|32))
-				{
-					return true;
-				}
-				failtext = (keynum & 0x80) ? GStrings(PD_ALL3) : GStrings(PD_ALL6);
-				break;
+		case 3:	// mapcolor
+			SC_MustGetNumber();
+			r=sc_Number;
+			SC_MustGetNumber();
+			g=sc_Number;
+			SC_MustGetNumber();
+			b=sc_Number;
+			lock->rgb=MAKERGB(r,g,b);
+			break;
 
-			case GAME_Heretic:
-				if ((keymask & (1|2|4)) == (1|2|4))
-				{
-					return true;
-				}
-				failtext = GStrings(PD_ALL3);
-				break;
+		case 4:	// locksound
+			SC_MustGetString();
+			lock->locksound = S_FindSound(sc_String);
+			break;
 
-			case GAME_Hexen:
-				if ((keymask & ((1<<11)-1)) == ((1<<11)-1))
+		default:
+			mi=TypeInfo::FindType(sc_String);
+			if (mi) 
+			{
+				keygroup=new Keygroup;
+				AddOneKey(keygroup, mi);
+				if (keygroup) 
 				{
-					return true;
+					keygroup->anykeylist.ShrinkToFit();
+					lock->keylist.Push(keygroup);
 				}
-				failtext = "You need all the keys";
-				break;
-				
-			default:
-				// The other enumerations won't reach this switch, but make GCC happy.
-				failtext = "That doesn't seem to work";
-				break;
 			}
+			break;
 		}
-		break;
 	}
+	// copy the messages if the other one does not exist
+	if (!lock->remotemsg && lock->message) lock->remotemsg=strdup(lock->message);
+	if (!lock->message && lock->remotemsg) lock->message=strdup(lock->remotemsg);
+	lock->keylist.ShrinkToFit();
+}
 
-	// If we get here, that means the actor isn't holding an appropriate key.
-	// If a failtext isn't already set, locate a key that would have satisfied
-	// the lock and grab text from it.
+//===========================================================================
+//
+// Clears all key numbers so the parser can assign its own ones
+// This ensures that only valid keys are considered by the key cheats
+//
+//===========================================================================
 
-	if (owner->CheckLocalView (consoleplayer))
+static void ClearLocks()
+{
+	int i;
+	for(i=0;i<TypeInfo::m_NumTypes;i++)
 	{
-		if (KeyTypes.Size() == 0)
+		if (TypeInfo::m_Types[i]->IsDescendantOf(RUNTIME_CLASS(AKey)))
 		{
-			FindValidKeys ();
+			static_cast<AKey*>(GetDefaultByType(TypeInfo::m_Types[i]))->KeyNumber=0;
 		}
+	}
+	for(i=0;i<256;i++)
+	{
+		if (locks[i]!=NULL) 
+		{
+			delete locks[i];
+			locks[i]=NULL;
+		}
+	}
+	currentnumber=0;
+	keysdone=false;
+}
 
-		for (size_t i = 0; i < KeyTypes.Size(); ++i)
+//===========================================================================
+//
+//
+//===========================================================================
+
+void P_InitKeyMessages()
+{
+	int lastlump, lump;
+
+	lastlump = 0;
+
+	ClearLocks();
+	while ((lump = Wads.FindLump ("LOCKDEFS", &lastlump)) != -1)
+	{
+		SC_OpenLumpNum (lump, "LOCKDEFS");
+		while (SC_GetString ())
 		{
-			AKey *keydef = (AKey *)GetDefaultByType (KeyTypes[i]);
-			if (keydef->KeyNumber == keynum || keydef->AltKeyNumber == keynum)
+			if (SC_Compare("LOCK")) 
 			{
-				AKey *tempkey = static_cast<AKey*>(Spawn (KeyTypes[i], 0,0,0));
-				failtext = tempkey->NeedKeyMessage (remote, keynum);
-				failsound = tempkey->NeedKeySound ();
-				tempkey->Destroy ();
-				break;
+				ParseLock();
 			}
+			else if (SC_Compare("CLEARLOCKS"))
+			{
+				// clear all existing lock defintions and key numbers
+				ClearLocks();
+			}
+			else 
+				SC_ScriptError("Unknown command %s in LockDef", sc_String);
 		}
-		if (failtext == NULL)
-		{
-			failtext = "You don't have the key";
-		}
-		if (failsound == NULL)
-		{
-			failsound = "misc/keytry";
-		}
-		C_MidPrint (failtext);
-		S_Sound (owner, CHAN_VOICE, failsound, 1, ATTN_NORM);
+		SC_Close();
+	}
+	keysdone=true;
+}
+
+
+//===========================================================================
+//
+// P_CheckKeys
+//
+// Returns true if the actor has the required key. If not, a message is
+// shown if the actor is also the consoleplayer's camarea, and false is
+// returned.
+//
+//===========================================================================
+
+bool P_CheckKeys (AActor *owner, int keynum, bool remote)
+{
+	const char *failtext = NULL;
+	int failsound = 0;
+
+	failtext = NULL;
+	failsound = NULL;
+
+	if (keynum<=0 || keynum>255) return true;
+	// Just a safety precaution. The messages should have been initialized upon game start.
+	if (!keysdone) P_InitKeyMessages();
+
+	if (!locks[keynum]) 
+	{
+		if (keynum==103 && gameinfo.gametype == GAME_Strife)
+			failtext = "THIS AREA IS ONLY AVAILABLE IN THE RETAIL VERSION OF STRIFE";
+		else
+			failtext = "That doesn't seem to work";
+
+		failsound = S_FindSound("misc/keytry");
+	}
+	else
+	{
+		if (locks[keynum]->check(owner)) return true;
+		failtext = remote? locks[keynum]->remotemsg : locks[keynum]->message;
+		failsound = locks[keynum]->locksound;
+	}
+
+	// If we get here, that means the actor isn't holding an appropriate key.
+
+	if (owner == players[consoleplayer].camera)
+	{
+		PrintMessage(failtext);
+		S_SoundID (owner, CHAN_VOICE, failsound, 1, ATTN_NORM);
 	}
 
 	return false;
 }
 
+//==========================================================================
+//
+// AKey implementation
+//
+//==========================================================================
+
 IMPLEMENT_STATELESS_ACTOR (AKey, Any, -1, 0)
  PROP_Inventory_FlagsSet (IF_INTERHUBSTRIP)
  PROP_Inventory_PickupSound ("misc/k_pkup")
@@ -243,6 +445,12 @@
 	return !!multiplayer;
 }
 
+//==========================================================================
+//
+// These 2 methods are practically obsolete... ;)
+//
+//==========================================================================
+
 const char *AKey::NeedKeyMessage (bool remote, int keynum)
 {
 	return "You don't have the key";
@@ -252,3 +460,38 @@
 {
 	return "misc/keytry";
 }
+
+
+//==========================================================================
+//
+// These functions can be used to get color information for
+// automap display of keys and locked doors
+//
+//==========================================================================
+
+int P_GetMapColorForLock(int lock)
+{
+	if (lock>0 && lock<256)
+	{
+		if (locks[lock]) return locks[lock]->rgb;
+	}
+	return 0;
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+int P_GetMapColorForKey(AInventory * key)
+{
+	int i;
+
+	for(i=0;i<256;i++)
+	{
+		if (locks[i] && locks[i]->check(key)) return locks[i]->rgb;
+	}
+	return 0;
+}
+
diff -durN 96/src/g_shared/a_keys.h 96x/src/g_shared/a_keys.h
--- 96/src/g_shared/a_keys.h	2004-11-29 20:16:08.000000000 +0100
+++ 96x/src/g_shared/a_keys.h	2005-06-22 15:18:32.000000000 +0200
@@ -41,5 +41,6 @@
 };
 
 bool P_CheckKeys (AActor *owner, int keynum, bool remote);
+void P_InitKeyMessages();
 
 #endif
diff -durN 96/src/g_shared/a_lightning.cpp 96x/src/g_shared/a_lightning.cpp
--- 96/src/g_shared/a_lightning.cpp	2004-11-18 21:35:54.000000000 +0100
+++ 96x/src/g_shared/a_lightning.cpp	2005-06-22 15:18:32.000000000 +0200
@@ -109,18 +109,20 @@
 	tempSec = sectors;
 	for (i = numsectors, j = 0; i > 0; --i, ++j, ++tempSec)
 	{
+		// allow combination of the lightning sector specials with bit masks
+		int special = (tempSec->special&0xff);
 		if (tempSec->ceilingpic == skyflatnum
-			|| tempSec->special == Light_IndoorLightning1
-			|| tempSec->special == Light_IndoorLightning2
-			|| tempSec->special == Light_OutdoorLightning)
+			|| special == Light_IndoorLightning1
+			|| special == Light_IndoorLightning2
+			|| special == Light_OutdoorLightning)
 		{
 			LightningLightLevels[j] = tempSec->lightlevel;
 			LightningLightLevels[numsectors+(j>>3)] |= 1<<(j&7);
-			if (tempSec->special == Light_IndoorLightning1)
+			if (special == Light_IndoorLightning1)
 			{
 				tempSec->lightlevel = MIN<int> (tempSec->lightlevel+64, flashLight);
 			}
-			else if (tempSec->special == Light_IndoorLightning2)
+			else if (special == Light_IndoorLightning2)
 			{
 				tempSec->lightlevel = MIN<int> (tempSec->lightlevel+32, flashLight);
 			}
diff -durN 96/src/g_shared/a_movingcamera.cpp 96x/src/g_shared/a_movingcamera.cpp
--- 96/src/g_shared/a_movingcamera.cpp	2004-12-06 12:04:05.000000000 +0100
+++ 96x/src/g_shared/a_movingcamera.cpp	2005-06-22 15:18:32.000000000 +0200
@@ -535,6 +535,11 @@
 	{
 		Printf ("ActorMover %d: Can't find target %d\n", tid, args[3]);
 	}
+	else
+	{
+		special1 = tracer->flags;
+		special2 = tracer->flags2;
+	}
 }
 
 bool AActorMover::Interpolate ()
diff -durN 96/src/g_shared/a_pickups.cpp 96x/src/g_shared/a_pickups.cpp
--- 96/src/g_shared/a_pickups.cpp	2004-12-24 16:15:42.000000000 +0100
+++ 96x/src/g_shared/a_pickups.cpp	2005-06-22 15:18:32.000000000 +0200
@@ -447,6 +447,10 @@
 	{
 		arc << AR_SOUNDW(PickupSound);
 	}
+	if (SaveVersion >= 228)
+	{
+		arc << UseState << PickupState << DropState;
+	}
 }
 
 //===========================================================================
@@ -461,7 +465,7 @@
 
 bool AInventory::SpecialDropAction (AActor *dropper)
 {
-	return false;
+	return CallStateChain (dropper, DropState);
 }
 
 //===========================================================================
@@ -751,6 +755,19 @@
 
 //===========================================================================
 //
+// AInventory :: DoUse
+//
+//===========================================================================
+
+bool AInventory::DoUse (bool pickup)
+{
+	bool useresult = CallStateChain (Owner, UseState);
+	useresult |= Use(pickup);
+	return useresult;
+}
+
+//===========================================================================
+//
 // AInventory :: Hide
 //
 // Hides this actor until it's time to respawn again. 
@@ -794,7 +811,7 @@
 		toucher = toucher->player->mo;
 	}
 
-	if (!TryPickup (toucher))
+	if (!DoTryPickup (toucher))
 		return;
 
 	if (!(ItemFlags & IF_QUIET))
@@ -1059,7 +1076,7 @@
 		}
 		// The item is placed in the inventory just long enough to be used.
 		toucher->AddInventory (this);
-		bool usegood = Use (true);
+		bool usegood = DoUse (true);
 		toucher->RemoveInventory (this);
 
 		if (usegood || (ItemFlags & IF_ALWAYSPICKUP))
@@ -1083,7 +1100,7 @@
 		copy->AttachToOwner (toucher);
 		if (ItemFlags & IF_AUTOACTIVATE)
 		{
-			if (copy->Use (true))
+			if (copy->DoUse (true))
 			{
 				if (--copy->Amount <= 0)
 				{
@@ -1098,6 +1115,20 @@
 
 //===========================================================================
 //
+// AInventory :: DoUse
+//
+//===========================================================================
+
+bool AInventory::DoTryPickup (AActor *toucher)
+{
+	bool useresult = CallStateChain (toucher, PickupState);
+	useresult |= TryPickup(toucher);
+	return useresult;
+}
+
+
+//===========================================================================
+//
 // CCMD printinv
 //
 // Prints the console player's current inventory.
diff -durN 96/src/g_shared/a_pickups.h 96x/src/g_shared/a_pickups.h
--- 96/src/g_shared/a_pickups.h	2004-12-23 21:37:23.000000000 +0100
+++ 96x/src/g_shared/a_pickups.h	2005-06-22 15:18:32.000000000 +0200
@@ -110,6 +110,7 @@
 	virtual void DoPickupSpecial (AActor *toucher);
 	virtual bool SpecialDropAction (AActor *dropper);
 	virtual bool DrawPowerup (int x, int y);
+	bool DoTryPickup (AActor *toucher);
 
 	virtual const char *PickupMessage ();
 	virtual void PlayPickupSound (AActor *toucher);
@@ -124,6 +125,9 @@
 	int RespawnTics;			// Tics from pickup time to respawn time
 	int Icon;					// Icon to show on status bar or HUD
 	int DropTime;				// Countdown after dropping
+	FState * UseState;			// state sequence that is executed when the item is being used
+	FState * PickupState;		// state sequence that is executed when the item is being picked up or given by other means
+	FState * DropState;			// state sequence that is executed when the item is being dropped by a monster
 
 	DWORD ItemFlags;
 
@@ -138,7 +142,8 @@
 	virtual bool GoAway ();
 	virtual void GoAwayAndDie ();
 	virtual bool HandlePickup (AInventory *item);
-	virtual bool Use (bool pickup);
+
+	bool DoUse (bool pickup);
 
 	virtual void AbsorbDamage (int damage, int damageType, int &newdamage);
 	virtual void AlterWeaponSprite (vissprite_t *vis);
@@ -146,6 +151,8 @@
 	virtual PalEntry GetBlend ();
 
 private:
+	virtual bool Use (bool pickup);	// Use DoUse instead to call the use state as well!
+
 	static int StaticLastMessageTic;
 	static const char *StaticLastMessage;
 };
diff -durN 96/src/g_shared/a_secrettrigger.cpp 96x/src/g_shared/a_secrettrigger.cpp
--- 96/src/g_shared/a_secrettrigger.cpp	2004-11-24 14:18:29.000000000 +0100
+++ 96x/src/g_shared/a_secrettrigger.cpp	2005-07-24 20:12:09.000000000 +0200
@@ -39,6 +39,8 @@
 #include "s_sound.h"
 #include "d_player.h"
 
+EXTERN_CVAR(String, secretmessage)
+
 class ASecretTrigger : public AActor
 {
 	DECLARE_STATELESS_ACTOR (ASecretTrigger, AActor)
@@ -63,7 +65,7 @@
 	{
 		if (args[0] <= 1)
 		{
-			C_MidPrint ("A secret is revealed!");
+			C_MidPrint (secretmessage);
 		}
 		if (args[0] == 0 || args[0] == 2)
 		{
@@ -71,6 +73,7 @@
 		}
 	}
 	level.found_secrets++;
+	if (activator->player) activator->player->secretcount++;
 	Destroy ();
 }
 
diff -durN 96/src/g_shared/a_sharedglobal.h 96x/src/g_shared/a_sharedglobal.h
--- 96/src/g_shared/a_sharedglobal.h	2004-08-10 13:43:19.000000000 +0200
+++ 96x/src/g_shared/a_sharedglobal.h	2005-06-22 15:18:32.000000000 +0200
@@ -74,8 +74,8 @@
 {
 	DECLARE_STATELESS_ACTOR (AImpactDecal, ADecal)
 public:
-	static AImpactDecal *StaticCreate (const char *name, fixed_t x, fixed_t y, fixed_t z, side_s *wall);
-	static AImpactDecal *StaticCreate (const FDecal *decal, fixed_t x, fixed_t y, fixed_t z, side_s *wall);
+	static AImpactDecal *StaticCreate (const char *name, fixed_t x, fixed_t y, fixed_t z, side_s *wall, PalEntry color=0);
+	static AImpactDecal *StaticCreate (const FDecal *decal, fixed_t x, fixed_t y, fixed_t z, side_s *wall, PalEntry color=0);
 
 	void BeginPlay ();
 	void Destroy ();
diff -durN 96/src/g_shared/a_weaponpiece.cpp 96x/src/g_shared/a_weaponpiece.cpp
--- 96/src/g_shared/a_weaponpiece.cpp	1970-01-01 01:00:00.000000000 +0100
+++ 96x/src/g_shared/a_weaponpiece.cpp	2005-07-11 18:10:00.000000000 +0200
@@ -0,0 +1,169 @@
+#include "a_pickups.h"
+#include "a_weaponpiece.h"
+#include "doomstat.h"
+
+
+
+// an internal class to hold the information for player class independent weapon piece handling
+class AWeaponHolder : public AInventory
+{
+	DECLARE_ACTOR(AWeaponHolder, AInventory)
+
+public:
+	int PieceMask;
+	const TypeInfo * PieceWeapon;
+
+	void Serialize (FArchive &arc)
+	{
+		arc << PieceMask ;
+		if (arc.IsStoring()) arc.UserWriteClass(PieceWeapon);
+		else arc.UserWriteClass(PieceWeapon);
+	}
+};
+
+
+
+IMPLEMENT_STATELESS_ACTOR (AWeaponHolder, Any, -1, 0)
+	PROP_Flags (MF_NOBLOCKMAP|MF_NOSECTOR)
+	PROP_Inventory_FlagsSet (IF_UNDROPPABLE)
+END_DEFAULTS
+
+IMPLEMENT_STATELESS_ACTOR (AWeaponPiece, Any, -1, 0)
+END_DEFAULTS
+
+
+void AWeaponPiece::Serialize (FArchive &arc)
+{
+	Super::Serialize (arc);
+	if (arc.IsStoring()) arc.UserWriteClass(WeaponClass); else arc.UserReadClass(WeaponClass);         
+	arc << FullWeapon;
+	arc << PieceValue;
+}
+
+//==========================================================================
+//
+// TryPickupWeaponPiece
+//
+//==========================================================================
+
+bool AWeaponPiece::TryPickup (AActor *toucher)
+{
+	AInventory * inv;
+	AWeaponHolder * hold=NULL;
+	bool shouldStay = PrivateShouldStay ();
+	int gaveAmmo;
+	AWeapon * Defaults=(AWeapon*)GetDefaultByType(WeaponClass);
+
+	FullWeapon=NULL;
+	for(inv=toucher->Inventory;inv;inv=inv->Inventory)
+	{
+		if (inv->IsKindOf(RUNTIME_CLASS(AWeaponHolder)))
+		{
+			hold=static_cast<AWeaponHolder*>(inv);
+
+			if (hold->PieceWeapon==WeaponClass) break;
+			hold=NULL;
+		}
+	}
+	if (!hold)
+	{
+		hold=static_cast<AWeaponHolder*>(Spawn(RUNTIME_CLASS(AWeaponHolder), 0, 0, 0));
+		hold->BecomeItem();
+		hold->AttachToOwner(toucher);
+		hold->PieceMask=0;
+		hold->PieceWeapon=WeaponClass;
+	}
+
+
+	if (shouldStay)
+	{ 
+		// Cooperative net-game
+		if (hold->PieceMask & PieceValue)
+		{ 
+			// Already has the piece
+			return false;
+		}
+		toucher->GiveAmmo (Defaults->AmmoType1, Defaults->AmmoGive1);
+		toucher->GiveAmmo (Defaults->AmmoType2, Defaults->AmmoGive2);
+	}
+	else
+	{ // Deathmatch or singleplayer game
+		gaveAmmo = toucher->GiveAmmo (Defaults->AmmoType1, Defaults->AmmoGive1) +
+					toucher->GiveAmmo (Defaults->AmmoType2, Defaults->AmmoGive2);
+		
+		if (hold->PieceMask & PieceValue)
+		{ 
+			// Already has the piece, check if mana needed
+			if (!gaveAmmo) return false;
+			GoAwayAndDie();
+			return true;
+		}
+	}
+
+	hold->PieceMask |= PieceValue;
+
+	// Check if  weapon assembled
+	if (hold->PieceMask== (1<<Defaults->health)-1)
+	{
+		if (!toucher->FindInventory (WeaponClass))
+		{
+			FullWeapon= static_cast<AWeapon*>(Spawn(WeaponClass, 0, 0, 0));
+			
+			// The weapon itself should not give more ammo to the player!
+			FullWeapon->AmmoGive1=0;
+			FullWeapon->AmmoGive2=0;
+			FullWeapon->AttachToOwner(toucher);
+			FullWeapon->AmmoGive1=Defaults->AmmoGive1;
+			FullWeapon->AmmoGive2=Defaults->AmmoGive2;
+		}
+	}
+	GoAwayAndDie();
+	return true;
+}
+
+bool AWeaponPiece::ShouldStay ()
+{
+	return PrivateShouldStay ();
+}
+
+bool AWeaponPiece::PrivateShouldStay ()
+{
+	// We want a weapon piece to behave like a weapon, so follow the exact
+	// same logic as weapons when deciding whether or not to stay.
+	if (((multiplayer &&
+		(!deathmatch && !alwaysapplydmflags)) || (dmflags & DF_WEAPONS_STAY)) &&
+		!(flags&MF_DROPPED))
+	{
+		return true;
+	}
+	return false;
+}
+
+//===========================================================================
+//
+// PickupMessage
+//
+// Returns the message to print when this actor is picked up.
+//
+//===========================================================================
+
+const char *AWeaponPiece::PickupMessage ()
+{
+	if (FullWeapon) return FullWeapon->PickupMessage();
+	return Super::PickupMessage();
+}
+
+//===========================================================================
+//
+// DoPlayPickupSound
+//
+// Plays a sound when this actor is picked up.
+//
+//===========================================================================
+
+void AWeaponPiece::PlayPickupSound (AActor *toucher)
+{
+	if (FullWeapon) FullWeapon->PlayPickupSound(toucher);
+	else Super::PlayPickupSound(toucher);
+}
+
diff -durN 96/src/g_shared/a_weaponpiece.h 96x/src/g_shared/a_weaponpiece.h
--- 96/src/g_shared/a_weaponpiece.h	1970-01-01 01:00:00.000000000 +0100
+++ 96x/src/g_shared/a_weaponpiece.h	2005-07-11 18:11:25.000000000 +0200
@@ -0,0 +1,17 @@
+
+class AWeaponPiece : public AInventory
+{
+	DECLARE_CLASS (AWeaponPiece, AInventory)
+protected:
+	bool PrivateShouldStay ();
+public:
+	void Serialize (FArchive &arc);
+	bool TryPickup (AActor *toucher);
+	bool ShouldStay ();
+	virtual const char *PickupMessage ();
+	virtual void PlayPickupSound (AActor *toucher);
+
+	int PieceValue;
+	const TypeInfo * WeaponClass;
+	AWeapon * FullWeapon;
+};
diff -durN 96/src/g_shared/a_weapons.cpp 96x/src/g_shared/a_weapons.cpp
--- 96/src/g_shared/a_weapons.cpp	2004-11-29 20:16:08.000000000 +0100
+++ 96x/src/g_shared/a_weapons.cpp	2005-06-22 15:18:32.000000000 +0200
@@ -857,6 +857,11 @@
 			strcpy (fullSection, "Heretic");
 			tackOn = fullSection + 7;
 		}
+		else if (gameinfo.gametype == GAME_Strife)
+		{
+			strcpy (fullSection, "Strife");
+			tackOn = fullSection + 6;
+		}
 		else
 		{
 			strcpy (fullSection, "Doom");
diff -durN 96/src/g_shared/hudmessages.cpp 96x/src/g_shared/hudmessages.cpp
--- 96/src/g_shared/hudmessages.cpp	2004-12-18 19:49:23.000000000 +0100
+++ 96x/src/g_shared/hudmessages.cpp	2005-06-29 15:24:19.000000000 +0200
@@ -39,7 +39,7 @@
 #include "v_video.h"
 #include "cmdlib.h"
 
-EXTERN_CVAR (Bool, con_scaletext)
+EXTERN_CVAR (Int, con_scaletext)
 
 IMPLEMENT_CLASS (DHUDMessage)
 IMPLEMENT_CLASS (DHUDMessageFadeOut)
@@ -201,7 +201,7 @@
 	}
 	else
 	{
-		width = con_scaletext ? SCREENWIDTH / CleanXfac : SCREENWIDTH;
+		width = con_scaletext>=2? SCREENWIDTH/2 : (con_scaletext ? SCREENWIDTH / CleanXfac : SCREENWIDTH);
 	}
 
 	if (Lines != NULL)
@@ -265,7 +265,9 @@
 
 	DrawSetup ();
 
-	if (HUDWidth == 0 && con_scaletext)
+	int screen_width = SCREENWIDTH;
+	int screen_height = SCREENHEIGHT;
+	if (HUDWidth == 0 && con_scaletext==1)
 	{
 		clean = true;
 		xscale = CleanXfac;
@@ -274,17 +276,23 @@
 	else
 	{
 		xscale = yscale = 1;
+		if (HUDWidth==0 && con_scaletext>1) 
+		{
+			screen_width/=2;
+			screen_height/=2;
+			bottom/=2;
+		}
 	}
 
 	if (HUDWidth == 0)
 	{
 		if (Left > 0.f)
 		{ // Position center
-			x = (int)((float)(SCREENWIDTH - Width * xscale) * Left);
+			x = (int)((float)(screen_width - Width * xscale) * Left);
 		}
 		else
 		{ // Position edge
-			x = (int)((float)SCREENWIDTH * -Left);
+			x = (int)((float)screen_width * -Left);
 		}
 		if (Top > 0.f)
 		{ // Position center
@@ -342,7 +350,7 @@
 	else
 	{
 		// A positive height means the HUD size does not cover the status bar
-		hudheight = Scale (HUDHeight, SCREENHEIGHT, bottom);
+		hudheight = Scale (HUDHeight, screen_height, bottom);
 	}
 
 	for (i = 0; i < NumLines; i++)
@@ -377,9 +385,20 @@
 {
 	if (hudheight == 0)
 	{
-		screen->DrawText (TextColor, x, y, Lines[linenum].string,
-			DTA_CleanNoMove, clean,
-			TAG_DONE);
+		if (con_scaletext<=1)
+		{
+			screen->DrawText (TextColor, x, y, Lines[linenum].string,
+				DTA_CleanNoMove, clean,
+				TAG_DONE);
+		}
+		else
+		{
+			screen->DrawText (TextColor, x, y, Lines[linenum].string,
+				DTA_VirtualWidth, SCREENWIDTH/2,
+				DTA_VirtualHeight, SCREENHEIGHT/2,
+				DTA_KeepRatio, true,
+				TAG_DONE);
+		}
 	}
 	else
 	{
@@ -459,10 +478,22 @@
 		fixed_t trans = -(Tics - FadeOutTics) * FRACUNIT / FadeOutTics;
 		if (hudheight == 0)
 		{
-			screen->DrawText (TextColor, x, y, Lines[linenum].string,
-				DTA_CleanNoMove, clean,
-				DTA_Alpha, trans,
-				TAG_DONE);
+			if (con_scaletext<=1)
+			{
+				screen->DrawText (TextColor, x, y, Lines[linenum].string,
+					DTA_CleanNoMove, clean,
+					DTA_Alpha, trans,
+					TAG_DONE);
+			}
+			else
+			{
+				screen->DrawText (TextColor, x, y, Lines[linenum].string,
+					DTA_VirtualWidth, SCREENWIDTH/2,
+					DTA_VirtualHeight, SCREENHEIGHT/2,
+					DTA_Alpha, trans,
+					DTA_KeepRatio, true,
+					TAG_DONE);
+			}
 		}
 		else
 		{
@@ -540,10 +571,22 @@
 		fixed_t trans = Tics * FRACUNIT / FadeInTics;
 		if (hudheight == 0)
 		{
-			screen->DrawText (TextColor, x, y, Lines[linenum].string,
-				DTA_CleanNoMove, clean,
-				DTA_Alpha, trans,
-				TAG_DONE);
+			if (con_scaletext<=1)
+			{
+				screen->DrawText (TextColor, x, y, Lines[linenum].string,
+					DTA_CleanNoMove, clean,
+					DTA_Alpha, trans,
+					TAG_DONE);
+			}
+			else
+			{
+				screen->DrawText (TextColor, x, y, Lines[linenum].string,
+					DTA_VirtualWidth, SCREENWIDTH/2,
+					DTA_VirtualHeight, SCREENHEIGHT/2,
+					DTA_Alpha, trans,
+					DTA_KeepRatio, true,
+					TAG_DONE);
+			}
 		}
 		else
 		{
@@ -685,9 +728,20 @@
 			Lines[linenum].string[LineVisible] = 0;
 			if (hudheight == 0)
 			{
-				screen->DrawText (TextColor, x, y, Lines[linenum].string,
-					DTA_CleanNoMove, clean,
-					TAG_DONE);
+				if (con_scaletext<=1)
+				{
+					screen->DrawText (TextColor, x, y, Lines[linenum].string,
+						DTA_CleanNoMove, clean,
+						TAG_DONE);
+				}
+				else
+				{
+					screen->DrawText (TextColor, x, y, Lines[linenum].string,
+						DTA_VirtualWidth, SCREENWIDTH/2,
+						DTA_VirtualHeight, SCREENHEIGHT/2,
+						DTA_KeepRatio, true,
+						TAG_DONE);
+				}
 			}
 			else
 			{
diff -durN 96/src/g_strife/a_merchants.cpp 96x/src/g_strife/a_merchants.cpp
--- 96/src/g_strife/a_merchants.cpp	2004-12-18 20:28:19.000000000 +0100
+++ 96x/src/g_strife/a_merchants.cpp	2005-06-22 15:18:32.000000000 +0200
@@ -15,7 +15,6 @@
 {
 	DECLARE_ACTOR (AMerchant, AActor)
 public:
-	void ConversationAnimation (int animnum);
 };
 
 void A_PainCatch (AActor *m)
@@ -83,6 +82,9 @@
 	PROP_SpawnState (S_MERCHANT_STND)
 	PROP_SeeState (S_MERCHANT_PAIN)
 	PROP_PainState (S_MERCHANT_PAIN)
+	PROP_GreetingsState (S_MERCHANT_GT)
+	PROP_YesState (S_MERCHANT_YES)
+	PROP_NoState (S_MERCHANT_NO)
 
 	PROP_SpawnHealthLong (10000000)
 	PROP_PainChance (150)
@@ -93,22 +95,6 @@
 	PROP_Flags4 (MF4_NOSPLASHALERT)
 END_DEFAULTS
 
-void AMerchant::ConversationAnimation (int animnum)
-{
-	switch (animnum)
-	{
-	case 0:
-		SetState (&States[S_MERCHANT_GT]);
-		break;
-	case 1:
-		SetState (&States[S_MERCHANT_YES]);
-		break;
-	case 2:
-		SetState (&States[S_MERCHANT_NO]);
-		break;
-	}
-}
-
 // Weapon Smith -------------------------------------------------------------
 
 class AWeaponSmith : public AMerchant
diff -durN 96/src/g_strife/a_programmer.cpp 96x/src/g_strife/a_programmer.cpp
--- 96/src/g_strife/a_programmer.cpp	2004-11-04 11:43:04.000000000 +0100
+++ 96x/src/g_strife/a_programmer.cpp	2005-06-22 15:18:32.000000000 +0200
@@ -25,7 +25,6 @@
 	DECLARE_ACTOR (AProgrammer, AActor)
 public:
 	void NoBlockingSet ();
-	bool OkayToSwitchTarget (AActor *other);
 };
 
 FState AProgrammer::States[] =
@@ -95,7 +94,7 @@
 	PROP_Flags (MF_SOLID|MF_SHOOTABLE|MF_NOGRAVITY|MF_FLOAT|
 				MF_NOBLOOD|MF_COUNTKILL|MF_NOTDMATCH)
 	PROP_Flags2 (MF2_PASSMOBJ|MF2_PUSHWALL|MF2_MCROSS)
-	PROP_Flags4 (MF4_LOOKALLAROUND|MF4_FIRERESIST|MF4_NOICEDEATH)
+	PROP_Flags4 (MF4_LOOKALLAROUND|MF4_FIRERESIST|MF4_NOICEDEATH|MF4_NOTARGETSWITCH)
 	PROP_MinMissileChance (150)
 	PROP_AttackSound ("programmer/attack")
 	PROP_PainSound ("programmer/pain")
@@ -103,12 +102,6 @@
 	PROP_ActiveSound ("programmer/active")
 END_DEFAULTS
 
-bool AProgrammer::OkayToSwitchTarget (AActor *other)
-{
-	// The Programmer never switches his target until his current one is dead.
-	return false;
-}
-
 //============================================================================
 //
 // AProgrammer :: NoBlockingSet
diff -durN 96/src/gameconfigfile.cpp 96x/src/gameconfigfile.cpp
--- 96/src/gameconfigfile.cpp	2003-12-08 12:43:36.000000000 +0100
+++ 96x/src/gameconfigfile.cpp	2005-06-22 15:18:32.000000000 +0200
@@ -395,16 +395,14 @@
 		}
 	}
 
-	// Until all the Strife weapons are coded, always reset
-	// the Strife weapon slots.
 	strcpy (subsection, "WeaponSlots");
-	if (game != Strife && SetSection (section))
+	if (SetSection (section))
 	{
 		LocalWeapons.RestoreSlots (*this);
 	}
 	else
 	{
-		SetupWeaponList (gamename);
+		SetupWeaponList(gamename);
 	}
 }
 
diff -durN 96/src/info.h 96x/src/info.h
--- 96/src/info.h	2004-11-29 20:16:40.000000000 +0100
+++ 96x/src/info.h	2005-07-24 20:12:09.000000000 +0200
@@ -70,20 +70,16 @@
 #include "farchive.h"
 #include "doomdef.h"
 
-const BYTE SF_WEAPONPARAM= 0x20;
+const BYTE SF_STATEPARAM = 0x20;
 const BYTE SF_FULLBRIGHT = 0x40;
 const BYTE SF_BIGTIC	 = 0x80;
 
-// For weapon action functions with parameters a few tricks are necessary
-// because the misc fields are already used. This serves as a base class
-// for all parameter lists for weapon functions.
-struct FWeaponParam
-{
-	SBYTE wp_xoffset;
-	BYTE wp_yoffset;
-};
 
-extern TArray<FWeaponParam *> WeaponParams;
+// All state parameters are stored in this array now.
+// The first 2 parameters for each function call represent 
+// the old misc1/misc2 values, even for non-weapons
+
+extern TArray<int> StateParameters;
 
 struct FState
 {
@@ -105,7 +101,7 @@
 
 	inline int GetFrame() const
 	{
-		return Frame & ~(SF_FULLBRIGHT|SF_BIGTIC|SF_WEAPONPARAM);
+		return Frame & ~(SF_FULLBRIGHT|SF_BIGTIC|SF_STATEPARAM);
 	}
 	inline int GetFullbright() const
 	{
@@ -160,6 +156,15 @@
 	}
 };
 
+// A truly awful hack to get to the state that called an action function
+// without knowing whether it has been called from a weapon or actor.
+extern FState * CallingState;
+int CheckIndex(int paramsize, FState ** pcallstate=NULL);
+
+// This is used when an inventory item's use state sequence is executed.
+bool CallStateChain(AActor * actor, FState * State);
+
+
 FArchive &operator<< (FArchive &arc, FState *&state);
 
 #if _MSC_VER
@@ -261,6 +266,7 @@
 	ADEF_RenderFlags,
 	ADEF_Translation,
 	ADEF_MinMissileChance,
+	ADEF_MeleeRange,
 
 	ADEF_SpawnState,
 	ADEF_SeeState,
@@ -275,6 +281,18 @@
 	ADEF_EDeathState,
 	ADEF_RaiseState,
 	ADEF_WoundState,
+	ADEF_YesState,
+	ADEF_NoState,
+	ADEF_GreetingsState,
+
+	ADEF_User1,
+	ADEF_User2,
+	ADEF_User3,
+	ADEF_User4,
+	ADEF_User5,
+	ADEF_User6,
+	ADEF_User7,
+	ADEF_User8,
 
 	ADEF_StrifeType,	// Not really a property. Used to init StrifeTypes[] in p_conversation.h.
 	ADEF_StrifeTeaserType,
diff -durN 96/src/infodefaults.cpp 96x/src/infodefaults.cpp
--- 96/src/infodefaults.cpp	2004-11-29 20:19:13.000000000 +0100
+++ 96x/src/infodefaults.cpp	2005-07-24 20:12:09.000000000 +0200
@@ -257,6 +257,7 @@
 		case ADEF_RenderFlags:	actor->renderflags = dataint;	break;
 		case ADEF_Translation:	actor->Translation = dataint;	break;
 		case ADEF_MinMissileChance: actor->MinMissileChance = dataint; break;
+		case ADEF_MeleeRange:	actor->meleerange = dataint;	break;
 
 		case ADEF_SpawnState:	actor->SpawnState = datastate;	break;
 		case ADEF_SeeState:		actor->SeeState = datastate;	break;
@@ -271,6 +272,18 @@
 		case ADEF_EDeathState:	actor->EDeathState = datastate;	break;
 		case ADEF_RaiseState:	actor->RaiseState = datastate;	break;
 		case ADEF_WoundState:	actor->WoundState = datastate;	break;
+		case ADEF_YesState:		actor->YesState = datastate;	break;
+		case ADEF_NoState:		actor->NoState = datastate;	break;
+		case ADEF_GreetingsState:actor->GreetingsState = datastate;	break;
+
+		case ADEF_User1:		actor->user[0] = dataint;		break;
+		case ADEF_User2:		actor->user[1] = dataint;		break;
+		case ADEF_User3:		actor->user[2] = dataint;		break;
+		case ADEF_User4:		actor->user[3] = dataint;		break;
+		case ADEF_User5:		actor->user[4] = dataint;		break;
+		case ADEF_User6:		actor->user[5] = dataint;		break;
+		case ADEF_User7:		actor->user[6] = dataint;		break;
+		case ADEF_User8:		actor->user[7] = dataint;		break;
 
 		case ADEF_StrifeType:	if (!(gameinfo.flags & GI_SHAREWARE)) StrifeTypes[dataint] = Class;	break;
 		case ADEF_StrifeTeaserType:
diff -durN 96/src/infomacros.h 96x/src/infomacros.h
--- 96/src/infomacros.h	2004-11-29 20:17:34.000000000 +0100
+++ 96x/src/infomacros.h	2005-07-24 20:12:09.000000000 +0200
@@ -246,6 +246,7 @@
 #define PROP_RenderFlags(x)				ADD_WORD_PROP(ADEF_RenderFlags,x)
 #define PROP_Translation(x,y)			ADD_WORD_PROP(ADEF_Translation,((x)<<8)|(y))
 #define PROP_MinMissileChance(x)		ADD_BYTE_PROP(ADEF_MinMissileChance,x)
+#define PROP_MeleeRange(x)				ADD_FIXD_PROP(ADEF_MeleeRange,x)
 
 #define PROP_SpawnState(x)				ADD_BYTE_PROP(ADEF_SpawnState,x)
 #define PROP_SeeState(x)				ADD_BYTE_PROP(ADEF_SeeState,x)
@@ -260,6 +261,18 @@
 #define PROP_EDeathState(x)				ADD_BYTE_PROP(ADEF_EDeathState,x)
 #define PROP_RaiseState(x)				ADD_BYTE_PROP(ADEF_RaiseState,x)
 #define PROP_WoundState(x)				ADD_BYTE_PROP(ADEF_WoundState,x)
+#define PROP_YesState(x)				ADD_BYTE_PROP(ADEF_YesState,x)
+#define PROP_NoState(x)					ADD_BYTE_PROP(ADEF_NoState,x)
+#define PROP_GreetingsState(x)			ADD_BYTE_PROP(ADEF_GreetingsState,x)
+
+#define PROP_User1(x)					ADD_LONG_PROP(ADEF_User1,x)
+#define PROP_User2(x)					ADD_LONG_PROP(ADEF_User2,x)
+#define PROP_User3(x)					ADD_LONG_PROP(ADEF_User3,x)
+#define PROP_User4(x)					ADD_LONG_PROP(ADEF_User4,x)
+#define PROP_User5(x)					ADD_LONG_PROP(ADEF_User5,x)
+#define PROP_User6(x)					ADD_LONG_PROP(ADEF_User6,x)
+#define PROP_User7(x)					ADD_LONG_PROP(ADEF_User7,x)
+#define PROP_User8(x)					ADD_LONG_PROP(ADEF_User8,x)
 
 #define PROP_StrifeType(x)				ADD_WORD_PROP(ADEF_StrifeType,x)
 #define PROP_StrifeTeaserType(x)		ADD_WORD_PROP(ADEF_StrifeTeaserType,x)
diff -durN 96/src/m_bbox.cpp 96x/src/m_bbox.cpp
--- 96/src/m_bbox.cpp	2002-01-14 15:39:16.000000000 +0100
+++ 96x/src/m_bbox.cpp	2005-06-22 15:18:32.000000000 +0200
@@ -43,12 +43,12 @@
 {
 	if (x < m_Box[BOXLEFT])
 		m_Box[BOXLEFT] = x;
-	else if (x > m_Box[BOXRIGHT])
+	if (x > m_Box[BOXRIGHT])
 		m_Box[BOXRIGHT] = x;
 
 	if (y < m_Box[BOXBOTTOM])
 		m_Box[BOXBOTTOM] = y;
-	else if (y > m_Box[BOXTOP])
+	if (y > m_Box[BOXTOP])
 		m_Box[BOXTOP] = y;
 }
 
diff -durN 96/src/m_cheat.cpp 96x/src/m_cheat.cpp
--- 96/src/m_cheat.cpp	2004-12-24 16:32:04.000000000 +0100
+++ 96x/src/m_cheat.cpp	2005-06-29 15:25:07.000000000 +0200
@@ -241,6 +241,17 @@
 		msg = GStrings(TXT_CHEATKEYS);
 		break;
 
+	// [GRB]
+	case CHT_RESSURECT:
+		player->playerstate = PST_LIVE;
+		player->health = player->mo->health = player->mo->GetDefault()->health;
+		player->mo->flags = player->mo->GetDefault()->flags;
+		player->mo->height = player->mo->GetDefault()->height;
+		player->mo->SetState(player->mo->SpawnState);
+		player->mo->Translation = TRANSLATION(TRANSLATION_Players,(player-players));
+		player->mo->GiveDefaultInventory();
+		break;
+
 	case CHT_TAKEWEAPS:
 		if (player->morphTics)
 		{
@@ -372,7 +383,7 @@
 				item->Amount = MIN (amount, item->MaxAmount);
 			}
 		}
-		if (!item->TryPickup (player->mo))
+		if (!item->DoTryPickup (player->mo))
 		{
 			item->Destroy ();
 		}
@@ -489,7 +500,7 @@
 				if (key->KeyNumber != 0)
 				{
 					key = static_cast<AKey *>(Spawn (TypeInfo::m_Types[i], 0,0,0));
-					if (!key->TryPickup (player->mo))
+					if (!key->DoTryPickup (player->mo))
 					{
 						key->Destroy ();
 					}
diff -durN 96/src/m_options.cpp 96x/src/m_options.cpp
--- 96/src/m_options.cpp	2004-12-24 15:03:45.000000000 +0100
+++ 96x/src/m_options.cpp	2005-06-23 13:46:51.000000000 +0200
@@ -342,6 +342,7 @@
 	{ redtext,	" ",					{NULL},	{0.0}, {0.0}, {0.0}, {NULL} },
 	{ whitetext,"Controls",				{NULL},	{0.0}, {0.0}, {0.0}, {NULL} },
 	{ control,	"Fire",					{NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+attack"} },
+	{ control,	"Alternate Fire",		{NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+altattack"} },
 	{ control,	"Use / Open",			{NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+use"} },
 	{ control,	"Move forward",			{NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+forward"} },
 	{ control,	"Move backward",		{NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+back"} },
@@ -512,7 +513,7 @@
  * Messages Menu
  *
  *=======================================*/
-EXTERN_CVAR (Bool, con_scaletext)
+EXTERN_CVAR (Int, con_scaletext)
 EXTERN_CVAR (Bool, con_centernotify)
 EXTERN_CVAR (Int,  msg0color)
 EXTERN_CVAR (Int,  msg1color)
@@ -522,6 +523,12 @@
 EXTERN_CVAR (Int,  msgmidcolor)
 EXTERN_CVAR (Int,  msglevel)
 
+value_t ScaleValues[3] = {
+	{ 0.0, "Off" },
+	{ 1.0, "On" },
+	{ 2.0, "Double" },
+};
+
 static value_t TextColors[] =
 {
 	{ 0.0, "brick" },
@@ -544,7 +551,7 @@
 };
 
 static menuitem_t MessagesItems[] = {
-	{ discrete,	"Scale text in high res", {&con_scaletext},		{2.0}, {0.0}, 	{0.0}, {OnOff} },
+	{ discrete,	"Scale text in high res", {&con_scaletext},		{3.0}, {0.0}, 	{0.0}, {ScaleValues} },
 	{ discrete, "Minimum message level", {&msglevel},		   	{3.0}, {0.0},   {0.0}, {MessageLevels} },
 	{ discrete, "Center messages",		{&con_centernotify},	{2.0}, {0.0},	{0.0}, {OnOff} },
 	{ redtext,	" ",					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
@@ -748,6 +755,7 @@
 	{ bitflag,	"Spawn item drops on the floor",			{&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_NOTOSSDROPS} },
 	{ bitflag,  "All special lines can block use lines",	{&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_USEBLOCKING} },
 	{ bitflag,	"Disable BOOM door light effect",			{&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_NODOORLIGHT} },
+	{ bitflag,	"Raven scroll types use original speed",	{&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_RAVENSCROLL} },
 };
 
 static menu_t CompatibilityMenu =
diff -durN 96/src/nodebuild_extract.cpp 96x/src/nodebuild_extract.cpp
--- 96/src/nodebuild_extract.cpp	2003-01-07 21:34:38.000000000 +0100
+++ 96x/src/nodebuild_extract.cpp	2005-06-22 15:18:32.000000000 +0200
@@ -62,6 +62,7 @@
 
 	subCount = Subsectors.Size();
 	outSubs = new subsector_t[subCount];
+	memset(outSubs,0,subCount * sizeof(subsector_t));
 
 	nodeCount = Nodes.Size ();
 	outNodes = new node_t[nodeCount];
diff -durN 96/src/p_acs.cpp 96x/src/p_acs.cpp
--- 96/src/p_acs.cpp	2004-12-24 16:32:22.000000000 +0100
+++ 96x/src/p_acs.cpp	2005-07-24 20:12:09.000000000 +0200
@@ -174,6 +174,13 @@
 	bool hadweap = actor->player != NULL ? actor->player->ReadyWeapon != NULL : true;
 
 	AInventory *item = static_cast<AInventory *>(Spawn (info, 0,0,0));
+
+	// This shouldn't count for the item statistics!
+	if (item->flags&MF_COUNTITEM) 
+	{
+		level.total_items--;
+		item->flags&=~MF_COUNTITEM;
+	}
 	if (info->IsDescendantOf (RUNTIME_CLASS(ABasicArmorPickup)))
 	{
 		if (static_cast<ABasicArmorPickup*>(item)->SaveAmount != 0)
@@ -193,7 +200,7 @@
 	{
 		item->Amount = amount;
 	}
-	if (!item->TryPickup (actor))
+	if (!item->DoTryPickup (actor))
 	{
 		item->Destroy ();
 	}
@@ -2079,13 +2086,22 @@
 #define APROP_Damage		2
 #define APROP_Alpha			3
 #define APROP_RenderStyle	4
-#define APROP_Ambush		10
-#define APROP_Invulnerable	11
 #define APROP_SeeSound		5	// Sounds can only be set, not gotten
 #define APROP_AttackSound	6
 #define APROP_PainSound		7
 #define APROP_DeathSound	8
 #define APROP_ActiveSound	9
+#define APROP_Ambush		10
+#define APROP_Invulnerable	11
+#define APROP_JumpZ			12	// [GRB]
+#define APROP_User1			13
+#define APROP_User2			14
+#define APROP_User3			15
+#define APROP_User4			16
+#define APROP_User5			17
+#define APROP_User6			18
+#define APROP_User7			19
+#define APROP_User8			20
 
 void DLevelScript::SetActorProperty (int tid, int property, int value)
 {
@@ -2126,11 +2142,22 @@
 	case APROP_RenderStyle:	actor->RenderStyle = value;	break;
 	case APROP_Ambush:		if (value) actor->flags |= MF_AMBUSH; else actor->flags &= ~MF_AMBUSH;		break;
 	case APROP_Invulnerable:if (value) actor->flags2 |= MF2_INVULNERABLE; else actor->flags2 &= ~MF2_INVULNERABLE;		break;
+	case APROP_JumpZ:		if (actor->IsKindOf (RUNTIME_CLASS (APlayerPawn)))
+								static_cast<APlayerPawn *>(actor)->JumpZ = value;						break; 	// [GRB]
 	case APROP_SeeSound:	actor->SeeSound = S_FindSound (FBehavior::StaticLookupString (value));		break;
 	case APROP_AttackSound:	actor->AttackSound = S_FindSound (FBehavior::StaticLookupString (value));	break;
 	case APROP_PainSound:	actor->PainSound = S_FindSound (FBehavior::StaticLookupString (value));		break;
 	case APROP_DeathSound:	actor->DeathSound = S_FindSound (FBehavior::StaticLookupString (value));	break;
 	case APROP_ActiveSound:	actor->ActiveSound = S_FindSound (FBehavior::StaticLookupString (value));	break;
+	
+	case APROP_User1:
+	case APROP_User2:
+	case APROP_User3:
+	case APROP_User4:
+	case APROP_User5:
+	case APROP_User6:
+	case APROP_User7:
+	case APROP_User8:		actor->user[property-APROP_User1] = value;	break;
 	}
 }
 
@@ -2163,6 +2190,24 @@
 	case APROP_Alpha:		return actor->alpha;
 	case APROP_RenderStyle:	return actor->RenderStyle;
 	case APROP_Ambush:		return !!(actor->flags & MF_AMBUSH);
+	case APROP_JumpZ:		if (actor->IsKindOf (RUNTIME_CLASS (APlayerPawn)))
+							{
+								return static_cast<APlayerPawn *>(actor)->JumpZ;	// [GRB]
+							}
+							else
+							{
+								return 0;
+							}
+
+	case APROP_User1:
+	case APROP_User2:
+	case APROP_User3:
+	case APROP_User4:
+	case APROP_User5:
+	case APROP_User6:
+	case APROP_User7:
+	case APROP_User8:		return actor->user[property-APROP_User1];
+							
 	default:				return 0;
 	}
 }
@@ -3706,16 +3751,21 @@
 					switch (STACK(1))
 					{
 					case BLOCK_NOTHING:
-						lines[line].flags &= ~(ML_BLOCKING|ML_BLOCKEVERYTHING);
+						lines[line].flags &= ~(ML_BLOCKING|ML_BLOCKEVERYTHING|ML_RAILING);
 						break;
 					case BLOCK_CREATURES:
 					default:
-						lines[line].flags &= ~ML_BLOCKEVERYTHING;
+						lines[line].flags &= ~(ML_BLOCKEVERYTHING|ML_RAILING);
 						lines[line].flags |= ML_BLOCKING;
 						break;
 					case BLOCK_EVERYTHING:
+						lines[line].flags &= ~(ML_RAILING);
 						lines[line].flags |= ML_BLOCKING|ML_BLOCKEVERYTHING;
 						break;
+					case BLOCK_RAILING:
+						lines[line].flags &= ~(ML_BLOCKEVERYTHING);
+						lines[line].flags |= ML_RAILING|ML_BLOCKING;
+						break;
 					}
 				}
 
@@ -4481,11 +4531,25 @@
 				if (camera != NULL)
 				{
 					int picnum = TexMan.CheckForTexture (picname, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable);
-					FCanvasTextureInfo::Add (camera, picnum, STACK(1));
+					if (picnum != -1)
+						FCanvasTextureInfo::Add (camera, picnum, STACK(1));
 				}
 				sp -= 3;
 			}
 			break;
+
+		case PCD_SETACTORANGLE:		// [GRB]
+			{
+				FActorIterator iterator (STACK(2));
+				AActor *actor;
+
+				while ( (actor = iterator.Next ()) )
+				{
+					actor->angle = STACK(1) << 16;
+				}
+			}
+			sp -= 2;
+			break;
 		}
 	}
 
diff -durN 96/src/p_acs.h 96x/src/p_acs.h
--- 96/src/p_acs.h	2004-12-23 21:00:37.000000000 +0100
+++ 96x/src/p_acs.h	2005-07-05 18:24:49.000000000 +0200
@@ -484,6 +484,7 @@
 		PCD_PRINTMAPCHARARRAY,		// [JB] start of new p-codes
 		PCD_PRINTWORLDCHARARRAY,
 		PCD_PRINTGLOBALCHARARRAY,	// [JB] end of new p-codes
+		PCD_SETACTORANGLE,			// [GRB]
 
 		PCODE_COMMAND_COUNT
 	};
@@ -522,7 +523,8 @@
 	enum {
 		BLOCK_NOTHING =			0,
 		BLOCK_CREATURES =		1,
-		BLOCK_EVERYTHING =		2
+		BLOCK_EVERYTHING =		2,
+		BLOCK_RAILING =			3
 	};
 	enum {
 		LEVELINFO_PAR_TIME,
diff -durN 96/src/p_conversation.cpp 96x/src/p_conversation.cpp
--- 96/src/p_conversation.cpp	2004-12-18 20:22:53.000000000 +0100
+++ 96x/src/p_conversation.cpp	2005-06-22 15:18:32.000000000 +0200
@@ -950,8 +950,11 @@
 	if (reply->GiveType != NULL)
 	{
 		AInventory *item = static_cast<AInventory *> (Spawn (reply->GiveType, 0, 0, 0));
+		// Items given here should not count as items!
+		if (item->flags & MF_COUNTITEM) level.total_items--;
+		item->flags&=~MF_COUNTITEM;
 		item->flags |= MF_DROPPED;
-		if (!item->TryPickup (players[consoleplayer].mo))
+		if (!item->DoTryPickup (players[consoleplayer].mo))
 		{
 			item->Destroy ();
 			takestuff = false;
diff -durN 96/src/p_doors.cpp 96x/src/p_doors.cpp
--- 96/src/p_doors.cpp	2004-11-03 08:08:19.000000000 +0100
+++ 96x/src/p_doors.cpp	2005-06-22 15:18:32.000000000 +0200
@@ -68,9 +68,13 @@
 
 	if (m_Sector->floorplane.d != m_OldFloorDist)
 	{
-		m_OldFloorDist = m_Sector->floorplane.d;
-		m_BotDist = m_Sector->ceilingplane.PointToDist (m_BotSpot,
-			m_Sector->floorplane.ZatPoint (m_BotSpot));
+		if (!m_Sector->floordata || !m_Sector->floordata->IsKindOf(RUNTIME_CLASS(DPlat)) ||
+			!((DPlat*)m_Sector->floordata)->IsLift())
+		{
+			m_OldFloorDist = m_Sector->floorplane.d;
+			m_BotDist = m_Sector->ceilingplane.PointToDist (m_BotSpot,
+				m_Sector->floorplane.ZatPoint (m_BotSpot));
+		}
 	}
 
 	switch (m_Direction)
@@ -201,7 +205,7 @@
 // [RH] DoorSound: Plays door sound depending on direction and speed
 void DDoor::DoorSound (bool raise) const
 {
-	const char *snd;
+	const char *snd="";
 
 	if (m_Sector->seqType >= 0)
 	{
@@ -213,7 +217,7 @@
 		{
 		default:
 			break;
-
+			
 		case GAME_Heretic:
 			snd = raise ? "HereticDoorOpen" : "HereticDoorClose";
 			break;
@@ -337,8 +341,17 @@
 		break;
 	}
 
-	height = sec->FindHighestFloorPoint (&m_BotSpot);
-	m_BotDist = sec->ceilingplane.PointToDist (m_BotSpot, height);
+	if (!m_Sector->floordata || !m_Sector->floordata->IsKindOf(RUNTIME_CLASS(DPlat)) ||
+		!((DPlat*)m_Sector->floordata)->IsLift())
+	{
+		height = sec->FindHighestFloorPoint (&m_BotSpot);
+		m_BotDist = sec->ceilingplane.PointToDist (m_BotSpot, height);
+	}
+	else
+	{
+		height = sec->FindLowestCeilingPoint(&m_BotSpot);
+		m_BotDist = sec->ceilingplane.PointToDist (m_BotSpot, height);
+	}
 	m_OldFloorDist = sec->floorplane.d;
 }
 
@@ -545,6 +558,7 @@
 			}
 
 			fixed_t topdist = m_Sector->ceilingplane.d;
+
 			if (MoveCeiling (2048*FRACUNIT, m_BotDist, 0, -1) == crushed)
 			{
 				m_Timer = m_Delay;
@@ -555,6 +569,7 @@
 
 			m_Line1->flags |= ML_BLOCKING;
 			m_Line2->flags |= ML_BLOCKING;
+
 			if (ani.CloseSound != NULL)
 			{
 				SN_StartSequence (m_Sector, ani.CloseSound);
@@ -573,7 +588,7 @@
 				// IF DOOR IS DONE CLOSING...
 				MoveCeiling (2048*FRACUNIT, m_BotDist, -1);
 				m_Sector->ceilingdata = NULL;
-				break;
+				Destroy();
 			}
 			else
 			{
@@ -670,7 +685,12 @@
 	sides[m_Line1->sidenum[0]].midtexture = picnum;
 	sides[m_Line2->sidenum[0]].midtexture = picnum;
 
-	topdist = TexMan[picnum]->GetHeight();
+
+	// don't forget texture scaling here!
+	FTexture * tex=TexMan[picnum];
+	topdist=tex? (tex->GetHeight() * 8 / (tex->ScaleY? tex->ScaleY : 8)) : 64;	
+	//topdist = TexMan[picnum]->GetHeight();	// old non-scaling sensitive code!
+
 	topdist = m_Sector->ceilingplane.d - topdist * m_Sector->ceilingplane.c;
 
 	m_Status = Opening;
diff -durN 96/src/p_effect.cpp 96x/src/p_effect.cpp
--- 96/src/p_effect.cpp	2003-04-14 09:44:02.000000000 +0200
+++ 96x/src/p_effect.cpp	2005-07-25 18:32:22.000000000 +0200
@@ -46,6 +46,7 @@
 #include "r_defs.h"
 #include "r_things.h"
 #include "s_sound.h"
+#include "templates.h"
 
 CVAR (Int, cl_rockettrails, 1, CVAR_ARCHIVE);
 
@@ -380,8 +381,10 @@
 		color1 = grey3;
 		color2 = grey1;
 		break;
-	default:
-		return;
+	default:	// colorized blood
+		color1 = ColorMatcher.Pick(RPART(kind), GPART(kind), BPART(kind));
+		color2 = ColorMatcher.Pick(RPART(kind)>>1, GPART(kind)>>1, BPART(kind)>>1);
+		break;
 	}
 
 	zvel = -128;
@@ -417,7 +420,7 @@
 	}
 }
 
-void P_DrawRailTrail (vec3_t start, vec3_t end)
+void P_DrawRailTrail (AActor * source, vec3_t start, vec3_t end, int color1, int color2, float maxdiff, bool silent)
 {
 	float length;
 	int steps, i;
@@ -430,33 +433,44 @@
 
 	if (length)
 	{
-		// The railgun's sound is special. It gets played from the
-		// point on the slug's trail that is closest to the hearing player.
-		AActor *mo = players[consoleplayer].camera;
-		vec3_t point;
-		float r;
-		float dirz;
-
 		length = 1 / length;
 
-		if (abs(mo->x - FLOAT2FIXED(start[0])) < 20 * FRACUNIT
-			&& (mo->y - FLOAT2FIXED(start[1])) < 20 * FRACUNIT)
-		{ // This player (probably) fired the railgun
-			S_Sound (mo, CHAN_WEAPON, "weapons/railgf", 1, ATTN_NORM);
-		}
-		else
+		if (!silent)
 		{
-			// Only consider sound in 2D (for now, anyway)
-			r = ((start[1] - FIXED2FLOAT(mo->y)) * (-dir[1]) -
-				 (start[0] - FIXED2FLOAT(mo->x)) * (dir[0])) * length * length;
+			int sound;
+			
+			// Allow other sounds than 'weapons/railgf'!
+			if (!source->player) sound = source->AttackSound;
+			else if (source->player->ReadyWeapon) sound = source->player->ReadyWeapon->AttackSound;
+			else sound = NULL;
+			if (!sound) sound=S_FindSound("weapons/railgf");
 
-			dirz = dir[2];
-			dir[2] = 0;
-			VectorMA (start, r, dir, point);
-			dir[2] = dirz;
+			// The railgun's sound is special. It gets played from the
+			// point on the slug's trail that is closest to the hearing player.
+			AActor *mo = players[consoleplayer].camera;
+			vec3_t point;
+			float r;
+			float dirz;
 
-			S_Sound (FLOAT2FIXED(point[0]), FLOAT2FIXED(point[1]),
-				CHAN_WEAPON, "weapons/railgf", 1, ATTN_NORM);
+			if (abs(mo->x - FLOAT2FIXED(start[0])) < 20 * FRACUNIT
+				&& (mo->y - FLOAT2FIXED(start[1])) < 20 * FRACUNIT)
+			{ // This player (probably) fired the railgun
+				S_SoundID (mo, CHAN_WEAPON, sound, 1, ATTN_NORM);
+			}
+			else
+			{
+				// Only consider sound in 2D (for now, anyway)
+				r = ((start[1] - FIXED2FLOAT(mo->y)) * (-dir[1]) -
+					(start[0] - FIXED2FLOAT(mo->x)) * (dir[0])) * length * length;
+
+				dirz = dir[2];
+				dir[2] = 0;
+				VectorMA (start, r, dir, point);
+				dir[2] = dirz;
+
+				S_SoundID (FLOAT2FIXED(point[0]), FLOAT2FIXED(point[1]), mo->z,
+					CHAN_WEAPON, sound, 1, ATTN_NORM);
+			}
 		}
 	}
 	else
@@ -470,73 +484,78 @@
 	VectorScale2 (extend, 3);
 	VectorScale (dir, 3, step);
 
-	VectorCopy (start, pos);
-	deg = 270;
-	for (i = steps; i; i--)
+	if (color1 != -1)
 	{
-		particle_t *p = NewParticle ();
-		vec3_t tempvec;
-
-		if (!p)
-			return;
+		VectorCopy (start, pos);
+		deg = 270;
+		for (i = steps; i; i--)
+		{
+			particle_t *p = NewParticle ();
+			vec3_t tempvec;
 
-		p->trans = 255;
-		p->ttl = 35;
-		p->fade = FADEFROMTTL(35);
-		p->size = 3;
+			if (!p)
+				return;
 
-		RotatePointAroundVector (tempvec, dir, extend, deg);
-		p->velx = FLOAT2FIXED(tempvec[0])>>4;
-		p->vely = FLOAT2FIXED(tempvec[1])>>4;
-		p->velz = FLOAT2FIXED(tempvec[2])>>4;
-		VectorAdd (tempvec, pos, tempvec);
-		deg += 14;
-		if (deg >= 360)
-			deg -= 360;
-		p->x = FLOAT2FIXED(tempvec[0]);
-		p->y = FLOAT2FIXED(tempvec[1]);
-		p->z = FLOAT2FIXED(tempvec[2]);
-		VectorAdd (pos, step, pos);
+			p->trans = 255;
+			p->ttl = 35;
+			p->fade = FADEFROMTTL(35);
+			p->size = 3;
 
-		{
-			int rand = M_Random();
+			RotatePointAroundVector (tempvec, dir, extend, deg);
+			p->velx = FLOAT2FIXED(tempvec[0])>>4;
+			p->vely = FLOAT2FIXED(tempvec[1])>>4;
+			p->velz = FLOAT2FIXED(tempvec[2])>>4;
+			VectorAdd (tempvec, pos, tempvec);
+			deg += 14;
+			if (deg >= 360)
+				deg -= 360;
+			p->x = FLOAT2FIXED(tempvec[0]);
+			p->y = FLOAT2FIXED(tempvec[1]);
+			p->z = FLOAT2FIXED(tempvec[2]);
+			VectorAdd (pos, step, pos);
 
-			if (rand < 155)
-				p->color = rblue2;
-			else if (rand < 188)
-				p->color = rblue1;
-			else if (rand < 222)
-				p->color = rblue3;
-			else
-				p->color = rblue4;
+			p->color = color1;
 		}
 	}
 
-	VectorCopy (start, pos);
-	for (i = steps; i; i--)
+	if (color2 != -1)
 	{
-		particle_t *p = JitterParticle (33);
-
-		if (!p)
-			return;
+		vec3_t diff;
+		VectorSet (diff, 0, 0, 0);
 
-		p->size = 2;
-		p->x = FLOAT2FIXED(pos[0]);
-		p->y = FLOAT2FIXED(pos[1]);
-		p->z = FLOAT2FIXED(pos[2]);
-		p->accz -= FRACUNIT/4096;
-		VectorAdd (pos, step, pos);
+		VectorCopy (start, pos);
+		for (i = steps; i; i--)
 		{
-			int rand = M_Random();
+			particle_t *p = JitterParticle (33);
 
-			if (rand < 85)
-				p->color = grey4;
-			else if (rand < 170)
-				p->color = grey2;
-			else
-				p->color = grey1;
+			if (!p)
+				return;
+
+			if (maxdiff > 0)
+			{
+				int rnd = M_Random ();
+				if (rnd & 1)
+					diff[0] = clamp<float> (diff[0] + ((rnd & 8) ? 1 : -1), -maxdiff, maxdiff);
+				if (rnd & 2)
+					diff[1] = clamp<float> (diff[1] + ((rnd & 16) ? 1 : -1), -maxdiff, maxdiff);
+				if (rnd & 4)
+					diff[2] = clamp<float> (diff[2] + ((rnd & 32) ? 1 : -1), -maxdiff, maxdiff);
+			}
+
+			vec3_t postmp;
+			VectorCopy (pos, postmp);
+			VectorAdd (postmp, diff, postmp);
+
+			p->size = 2;
+			p->x = FLOAT2FIXED(postmp[0]);
+			p->y = FLOAT2FIXED(postmp[1]);
+			p->z = FLOAT2FIXED(postmp[2]);
+			if (color1 != -1)
+				p->accz -= FRACUNIT/4096;
+			VectorAdd (pos, step, pos);
+
+			p->color = color2;
 		}
-		p->color = white;
 	}
 }
 
diff -durN 96/src/p_effect.h 96x/src/p_effect.h
--- 96/src/p_effect.h	2004-12-20 18:33:45.000000000 +0100
+++ 96x/src/p_effect.h	2005-07-25 17:54:26.000000000 +0200
@@ -58,7 +58,7 @@
 
 void P_RunEffect (AActor *actor, int effects);
 
-void P_DrawRailTrail (vec3_t start, vec3_t end);
+void P_DrawRailTrail (AActor * source, vec3_t start, vec3_t end, int color1, int color2, float maxdiff = 0, bool silent = false);
 void P_DrawSplash (int count, fixed_t x, fixed_t y, fixed_t z, angle_t angle, int kind);
 void P_DrawSplash2 (int count, fixed_t x, fixed_t y, fixed_t z, angle_t angle, int updown, int kind);
 void P_DisconnectEffect (AActor *actor);
diff -durN 96/src/p_enemy.cpp 96x/src/p_enemy.cpp
--- 96/src/p_enemy.cpp	2004-12-06 13:08:33.000000000 +0100
+++ 96x/src/p_enemy.cpp	2005-07-24 20:12:09.000000000 +0200
@@ -57,11 +57,20 @@
 static FRandom pr_facetarget ("FaceTarget");
 static FRandom pr_railface ("RailFace");
 static FRandom pr_dropitem ("DropItem");
-static FRandom pr_fastchase ("FastChase");
 static FRandom pr_look2 ("LookyLooky");
 static FRandom pr_look3 ("IGotHooky");
 static FRandom pr_slook ("SlooK");
 
+static FRandom pr_skiptarget("SkipTarget");
+
+// movement interpolation is fine for objects that are moved by their own
+// momentum. But for monsters it is problematic.
+// 1. They don't move every tic
+// 2. Their animation is not designed for movement interpolation
+// The result is that they tend to 'glide' across the floor
+// so this CVAR allows to switch it off.
+CVAR(Bool, nomonsterinterpolation, false, CVAR_GLOBALCONFIG|CVAR_ARCHIVE)
+
 //
 // P_NewChaseDir related LUT.
 //
@@ -134,7 +143,7 @@
 		{
 			continue;
 		}
-
+		
 		if ( sides[ check->sidenum[0] ].sector == sec)
 			other = sides[ check->sidenum[1] ].sector;
 		else
@@ -208,7 +217,7 @@
 	pl = target;
 	dist = P_AproxDistance (pl->x - x, pl->y - y);
 
-	if (dist >= MELEERANGE-20*FRACUNIT + pl->radius)
+	if (dist >= meleerange + pl->radius)
 		return false;
 
 	// [RH] If moving toward goal, then we've reached it.
@@ -314,6 +323,33 @@
 	return pr_checkmissilerange() >= MIN<int> (dist >> FRACBITS, MinMissileChance);
 }
 
+//=============================================================================
+//
+// P_HitFriend()
+//
+// killough 12/98
+// This function tries to prevent shooting at friends that get in the line of fire
+//
+// [GrafZahl] Taken from MBF but this has been cleaned up to make it readable.
+//
+//=============================================================================
+
+bool P_HitFriend(AActor * self)
+{
+	if (self->flags&MF_FRIENDLY && self->target != NULL)
+	{
+		angle_t angle = R_PointToAngle2 (self->x, self->y, self->target->x, self->target->y);
+		fixed_t dist = P_AproxDistance (self->x-self->target->x, self->y-self->target->y);
+		P_AimLineAttack (self, self->angle, dist, 0);
+		if (linetarget != NULL && linetarget != self->target)
+		{
+			return P_IsFriend(self, linetarget);
+		}
+	}
+	return false;
+}
+
+
 //
 // P_Move
 // Move in the current direction,
@@ -375,6 +411,15 @@
 	// killough 3/15/98: don't jump over dropoffs:
 	try_ok = P_TryMove (actor, tryx, tryy, false);
 
+	// [GrafZahl] Interpolating monster movement as it is done here just looks bad
+	// so make it switchable!
+	if (nomonsterinterpolation)
+	{
+		actor->PrevX = actor->x;
+		actor->PrevY = actor->y;
+		actor->PrevZ = actor->z;
+	}
+
 	if (try_ok && friction > ORIG_FRICTION)
 	{
 		actor->x = origx;
@@ -1047,6 +1092,22 @@
 			other = link;
 		}
 
+		// [MBF] If the monster is already engaged in a one-on-one attack
+		// with a healthy friend, don't attack around 60% the time.
+		
+		// [GrafZahl] This prevents friendlies from attacking all the same 
+		// target.
+		
+		if (other)
+		{
+			AActor *targ = other->target;
+			if (targ && targ->target == other && pr_skiptarget() > 100 && P_IsFriend(lookee, targ) &&
+				targ->health*2 >= targ->GetDefault()->health)
+			{
+				continue;
+			}
+		}
+
 		if (other == NULL || !P_CheckSight (lookee, other, 2))
 			continue;			// out of sight
 	/*						
@@ -1310,8 +1371,13 @@
 
 	if (targ && (targ->flags & MF_SHOOTABLE))
 	{
-		if (actor->flags & targ->flags & MF_FRIENDLY)
+		if (P_IsFriend(actor, targ))	// be a little more precise!
 		{
+			// If we find a valid target here, the wandering logic should *not*
+			// be activated! If would cause the seestate to be set twice.
+			if (P_LookForPlayers (actor, actor->flags4 & MF4_LOOKALLAROUND))
+				goto seeyou;
+
 			// Let the actor wander around aimlessly looking for a fight
 			if (actor->SeeState != NULL)
 			{
@@ -1462,10 +1528,15 @@
 =
 = Actor has a melee attack, so it tries to close as fast as possible
 =
-==============
 */
+// [GrafZahl] integrated A_FastChase, A_SerpentChase and A_SerpentWalk into this
+// to allow the monsters using those functions to take advantage of the
+// enhancements.
+//
+//=============================================================================
+#define CLASS_BOSS_STRAFE_RANGE	64*10*FRACUNIT
 
-void A_Chase (AActor *actor)
+void A_DoChase(AActor * actor, bool fastchase, FState * meleestate, FState * missilestate, bool playactive, bool nightmarefast)
 {
 	int delta;
 
@@ -1503,7 +1574,7 @@
 		}
 	}
 
-	if (gameinfo.gametype == GAME_Heretic &&
+	if (nightmarefast &&
 		(gameskill == sk_nightmare || (dmflags & DF_FAST_MONSTERS)))
 	{ // Monsters move faster in nightmare mode
 		actor->tics -= actor->tics / 2;
@@ -1556,7 +1627,12 @@
 		}
 		if (player->attacker && player->attacker->health > 0 && player->attacker->flags & MF_SHOOTABLE && pr_newchasedir() < 80)
 		{
-			actor->target = player->attacker;
+			if (!(player->attacker->flags&MF_FRIENDLY) || 
+				(deathmatch && actor->FriendPlayer!=0 && player->attacker->FriendPlayer!=0 &&
+				 actor->FriendPlayer!=player->attacker->FriendPlayer))
+			{
+				actor->target = player->attacker;
+			}
 		}
 	}
 	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
@@ -1638,24 +1714,57 @@
 		goto nomissile;
 	}
 
+	// Strafe	(Hexen's class bosses)
+	// This was the sole reason for the separate A_FastChase function but
+	// it can be just as easily handled by a simple flag so the monsters
+	// can take advantage of all the other enhancements of A_Chase.
+
+	if (fastchase)
+	{
+		if (actor->special2 > 0)
+		{
+			actor->special2--;
+		}
+		else
+		{
+			actor->special2 = 0;
+			actor->momx = 0;
+			actor->momy = 0;
+			fixed_t dist = P_AproxDistance (actor->x - actor->target->x, actor->y - actor->target->y);
+			if (dist < CLASS_BOSS_STRAFE_RANGE)
+			{
+				if (pr_chase() < 100)
+				{
+					angle_t ang = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+					if (pr_chase() < 128) ang += ANGLE_90;
+					else ang -= ANGLE_90;
+					actor->momx = 13 * finecosine[ang>>ANGLETOFINESHIFT];
+					actor->momy = 13 * finesine[ang>>ANGLETOFINESHIFT];
+					actor->special2 = 3;		// strafe time
+				}
+			}
+		}
+
+	}
+
 	// [RH] Scared monsters attack less frequently
 	if (actor->target->player == NULL ||
 		!(actor->target->player->cheats & CF_FRIGHTENING) ||
 		pr_scaredycat() < 43)
 	{
 		// check for melee attack
-		if (actor->MeleeState && actor->CheckMeleeRange ())
+		if (meleestate && actor->CheckMeleeRange ())
 		{
 			if (actor->AttackSound)
 				S_SoundID (actor, CHAN_WEAPON, actor->AttackSound, 1, ATTN_NORM);
 
-			actor->SetState (actor->MeleeState);
+			actor->SetState (meleestate);
 			actor->flags &= ~MF_INCHASE;
 			return;
 		}
-		
+
 		// check for missile attack
-		if (actor->MissileState)
+		if (missilestate)
 		{
 			if (gameskill < sk_nightmare
 				&& actor->movecount && !(dmflags & DF_FAST_MONSTERS))
@@ -1666,7 +1775,7 @@
 			if (!P_CheckMissileRange (actor))
 				goto nomissile;
 			
-			actor->SetState (actor->MissileState);
+			actor->SetState (missilestate);
 			actor->flags |= MF_JUSTATTACKED;
 			actor->flags4 |= MF4_INCOMBAT;
 			actor->flags &= ~MF_INCHASE;
@@ -1699,14 +1808,43 @@
 		}
 	}
 	
+	//
 	// chase towards player
-	if (--actor->movecount < 0 || !P_Move (actor))
+	//
+
+
+	// class bosses don't do this when strafing
+	if (!fastchase || !actor->special2)
 	{
-		P_NewChaseDir (actor);
+		// CANTLEAVEFLOORPIC handling was completely missing in the non-serpent functions!
+		fixed_t oldX = actor->x;
+		fixed_t oldY = actor->y;
+		int oldFloor = actor->floorpic;
+
+		// chase towards player
+		if (--actor->movecount < 0 || !P_Move (actor))
+		{
+			P_NewChaseDir (actor);
+		}
+		
+		// if the move was illegal, reset it 
+		// (copied from A_SerpentChase - it applies to everything with CANTLEAVEFLOORPIC!)
+		if (actor->flags2&MF2_CANTLEAVEFLOORPIC && actor->floorpic != oldFloor )
+		{
+			if (P_TryMove (actor, oldX, oldY, false))
+			{
+				if (nomonsterinterpolation)
+				{
+					actor->PrevX = oldX;
+					actor->PrevY = oldY;
+				}
+			}
+			P_NewChaseDir (actor);
+		}
 	}
 	
 	// make active sound
-	if (pr_chase() < 3)
+	if (playactive && pr_chase() < 3)
 	{
 		actor->PlayActiveSound ();
 	}
@@ -1714,6 +1852,16 @@
 	actor->flags &= ~MF_INCHASE;
 }
 
+void A_Chase(AActor * actor)
+{
+	A_DoChase(actor, false, actor->MeleeState, actor->MissileState, true, gameinfo.gametype == GAME_Heretic);
+}
+
+void A_FastChase(AActor * actor)
+{
+	A_DoChase(actor, true, actor->MeleeState, actor->MissileState, true, true);
+}
+
 
 //
 // A_FaceTarget
@@ -1775,7 +1923,8 @@
 		actor->angle += pr_railface.Random2() << 21;
     }
 
-	P_RailAttack (actor, actor->damage, 0);
+	P_RailAttack (actor, actor->damage, 0,
+		ColorMatcher.Pick (0, 0, 255), ColorMatcher.Pick (255, 255, 255));
 }
 
 void A_Scream (AActor *actor)
@@ -2007,6 +2156,22 @@
 		MT_SORCERER2
 	} type;
 
+	// Do generic special death actions first
+	bool checked = false;
+	FSpecialAction * sa=level.info->specialactions;
+	while (sa)
+	{
+		if (!strcmp(sa->type,actor->GetClass()->Name+1))
+		{
+			if (!checked && !CheckBossDeath(actor)) return;
+			checked=true;
+
+			LineSpecials[sa->action](NULL, actor, false, 
+				sa->args[0], sa->args[1], sa->args[2], sa->args[3], sa->args[4]);
+		}
+		sa=sa->next;
+	}
+
 	// [RH] These all depend on the presence of level flags now
 	//		rather than being hard-coded to specific levels/episodes.
 
@@ -2190,158 +2355,6 @@
 	return done;		// Reached target height
 }
 
-//============================================================================
-// Class Bosses
-//============================================================================
-#define CLASS_BOSS_STRAFE_RANGE	64*10*FRACUNIT
-
-void A_FastChase (AActor *actor)
-{
-	int delta;
-	fixed_t dist;
-	angle_t ang;
-	AActor *target;
-
-	if (actor->reactiontime)
-	{
-		actor->reactiontime--;
-	}
-
-	// Modify target threshold
-	if (actor->threshold)
-	{
-		actor->threshold--;
-	}
-
-	if (gameskill == sk_nightmare)
-	{ // Monsters move faster in nightmare mode
-		actor->tics -= actor->tics/2;
-		if (actor->tics < 3)
-		{
-			actor->tics = 3;
-		}
-	}
-
-//
-// turn towards movement direction if not there yet
-//
-	if (actor->movedir < 8)
-	{
-		actor->angle &= (7<<29);
-		delta = actor->angle-(actor->movedir << 29);
-		if (delta > 0)
-		{
-			actor->angle -= ANG90/2;
-		}
-		else if (delta < 0)
-		{
-			actor->angle += ANG90/2;
-		}
-	}
-
-	if (!actor->target || !(actor->target->flags&MF_SHOOTABLE))
-	{ // look for a new target
-		if (P_LookForPlayers(actor, true))
-		{ // got a new target
-			return;
-		}
-		actor->SetState (actor->SpawnState);
-		return;
-	}
-
-//
-// don't attack twice in a row
-//
-	if (actor->flags & MF_JUSTATTACKED)
-	{
-		actor->flags &= ~MF_JUSTATTACKED;
-		if (gameskill != sk_nightmare)
-			P_NewChaseDir (actor);
-		return;
-	}
-
-	// Strafe
-	if (actor->special2 > 0)
-	{
-		actor->special2--;
-	}
-	else
-	{
-		target = actor->target;
-		actor->special2 = 0;
-		actor->momx = actor->momy = 0;
-		dist = P_AproxDistance (actor->x - target->x,
-								actor->y - target->y);
-		if (dist < CLASS_BOSS_STRAFE_RANGE)
-		{
-			if (pr_fastchase() < 100)
-			{
-				ang = R_PointToAngle2(actor->x, actor->y,
-									target->x, target->y);
-				if (pr_fastchase() < 128)
-					ang += ANGLE_90;
-				else
-					ang -= ANGLE_90;
-				ang >>= ANGLETOFINESHIFT;
-				actor->momx = 13 * finecosine[ang];
-				actor->momy = 13 * finesine[ang];
-				actor->special2 = 3;		// strafe time
-			}
-		}
-	}
-
-//
-// check for missile attack
-//
-	if (actor->MissileState)
-	{
-		if (gameskill < sk_nightmare && actor->movecount)
-			goto nomissile;
-		if (!P_CheckMissileRange (actor))
-			goto nomissile;
-		actor->SetState (actor->MissileState);
-		actor->flags |= MF_JUSTATTACKED;
-		actor->flags4 |= MF4_INCOMBAT;
-		return;
-	}
-nomissile:
-
-//
-// possibly choose another target
-//
-	if ((multiplayer || actor->TIDtoHate)
-		&& !actor->threshold
-		&& !P_CheckSight (actor, actor->target, 0) )
-	{
-		bool lookForBetter = false;
-		BOOL gotNew;
-		if (actor->flags3 & MF3_NOSIGHTCHECK)
-		{
-			actor->flags3 &= ~MF3_NOSIGHTCHECK;
-			lookForBetter = true;
-		}
-		gotNew = P_LookForPlayers (actor, true);
-		if (lookForBetter)
-		{
-			actor->flags3 |= MF3_NOSIGHTCHECK;
-		}
-		if (gotNew)
-		{
-			return; 	// got a new target
-		}
-	}
-
-//
-// chase towards player
-//
-	if (!actor->special2)
-	{
-		if (--actor->movecount<0 || !P_Move (actor))
-		{
-			P_NewChaseDir (actor);
-		}
-	}
-}
 
 void A_ClassBossHealth (AActor *actor)
 {
diff -durN 96/src/p_enemy.h 96x/src/p_enemy.h
--- 96/src/p_enemy.h	2004-12-24 15:55:30.000000000 +0100
+++ 96x/src/p_enemy.h	2005-06-22 15:18:33.000000000 +0200
@@ -19,6 +19,7 @@
 
 extern fixed_t xspeed[8], yspeed[8];
 
+bool P_HitFriend(AActor * self);
 void P_NoiseAlert (AActor *target, AActor *emmiter, bool splash=false);
 bool P_CheckMeleeRange2 (AActor *actor);
 BOOL P_Move (AActor *actor);
diff -durN 96/src/p_floor.cpp 96x/src/p_floor.cpp
--- 96/src/p_floor.cpp	2004-08-25 20:05:32.000000000 +0200
+++ 96x/src/p_floor.cpp	2005-06-22 15:18:33.000000000 +0200
@@ -335,7 +335,6 @@
 		floor->m_ResetCount = 0;				// [RH]
 		floor->m_OrgDist = sec->floorplane.d;	// [RH]
 
-		floor->StartFloorSound ();
 
 		switch (floortype)
 		{
@@ -498,10 +497,28 @@
 		// Do not interpolate instant movement floors.
 		// Note for ZDoomGL: Check to make sure that you update the sector
 		// after the floor moves, because it hasn't actually moved yet.
-		if (floor->m_Speed >= abs(sec->floorplane.d - floor->m_FloorDestDist))
+		bool silent=false;
+
+		if ((floor->m_Direction>0 && floor->m_FloorDestDist>sec->floorplane.d) ||	// moving up but going down
+			(floor->m_Direction<0 && floor->m_FloorDestDist<sec->floorplane.d) ||	// moving down but going up
+			(floor->m_Speed >= abs(sec->floorplane.d - floor->m_FloorDestDist)))	// moving in one step
 		{
 			stopinterpolation (INTERP_SectorFloor, sec);
+
+			// [Graf Zahl]
+			// Don't make sounds for instant movement hacks but make an exception for
+			// switches that activate their own back side. 
+			// I'll leave the decision about this to somebody else. In many maps 
+			// it helps but there are some where this omits sounds that should be there.
+			#ifdef SILENT_INSTANT_FLOORS
+			if (floortype!=DFloor::floorRaiseInstant && floortype!=DFloor::floorLowerInstant) 
+			{
+				if (!line || GET_SPAC(line->flags)!=SPAC_USE || line->backsector!=sec)
+					silent=true;
+			}
+			#endif
 		}
+		if (!silent) floor->StartFloorSound ();
 
 		if (change & 3)
 		{
diff -durN 96/src/p_interaction.cpp 96x/src/p_interaction.cpp
--- 96/src/p_interaction.cpp	2004-12-22 13:45:34.000000000 +0100
+++ 96x/src/p_interaction.cpp	2005-07-25 13:14:10.000000000 +0200
@@ -298,7 +298,11 @@
 			default:
 				if (attacker->player->ReadyWeapon != NULL)
 				{
-					message = attacker->player->ReadyWeapon->GetObituary ();
+					message = attacker->player->ReadyWeapon->GetClass()->Meta.GetMetaString (AMETA_Obituary);
+					if (message == NULL)
+					{
+						message = attacker->player->ReadyWeapon->GetObituary ();
+					}
 				}
 				break;
 			case MOD_ROCKET:		messagenum = OB_MPROCKET;		break;
@@ -598,6 +602,10 @@
 			// don't die in auto map, switch view prior to dying
 			AM_Stop ();
 		}
+
+		// [GRB] Clear extralight. When you killed yourself with weapon that
+		// called A_Light1/2 before it called A_Light0, extraligh remained.
+		player->extralight = 0;
 	}
 
 	// [RH] If this is the unmorphed version of another monster, destroy this
@@ -622,7 +630,6 @@
 		(!deh.NoAutofreeze && !(flags4 & MF4_NOICEDEATH)) &&
 		(player || (flags3 & MF3_ISMONSTER)))))
 	{ // Ice death
-		flags |= MF_ICECORPSE;
 		if (IDeathState)
 		{
 			SetState (IDeathState);
@@ -632,18 +639,24 @@
 			SetState (&AActor::States[S_GENERICFREEZEDEATH]);
 		}
 	}
-	else if (XDeathState &&
-		health < (gameinfo.gametype == GAME_Doom
-				  ? -GetDefault()->health : -GetDefault()->health/2))
-	{ // Extreme death
-		SetState (XDeathState);
-	}
 	else
-	{ // Normal death
+	{
+		int flags4 = !inflictor ? 0 : inflictor->player && inflictor->player->ReadyWeapon ? 
+			inflictor->player->ReadyWeapon->flags4 : inflictor->flags4;
+
+		int gibhealth = -abs(GetClass()->Meta.GetMetaInt (AMETA_GibHealth));
+		if (gibhealth == 0) gibhealth = (gameinfo.gametype == GAME_Doom ? -GetDefault()->health : -GetDefault()->health/2);
+		
 		DamageType = MOD_UNKNOWN;	// [RH] "Frozen" barrels shouldn't do freezing damage
-		SetState (DeathState);
+		if (XDeathState && (health<gibhealth || flags4 & MF4_EXTREMEDEATH) && !(flags4 & MF4_NOEXTREMEDEATH))
+		{ // Extreme death
+			SetState (XDeathState);
+		}
+		else
+		{ // Normal death
+			SetState (DeathState);
+		}
 	}
-
 	tics -= pr_killmobj() & 3;
 	if (tics < 1)
 		tics = 1;
@@ -778,6 +791,25 @@
 	{ // Shouldn't happen
 		return;
 	}
+
+	// Cheap ass MoD exclusion
+	if ((!target->DeathState) && (target->IDeathState || target->BDeathState || target->EDeathState))
+	{
+		if (mod==MOD_ICE)
+		{
+			if (!target->IDeathState) return;
+		}
+		else if (mod==MOD_FIRE)
+		{
+			if (!target->BDeathState) return;
+		}
+		else if (mod==MOD_DISINTEGRATE)
+		{
+			if (!target->EDeathState) return;
+		}
+		else return;
+	}
+
 	// Spectral targets only take damage from spectral projectiles.
 	if (target->flags4 & MF4_SPECTRAL && damage < 1000000)
 	{
@@ -1066,12 +1098,20 @@
 		target->Die (source, inflictor);
 		return;
 	}
-	if (target->health <= 6 && target->WoundState != NULL)
+	if (target->WoundState != NULL)
 	{
-		target->SetState (target->WoundState);
-		return;
+		int woundhealth = RUNTIME_TYPE(target)->Meta.GetMetaInt (AMETA_WoundHealth);
+		if (!woundhealth)
+			woundhealth = 6;
+
+		if (target->health <= woundhealth)
+		{
+			target->SetState (target->WoundState);
+			return;
+		}
 	}
-	if ((pr_damagemobj() < target->PainChance)
+	// [GrafZahl] A missing pain state should not make the monster disappear!
+	if ((pr_damagemobj() < target->PainChance && target->PainState)
 		 && !(target->flags & MF_SKULLFLY))
 	{
 		if (inflictor && inflictor->IsKindOf (RUNTIME_CLASS(ALightning)))
@@ -1138,24 +1178,44 @@
 	}
 }
 
+
 bool AActor::OkayToSwitchTarget (AActor *other)
 {
 	if (other == this)
 		return false;		// [RH] Don't hate self (can happen when shooting barrels)
+
+	if (!(other->flags&MF_SHOOTABLE))
+		return false;		// Don't attack things that can't be hurt
+		
+	if ((flags4 & MF4_NOTARGETSWITCH) && target!=NULL)
+		return false;		// Don't switch target if not allowed
+	
+	if ((master != NULL && other->IsA(master->GetClass())) ||		// don't attack your master (or others of its type)
+		(other->master != NULL && IsA(other->master->GetClass())))	// don't attack your minion (or those of others of your type)
+	{
+		if (!P_IsHostile(this, other) &&						// allow target switch if other is considered hostile
+			(other->tid != TIDtoHate || TIDtoHate == 0) &&		// or has the tid we hate
+			other->TIDtoHate == TIDtoHate)						// or has different hate information
+		{
+			return false;
+		}
+	}
+
 	if ((other->flags3 & MF3_NOTARGET) &&
 		(other->tid != TIDtoHate || TIDtoHate == 0) &&
-		((other->flags ^ flags) & MF_FRIENDLY) == 0)
+		!P_IsHostile(this, other))
+	{
 		return false;
+	}
+
 	if (threshold != 0 && !(flags4 & MF4_QUICKTORETALIATE))
 		return false;
-	if (other->flags & flags & MF_FRIENDLY)
+
+	if (P_IsFriend(this, other))
 	{ // [RH] Friendlies don't target other friendlies
-		if (!deathmatch || other->FriendPlayer == 0 || FriendPlayer == 0 ||
-			other->FriendPlayer != FriendPlayer)
-		{
-			return false;
-		}
+		return false;
 	}
+
 	if ((flags & MF_FRIENDLY) && other->player != NULL)
 	{ // [RH] Friendlies don't target their player friends either
 		if (!deathmatch || other->player - players == FriendPlayer+1)
@@ -1163,11 +1223,12 @@
 			return false;
 		}
 	}
-	if (gameinfo.gametype == GAME_Strife)
-		if (!(other->flags & MF_FRIENDLY) && !(flags & MF_FRIENDLY))
-			return false;	// Strife: Non-friendlies don't target other non-friendlies
-	if (infighting < 0 && other->player == NULL)
-		return false;		// [RH] Allow monsters to ignore each other
+
+	if ((gameinfo.gametype == GAME_Strife || infighting < 0) && 
+		other->player == NULL && !P_IsHostile(this, other))
+	{
+		return false;	// Strife & infighting off: Non-friendlies don't target other non-friendlies
+	}
 	if (TIDtoHate != 0 && TIDtoHate == other->TIDtoHate)
 		return false;		// [RH] Don't target "teammates"
 	if (other->player != NULL && (flags4 & MF4_NOHATEPLAYERS))
diff -durN 96/src/p_lights.cpp 96x/src/p_lights.cpp
--- 96/src/p_lights.cpp	2004-01-17 21:27:58.000000000 +0100
+++ 96x/src/p_lights.cpp	2005-07-24 20:12:09.000000000 +0200
@@ -32,6 +32,7 @@
 
 // State.
 #include "r_state.h"
+#include "statnums.h"
 
 static FRandom pr_flicker ("Flicker");
 static FRandom pr_lightflash ("LightFlash");
@@ -47,6 +48,7 @@
 DLighting::DLighting (sector_t *sector)
 	: DSectorEffect (sector)
 {
+	ChangeStatNum(STAT_LIGHT);
 }
 
 //
@@ -710,7 +712,7 @@
 	{
 		if (effect->GetSector()->tag == tag)
 		{
-			delete effect;
+			effect->Destroy();
 		}
 	}
 }
diff -durN 96/src/p_lnspec.cpp 96x/src/p_lnspec.cpp
--- 96/src/p_lnspec.cpp	2004-11-24 14:18:29.000000000 +0100
+++ 96x/src/p_lnspec.cpp	2005-06-22 17:49:43.000000000 +0200
@@ -793,9 +793,29 @@
 }
 
 FUNC(LS_ThrustThing)
-// ThrustThing (angle, force, nolimit)
+// ThrustThing (angle, force, nolimit, tid)
 {
-	if (it)
+	if (arg3!=0)
+	{
+		AActor * victim;
+
+		angle_t angle = BYTEANGLE(arg0) >> ANGLETOFINESHIFT;
+
+		FActorIterator iterator (arg3);
+
+		while ( (victim = iterator.Next ()) )
+		{
+			victim->momx += arg1 * finecosine[angle];
+			victim->momy += arg1 * finesine[angle];
+			if (!arg2)
+			{
+				victim->momx = clamp<fixed_t> (victim->momx, -MAXMOVE, MAXMOVE);
+				victim->momy = clamp<fixed_t> (victim->momy, -MAXMOVE, MAXMOVE);
+			}
+		}
+		return true;
+	}
+	else if (it)
 	{
 		angle_t angle = BYTEANGLE(arg0) >> ANGLETOFINESHIFT;
 
@@ -1016,6 +1036,9 @@
 	while (actor)
 	{
 		AActor *temp = iterator.Next ();
+		// be friendly to the level statistics! ;)
+		if (actor->flags&MF_COUNTKILL && actor->health > 0) level.total_monsters--;
+		if (actor->flags&MF_COUNTITEM) level.total_items--;
 		actor->Destroy ();
 		actor = temp;
 	}
diff -durN 96/src/p_lnspec.h 96x/src/p_lnspec.h
--- 96/src/p_lnspec.h	2004-11-04 09:22:47.000000000 +0100
+++ 96x/src/p_lnspec.h	2005-06-22 15:18:33.000000000 +0200
@@ -346,6 +346,9 @@
 	sDamage_SuperHellslime = 116,
 	Scroll_StrifeCurrent = 118,
 
+	// Caverns of Darkness healing sector
+	Sector_Heal = 196,
+
 	Light_OutdoorLightning = 197,
 	Light_IndoorLightning1 = 198,
 	Light_IndoorLightning2 = 199,
diff -durN 96/src/p_local.h 96x/src/p_local.h
--- 96/src/p_local.h	2004-11-18 18:03:54.000000000 +0100
+++ 96x/src/p_local.h	2005-07-25 17:54:03.000000000 +0200
@@ -99,7 +99,7 @@
 bool P_SeekerMissile (AActor *actor, angle_t thresh, angle_t turnMax);
 
 AActor *P_SpawnPuff (const TypeInfo *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, bool hit=false);
-void	P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, angle_t dir, int damage);
+void	P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, angle_t dir, int damage, AActor * originator);
 void	P_BloodSplatter (fixed_t x, fixed_t y, fixed_t z, AActor *originator);
 void	P_BloodSplatter2 (fixed_t x, fixed_t y, fixed_t z, AActor *originator);
 void	P_RipperBlood (AActor *mo);
@@ -275,6 +275,9 @@
 extern bool				DoRipping;
 extern AActor			*LastRipped;
 
+bool P_IsFriend(AActor * self, AActor * other);
+bool P_IsHostile(AActor * self, AActor * other);
+
 BOOL	P_TestMobjLocation (AActor *mobj);
 bool	P_TestMobjZ (AActor *mobj);
 BOOL	P_CheckPosition (AActor *thing, fixed_t x, fixed_t y);
@@ -303,7 +306,7 @@
 void	P_TraceBleed (int damage, AActor *target, angle_t angle, int pitch);
 void	P_TraceBleed (int damage, AActor *target, AActor *missile);		// missile version
 void	P_TraceBleed (int damage, AActor *target);		// random direction version
-void	P_RailAttack (AActor *source, int damage, int offset);	// [RH] Shoot a railgun
+void	P_RailAttack (AActor *source, int damage, int offset, int color1, int color2, float maxdiff = 0, bool silent = false);	// [RH] Shoot a railgun
 bool	P_HitFloor (AActor *thing);
 bool	P_HitWater (AActor *thing, sector_t *sec);
 bool	P_CheckMissileSpawn (AActor *missile);
diff -durN 96/src/p_map.cpp 96x/src/p_map.cpp
--- 96/src/p_map.cpp	2004-12-18 20:22:53.000000000 +0100
+++ 96x/src/p_map.cpp	2005-07-25 17:53:43.000000000 +0200
@@ -268,10 +268,18 @@
 	}
 
 	// [RH] Z-Check
-	if (tmz > thing->z + thing->height)
-		return true;        // overhead
-	if (tmz+tmthing->height < thing->z)
-		return true;        // underneath
+	// But not if not MF2_PASSMOBJ or MF3_DONTOVERLAP are set!
+	// Otherwise those things would get stuck inside each other.
+	if (tmthing->flags2&MF2_PASSMOBJ)
+	{
+		if (!(thing->flags3 & tmthing->flags3 & MF3_DONTOVERLAP))
+		{
+			if (tmz > thing->z + thing->height)
+				return true;        // overhead
+			if (tmz+tmthing->height < thing->z)
+				return true;        // underneath
+		}
+	}
 
 	// monsters don't stomp things except on boss level
 	// [RH] Some Heretic/Hexen monsters can telestomp
@@ -743,7 +751,8 @@
 		// 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.
-		openbottom == tmthing->Sector->floorplane.ZatPoint (sx, sy))
+		(openbottom == tmthing->Sector->floorplane.ZatPoint (sx, sy) ||
+		gameinfo.gametype != GAME_Strife || level.flags & LEVEL_HEXENFORMAT))
 	{
 		openbottom += 32*FRACUNIT;
 	}
@@ -775,9 +784,68 @@
 	return true;
 }
 
+
+//==========================================================================
+//
+// Species is defined as the lowest base class that is a monster
+// with no non-monster class in between
+//
+//==========================================================================
+
+const TypeInfo * P_GetSpecies(AActor * self)
+{
+	const TypeInfo * thistype=self->GetClass();
+
+	if (GetDefaultByType(thistype)->flags3 & MF3_ISMONSTER) while (thistype->ParentType)
+	{
+		if (GetDefaultByType(thistype->ParentType)->flags3 & MF3_ISMONSTER)
+			thistype=thistype->ParentType;
+		else 
+			break;
+	}
+	return thistype;
+}
+
+//==========================================================================
+//
+// Checks whether 2 monsters have to be considered friendly
+//
+//==========================================================================
+
+bool P_IsFriend(AActor * self, AActor * other)
+{
+	if (self->flags & other->flags&MF_FRIENDLY)
+	{
+		return !deathmatch || self->FriendPlayer==other->FriendPlayer || self->FriendPlayer==0 || other->FriendPlayer==0;
+	}
+	return false;
+}
+
+//==========================================================================
+//
+// Checks whether 2 monsters have to be considered hostile under any circumstances
+//
+//==========================================================================
+
+bool P_IsHostile(AActor * self, AActor * other)
+{
+	// Both monsters are non-friendlies so hostilities depend on infighting settings
+	if (!((self->flags | other->flags) & MF_FRIENDLY)) return false;
+
+	// Both monsters are friendly and belong to the same player if applicable.
+	if (self->flags & other->flags&MF_FRIENDLY)
+	{
+		return deathmatch && self->FriendPlayer!=other->FriendPlayer && self->FriendPlayer!=0 && other->FriendPlayer!=0;
+	}
+	return true;
+}
+
+//==========================================================================
 //
 // PIT_CheckThing
 //
+//==========================================================================
+
 static AActor *stepthing;
 
 BOOL PIT_CheckThing (AActor *thing)
@@ -901,31 +969,71 @@
 
 		// [RH] Extend DeHacked infighting to allow for monsters
 		// to never fight each other
-		if ((infighting < 0 && tmthing->target != NULL && (tmthing->target->flags & MF_SHOOTABLE)) &&
-			!thing->player &&
-			(!tmthing->target || !tmthing->target->player) &&
-			thing != tmthing->target)
-		{
-			return false;
-		}
 
-		if (thing == tmthing->target)
-		{ // Don't missile self
-			return true;
-		}
+		// [Graf Zahl] Why do I have the feeling that this didn't really work anymore now
+		// that ZDoom supports friendly monsters?
 
-		if (tmthing->target && (
-			tmthing->target->IsKindOf (RUNTIME_TYPE (thing)) ||
-			thing->IsKindOf (RUNTIME_TYPE (tmthing->target)) ||
-			(thing->TIDtoHate != 0 && thing->TIDtoHate == tmthing->target->TIDtoHate)) )
-			// [RH] Don't hurt monsters that hate the same thing as you do
-		{ // Don't hit same species as originator.
-			// [RH] DeHackEd infighting is here.
-			if ((infighting == 0) && !thing->player)
-			{ // Hit same species as originator => explode, no damage
-				return false;
+		if (tmthing->target != NULL)
+		{
+			if (thing == tmthing->target)
+			{ // Don't missile self
+				return true;
+			}
+			// players are never subject to infighting settings and are always allowed
+			// to harm / be harmed by anything.
+			if (!thing->player && !tmthing->target->player)
+			{
+				if (infighting < 0)
+				{
+					// -1: Monsters cannot hurt each other, but make exceptions for
+					//     friendliness and hate status.
+					if (tmthing->target->flags & MF_SHOOTABLE)
+					{
+						if (!(thing->flags3 & MF3_ISMONSTER))
+						{
+							return false;	// Question: Should monsters be allowed to shoot barrels in this mode?
+											// The old code does not.
+						}
+
+						// Monsters that are clearly hostile can always hurt each other
+						if (!P_IsHostile(thing, tmthing->target))
+						{
+							// The same if the shooter hates the target
+							if (thing->tid == 0 || tmthing->target->tid != thing->tid)
+							{
+								return false;
+							}
+						}
+					}
+				}
+				else if (infighting == 0)
+				{
+					//  0: Monsters cannot hurt same species except 
+					//     cases where they are clearly supposed to do that
+					if (P_IsFriend(thing, tmthing->target))
+					{
+						// Friends never harm each other
+						return false;
+					}
+					if (thing->TIDtoHate != 0 && thing->TIDtoHate == tmthing->target->TIDtoHate)
+					{
+						// [RH] Don't hurt monsters that hate the same thing as you do
+						return false;
+					}
+					if (P_GetSpecies(thing) == P_GetSpecies(tmthing->target))
+					{
+						// Don't hurt same species or any relative
+						if (!P_IsHostile(thing, tmthing->target))
+						{
+							// But only if the target isn't one's hostile.
+							return false;
+						}
+					}
+				}
+				// else if (infighting==1) any shot hurts anything - no further tests
 			}
 		}
+
 		if (!(thing->flags & MF_SHOOTABLE))
 		{ // Didn't do any damage
 			return !(thing->flags & MF_SOLID);
@@ -941,6 +1049,7 @@
 				LastRipped = thing;
 				if (!(thing->flags & MF_NOBLOOD) &&
 					!(thing->flags2 & MF2_REFLECTIVE) &&
+					!(tmthing->flags3 & MF3_BLOODLESSIMPACT) &&
 					!(thing->flags2 & (MF2_INVULNERABLE|MF2_DORMANT)))
 				{ // Ok to spawn blood
 					P_RipperBlood (tmthing);
@@ -982,7 +1091,9 @@
 			{
 				P_BloodSplatter (tmthing->x, tmthing->y, tmthing->z, thing);
 			}
-			else if (!(tmthing->flags3 & MF3_BLOODLESSIMPACT))
+			// No blood decal when there is a blood splatter sprite?
+			// That can't be right, can it?
+			/*else*/ if (!(tmthing->flags3 & MF3_BLOODLESSIMPACT))
 			{
 				P_TraceBleed (damage, thing, tmthing);
 			}
@@ -2644,6 +2755,14 @@
 		}
 		else
 		{
+			bool axeBlood;
+
+			// AXEBLOOD is a weapon flag so if it set it should also work in Doom, right?
+			axeBlood = (t1->player != NULL &&
+				t1->player->ReadyWeapon != NULL &&
+				(t1->player->ReadyWeapon->WeaponFlags & WIF_AXEBLOOD));
+
+
 			// Hit a thing, so it could be either a puff or blood
 			hitx = t1->x + FixedMul (vx, trace.Distance);
 			hity = t1->y + FixedMul (vy, trace.Distance);
@@ -2657,23 +2776,17 @@
 			{
 				puff = P_SpawnPuff (pufftype, hitx, hity, hitz, angle - ANG180, 2, true);
 			}
-			if ((gameinfo.gametype & (GAME_Doom|GAME_Strife)) &&
+			if ((gameinfo.gametype & (GAME_DoomStrife)) && !axeBlood &&
 				!(trace.Actor->flags & MF_NOBLOOD) &&
 				!(trace.Actor->flags2 & (MF2_INVULNERABLE|MF2_DORMANT)))
 			{
-				P_SpawnBlood (hitx, hity, hitz, angle - ANG180, damage);
+				P_SpawnBlood (hitx, hity, hitz, angle - ANG180, damage, trace.Actor);
 			}
 
 			if (damage)
 			{
-				if (gameinfo.gametype != GAME_Doom)
+				if ((gameinfo.gametype&GAME_Raven) || axeBlood)
 				{
-					bool axeBlood;
-
-					axeBlood = (t1->player != NULL &&
-						t1->player->ReadyWeapon != NULL &&
-						(t1->player->ReadyWeapon->WeaponFlags & WIF_AXEBLOOD));
-
 					if (!(trace.Actor->flags&MF_NOBLOOD) &&
 						!(trace.Actor->flags2&(MF2_INVULNERABLE|MF2_DORMANT)))
 					{
@@ -2774,9 +2887,20 @@
 		{
 			if (bleedtrace.HitType == TRACE_HitWall)
 			{
+				PalEntry bloodcolor = (PalEntry)actor->GetClass()->Meta.GetMetaInt(AMETA_BloodColor);
+				if (bloodcolor != 0)
+				{
+					bloodcolor.r>>=1;	// the full color is too bright for blood decals
+					bloodcolor.g>>=1;
+					bloodcolor.b>>=1;
+					bloodcolor.a=1;
+				}
+
 				AImpactDecal::StaticCreate (bloodType,
 					bleedtrace.X, bleedtrace.Y, bleedtrace.Z,
-					sides + bleedtrace.Line->sidenum[bleedtrace.Side]);
+					sides + bleedtrace.Line->sidenum[bleedtrace.Side],
+					bloodcolor);
+
 			}
 		}
 	}
@@ -2854,7 +2978,7 @@
 	return true;
 }
 
-void P_RailAttack (AActor *source, int damage, int offset)
+void P_RailAttack (AActor *source, int damage, int offset, int color1, int color2, float maxdiff, bool silent)
 {
 	fixed_t vx, vy, vz;
 	angle_t angle, pitch;
@@ -2944,14 +3068,14 @@
 		}
 		else
 		{
-			P_SpawnBlood (x, y, z, source->angle - ANG180, damage);
+			P_SpawnBlood (x, y, z, source->angle - ANG180, damage, RailHits[i].HitActor);
 		}
 		P_DamageMobj (RailHits[i].HitActor, source, source, damage, MOD_RAILGUN);
 		P_TraceBleed (damage, x, y, z, RailHits[i].HitActor, angle, pitch);
 	}
 
 	VectorFixedSet (end, trace.X, trace.Y, trace.Z);
-	P_DrawRailTrail (start, end);
+	P_DrawRailTrail (source, start, end, color1, color2, maxdiff, silent);
 }
 
 //
@@ -3007,6 +3131,15 @@
 	// [RH] Check for things to talk with
 	if (!in->isaline)
 	{
+		if (usething==in->d.thing) return true;
+
+		// enable activation of puzzle items by pressing +use
+		if (in->d.thing->special==UsePuzzleItem)
+		{
+			if (LineSpecials[UsePuzzleItem](NULL, usething, false, in->d.thing->args[0], in->d.thing->args[1], 0, 0, 0))
+				return false;
+		}
+
 		// Dead things can't talk.
 		if (in->d.thing->health <= 0)
 		{
@@ -3038,6 +3171,7 @@
 	if (in->d.line->special == 0 || (GET_SPAC(in->d.line->flags) != SPAC_USETHROUGH &&
 		GET_SPAC(in->d.line->flags) != SPAC_USE))
 	{
+blocked:
 		if (in->d.line->flags & ML_BLOCKEVERYTHING)
 		{
 			openrange = 0;
@@ -3066,7 +3200,8 @@
 		
 	if (P_PointOnLineSide (usething->x, usething->y, in->d.line) == 1)
 		// [RH] continue traversal for two-sided lines
-		return in->d.line->backsector != NULL;		// don't use back side
+		//return in->d.line->backsector != NULL;		// don't use back side
+		goto blocked;	// do a proper check for back sides of triggers!
 		
 	P_ActivateLine (in->d.line, usething, 0, SPAC_USE);
 
@@ -3097,17 +3232,16 @@
 
 BOOL PTR_NoWayTraverse (intercept_t *in)
 {
-	line_t *ld = in->d.line;					// This linedef
+	line_t *ld = in->d.line;
 
-	return ld->special || !(					// Ignore specials
-		ld->flags & (ML_BLOCKING|ML_BLOCKEVERYTHING) || (		// Always blocking
-		P_LineOpening(ld, trace.x + FixedMul (trace.dx, in->frac),
-			trace.y + FixedMul (trace.dy, in->frac)),			// Find openings
-		openrange <= 0 ||						// No opening
-		openbottom > usething->z+gameinfo.StepHeight ||	// Too high it blocks
-		opentop < usething->z+usething->height	// Too low it blocks
-	)
-	);
+	// [GrafZahl] de-obfuscated. Was I the only one who was unable to makes sense out of
+	// this convoluted mess?
+	if (ld->special) return true;
+	if (ld->flags&(ML_BLOCKING|ML_BLOCKEVERYTHING)) return false;
+	P_LineOpening(ld, trace.x+FixedMul(trace.dx, in->frac),trace.y+FixedMul(trace.dy, in->frac));
+	return  openrange >0 && 
+			openbottom <= usething->z + gameinfo.StepHeight &&
+			opentop >= usething->z + usething->height;
 }
 
 /*
@@ -3287,6 +3421,16 @@
 		return true;
 	}
 
+	// a much needed option: monsters that fire explosive projectiles cannot 
+	// be hurt by projectiles fired by a monster of the same type.
+	// Controlled by the DONTHURTSPECIES flag.
+	if (bombsource && 
+		thing->GetClass() == bombsource->GetClass() && 
+		!thing->player &&
+		bombsource->flags4 & MF4_DONTHURTSPECIES
+		) return true;
+
+
 	// Barrels always use the original code, since this makes
 	// them far too "active." BossBrains also use the old code
 	// because some user levels require they have a height of 16,
@@ -3675,6 +3819,14 @@
 		!(thing->flags3 & MF3_DONTGIB) &&
 		(thing->health <= 0))
 	{
+		if (thing->CrushState && !(thing->flags & MF_ICECORPSE))
+		{
+			// Clear MF_CORPSE so that this isn't done more than once
+			thing->flags&=~(MF_CORPSE|MF_SOLID);
+			thing->height=thing->radius=0;
+			thing->SetState(thing->CrushState);
+			return;
+		}
 		if (!(thing->flags & MF_NOBLOOD))
 		{
 			AActor *gib = Spawn<ARealGibs> (thing->x, thing->y, thing->z);
@@ -3683,6 +3835,9 @@
 			gib->height = 0;
 			gib->radius = 0;
 			S_Sound (thing, CHAN_BODY, "misc/fallingsplat", 1, ATTN_IDLE);
+
+			PalEntry bloodcolor = (PalEntry)thing->GetClass()->Meta.GetMetaInt(AMETA_BloodColor);
+			if (bloodcolor!=0) gib->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
 		}
 		if (thing->flags & MF_ICECORPSE)
 		{
@@ -3704,6 +3859,14 @@
 
 	// crunch dropped items
 	if (thing->flags & MF_DROPPED)
+    {
+    	// [GrafZahl] Just a quick fix to prevent items in a player's inventory from being crushed
+    	// I still think this should be taken care of by the inventory system, not a special check here.
+		if (!thing->IsKindOf(RUNTIME_CLASS(AInventory)) || !static_cast<AInventory*>(thing)->Owner)
+			thing->Destroy ();
+		return;		// keep checking
+    }
+	if (thing->flags & MF_DROPPED)
 	{
 		thing->Destroy ();
 		return;		// keep checking
@@ -3729,6 +3892,8 @@
 		if ((!(thing->flags&MF_NOBLOOD)) &&
 			(!(thing->flags2&(MF2_INVULNERABLE|MF2_DORMANT))))
 		{
+			PalEntry bloodcolor = (PalEntry)thing->GetClass()->Meta.GetMetaInt(AMETA_BloodColor);
+
 			P_TraceBleed (crushchange, thing);
 			if (cl_bloodtype <= 1)
 			{
@@ -3739,6 +3904,7 @@
 
 				mo->momx = pr_crunch.Random2 () << 12;
 				mo->momy = pr_crunch.Random2 () << 12;
+				if (bloodcolor!=0) mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
 			}
 			if (cl_bloodtype >= 1)
 			{
@@ -3746,7 +3912,7 @@
 
 				an = (M_Random () - 128) << 24;
 				P_DrawSplash2 (32, thing->x, thing->y,
-							   thing->z + thing->height/2, an, 2, 0);
+							   thing->z + thing->height/2, an, 2, bloodcolor);
 			}
 		}
 	}
diff -durN 96/src/p_mobj.cpp 96x/src/p_mobj.cpp
--- 96/src/p_mobj.cpp	2004-12-24 17:11:40.000000000 +0100
+++ 96x/src/p_mobj.cpp	2005-07-24 20:12:09.000000000 +0200
@@ -41,6 +41,7 @@
 #include "b_bot.h"	//Added by MC:
 #include "stats.h"
 #include "a_doomglobal.h"
+#include "a_hexenglobal.h"
 #include "a_sharedglobal.h"
 #include "gi.h"
 #include "sbar.h"
@@ -163,6 +164,7 @@
  DECLARE_POINTER (LastLook)	// This is actually a union
  DECLARE_POINTER (Inventory)
  DECLARE_POINTER (LastHeard)
+ DECLARE_POINTER (master)
 END_POINTERS
 
 AActor::~AActor ()
@@ -311,6 +313,26 @@
 		<< EDeathState
 		<< RaiseState
 		<< WoundState;
+	if (SaveVersion >= 228)
+	{
+		arc << HealState
+			<< YesState
+			<< NoState
+			<< GreetingsState;
+	}
+	if (SaveVersion >= 229)
+	{
+		arc << user[0]
+			<< user[1]
+			<< user[2]
+			<< user[3]
+			<< user[4]
+			<< user[5]
+			<< user[6]
+			<< user[7]
+			<< meleerange
+			<< CrushState;
+	}
 	if (SaveVersion >= 224)
 	{
 		arc << DamageType;
@@ -487,6 +509,14 @@
 		}
 		if (newstate->GetAction())
 		{
+			// The parameterized action functions need access to the current state and
+			// if a function is supposed to work with both actors and weapons
+			// there is no real means to get to it reliably so I store it in a global variable here.
+			// Yes, I know this is truly awful but it is the only method I can think of 
+			// that does not involve changing stuff throughout the code. 
+			// Of course this should be rewritten ASAP.
+			CallingState=newstate;
+
 			newstate->GetAction() (this);
 		}
 		newstate = newstate->GetNextState();
@@ -646,7 +676,9 @@
 	{
 		return false;
 	}
-	if (!item->Use (false))
+	// Changed this call to an intermediate function
+	// so use state calls can be done independently of subclass.
+	if (!item->DoUse (false))
 	{
 		return false;
 	}
@@ -721,7 +753,7 @@
 	AInventory *item;
 
 	item = static_cast<AInventory *>(Spawn (type, 0,0,0));
-	if (!item->TryPickup (this))
+	if (!item->DoTryPickup (this))
 	{
 		item->Destroy ();
 		return NULL;
@@ -742,7 +774,7 @@
 	AInventory *item = static_cast<AInventory *>(Spawn (type, 0, 0, 0));
 	item->Amount = amount;
 	item->flags |= MF_DROPPED;
-	if (!item->TryPickup (this))
+	if (!item->DoTryPickup (this))
 	{
 		item->Destroy ();
 		return false;
@@ -791,6 +823,18 @@
 
 void AActor::ConversationAnimation (int animnum)
 {
+	switch (animnum)
+	{
+	case 0:
+		if (GreetingsState != NULL) SetState (GreetingsState);
+		break;
+	case 1:
+		if (YesState != NULL) SetState (YesState);
+		break;
+	case 2:
+		if (NoState != NULL) SetState (NoState);
+		break;
+	}
 }
 
 //============================================================================
@@ -821,6 +865,9 @@
 			return;
 		}
 	}
+
+	mo->momx = mo->momy = mo->momz = 0;
+	
 	mo->SetState (mo->DeathState);
 	if (mo->ObjectFlags & OF_MassDestruction)
 	{
@@ -878,7 +925,6 @@
 		}
 	}
 
-	mo->momx = mo->momy = mo->momz = 0;
 	mo->effects = 0;		// [RH]
 
 	if (mo->DeathState != NULL)
@@ -906,7 +952,7 @@
 			}
 		}
 
-		if (gameinfo.gametype == GAME_Doom)
+		if (mo->flags4 & MF4_RANDOMIZE)
 		{
 			mo->tics -= (pr_explodemissile() & 3) * TICRATE / 35;
 			if (mo->tics < 1)
@@ -1924,7 +1970,8 @@
 	// something is occupying its position?
 	if (!P_TestMobjLocation (mo))
 	{
-		level.total_monsters--;
+		//[GrafZahl] MF_COUNTKILL still needs to be checked here!
+		if (mo->flags & MF_COUNTKILL) level.total_monsters--;
 		mo->Destroy ();
 		return;		// no respawn
 	}
@@ -1945,6 +1992,9 @@
 	mo->LastLook = mobj->LastLook;
 	mo->flags3 |= mobj->flags3 & MF3_HUNTPLAYERS;
 	mo->flags4 |= mobj->flags4 & MF4_NOHATEPLAYERS;
+	// Friendliness information must also be transferred
+	mo->flags = (mo->flags&~MF_FRIENDLY) | (mobj->flags&MF_FRIENDLY);
+	mo->FriendPlayer = mobj->FriendPlayer;
 
 	// spawn a teleport fog at old spot because of removal of the body?
 	mo = Spawn ("TeleportFog", mobj->x, mobj->y, mobj->z);
@@ -2107,10 +2157,37 @@
 
 bool AActor::AdjustReflectionAngle (AActor *thing, angle_t &angle)
 {
-	angle += ANGLE_1 * ((pr_reflect()%16)-8);
+	// Change angle for reflection
+	if (thing->flags4&MF4_SHIELDREFLECT)
+	{
+		// Shield reflection (from the Centaur
+		if (abs (angle - thing->angle)>>24 > 45)
+			return true;	// Let missile explode
+
+		if (thing->IsKindOf (RUNTIME_CLASS(AHolySpirit)))	// shouldn't this be handled by another flag???
+			return true;
+
+		if (pr_reflect () < 128)
+			angle += ANGLE_45;
+		else
+			angle -= ANGLE_45;
+
+	}
+	else if (thing->flags4&MF4_DEFLECT)
+	{
+		// deflect (like the Heresiarch)
+		if(pr_reflect() < 128) 
+			angle += ANG45;
+		else 
+			angle -= ANG45;
+	}
+	else 
+		angle += ANGLE_1 * ((pr_reflect()%16)-8);
+
 	return false;
 }
 
+
 void AActor::PlayActiveSound ()
 {
 	if (ActiveSound && !S_IsActorPlayingSomething (this, CHAN_VOICE))
@@ -2183,6 +2260,18 @@
 	// [RH] Data for Heretic/Hexen scrolling sectors
 	static const byte HexenScrollDirs[8] = { 64, 0, 192, 128, 96, 32, 224, 160 };
 	static const char HexenSpeedMuls[3] = { 5, 10, 25 };
+	static const char hexenScrollies[24][2] =
+	{
+		{  0,  1 }, {  0,  2 }, {  0,  4 },
+		{ -1,  0 }, { -2,  0 }, { -4,  0 },
+		{  0, -1 }, {  0, -2 }, {  0, -4 },
+		{  1,  0 }, {  2,  0 }, {  4,  0 },
+		{  1,  1 }, {  2,  2 }, {  4,  4 },
+		{ -1,  1 }, { -2,  2 }, { -4,  4 },
+		{ -1, -1 }, { -2, -2 }, { -4, -4 },
+		{  1, -1 }, {  2, -2 }, {  4, -4 }
+	};
+
 
 	static const byte HereticScrollDirs[4] = { 6, 9, 1, 4 };
 	static const char HereticSpeedMuls[5] = { 5, 10, 25, 30, 35 };
@@ -2208,6 +2297,25 @@
 
 	fixed_t oldz = z;
 
+	// [RH] Give the pain elemental vertical friction
+	// This used to be in APainElemental::Tick but in order to use
+	// A_PainAttack with other monsters it has to be here!
+	if (flags4 & MF4_VFRICTION)
+	{
+		if (health >0)
+		{
+			if (abs (momz) < FRACUNIT/4)
+			{
+				momz = 0;
+				flags4 &= ~MF4_VFRICTION;
+			}
+			else
+			{
+				momz = FixedMul (momz, 0xe800);
+			}
+		}
+	}
+
 	// [RH] Pulse in and out of visibility
 	if (effects & FX_VISIBILITYPULSE)
 	{
@@ -2340,10 +2448,19 @@
 					scrolltype <= Scroll_SouthWest_Fast)
 				{ // Hexen scroll special
 					scrolltype -= Scroll_North_Slow;
-					angle_t fineangle = HexenScrollDirs[scrolltype / 3] * 32;
-					fixed_t carryspeed = HexenSpeedMuls[scrolltype % 3] * 2048;
-					scrollx += FixedMul (carryspeed, finecosine[fineangle]);
-					scrolly += FixedMul (carryspeed, finesine[fineangle]);
+					if (compatflags&COMPATF_RAVENSCROLL)
+					{
+						angle_t fineangle = HexenScrollDirs[scrolltype / 3] * 32;
+						fixed_t carryspeed = HexenSpeedMuls[scrolltype % 3] * 2048;
+						scrollx += FixedMul (carryspeed, finecosine[fineangle]);
+						scrolly += FixedMul (carryspeed, finesine[fineangle]);
+					}
+					else
+					{
+						// Use speeds that actually match the scrolling textures!
+						scrollx =-3*1024*hexenScrollies[scrolltype][0];
+						scrolly = 3*1024*hexenScrollies[scrolltype][1];
+					}
 				}
 				else if (scrolltype >= Carry_East5 &&
 						 scrolltype <= Carry_West35)
@@ -2351,12 +2468,25 @@
 					scrolltype -= Carry_East5;
 					byte dir = HereticScrollDirs[scrolltype / 5];
 					fixed_t carryspeed = HereticSpeedMuls[scrolltype % 5] * 2048;
+					if (scrolltype<=Carry_East35 && !(compatflags&COMPATF_RAVENSCROLL)) 
+					{
+						// Use speeds that actually match the scrolling textures!
+						carryspeed = 3072 * (1<<(scrolltype%5));
+					}
 					scrollx += carryspeed * ((dir & 3) - 1);
 					scrolly += carryspeed * (((dir & 12) >> 2) - 1);
 				}
 				else if (scrolltype == dScroll_EastLavaDamage)
 				{ // Special Heretic scroll special
-					scrollx += 2048*28;
+					if (compatflags&COMPATF_RAVENSCROLL)
+					{
+						scrollx += 2048*28;
+					}
+					else
+					{
+						// Use a speed that actually matches the scrolling texture!
+						scrollx += 2048*12;
+					}
 				}
 				else if (scrolltype == Scroll_StrifeCurrent)
 				{ // Strife scroll special
@@ -2399,13 +2529,18 @@
 			if (scrolly) county++;
 		}
 
-		if (countx > 1)
-		{
-			cummx /= countx;
-		}
-		if (county > 1)
+		// Some Boom compatible levels don't like this
+		// But it is only important for non-player objects.
+		if (!(level.flags&LEVEL_ADDITIVE_SCROLLERS) || player)
 		{
-			cummy /= county;
+			if (countx > 1)
+			{
+				cummx /= countx;
+			}
+			if (county > 1)
+			{
+				cummy /= county;
+			}
 		}
 		momx += cummx;
 		momy += cummy;
@@ -2701,6 +2836,15 @@
 	PROP_RenderStyle (STYLE_Normal)
 	PROP_Alpha (FRACUNIT)
 	PROP_MinMissileChance (200)
+	PROP_MeleeRange(44)	// MELEERANGE(64) - 20
+	PROP_User1 (0)
+	PROP_User2 (0)
+	PROP_User3 (0)
+	PROP_User4 (0)
+	PROP_User5 (0)
+	PROP_User6 (0)
+	PROP_User7 (0)
+	PROP_User8 (0)
 END_DEFAULTS
 
 //==========================================================================
@@ -2863,6 +3007,12 @@
 	if (SpawnFlags & MTF_FRIENDLY)
 	{
 		flags |= MF_FRIENDLY;
+		// Friendlies don't count as kills!
+		if (flags&MF_COUNTKILL)
+		{
+			flags &= MF_COUNTKILL;
+			level.total_monsters--;
+		}
 	}
 	if (SpawnFlags & MTF_SHADOW)
 	{
@@ -3005,7 +3155,7 @@
 	// [RH] Things 4001-? are also multiplayer starts. Just like 1-4.
 	//		To make things simpler, figure out which player is being
 	//		spawned here.
-	if (mthing->type <= 4)
+	if (mthing->type <= 4 || gameinfo.gametype == GAME_Strife)	// don't forget Strife's starts 5-8 here!
 	{
 		playernum = mthing->type - 1;
 	}
@@ -3125,7 +3275,7 @@
 	p->multicount = 0;
 	p->lastkilltime = 0;
 	p->BlendR = p->BlendG = p->BlendB = p->BlendA = 0.f;
-	p->air_finished = level.time + 10*TICRATE;
+	p->air_finished = level.time + level.airsupply;
 
 	p->momx = p->momy = 0;		// killough 10/98: initialize bobbing to 0.
 
@@ -3156,7 +3306,7 @@
 				if (key->KeyNumber != 0)
 				{
 					key = static_cast<AKey *>(Spawn (TypeInfo::m_Types[i], 0,0,0));
-					if (!key->TryPickup (p->mo))
+					if (!key->DoTryPickup (p->mo))
 					{
 						key->Destroy ();
 					}
@@ -3451,7 +3601,7 @@
 
 	// don't spawn any monsters if -nomonsters
 	if (dmflags & DF_NO_MONSTERS
-		&& (i->IsDescendantOf (RUNTIME_CLASS(ALostSoul)) || (info->flags3 & MF3_ISMONSTER)) )
+		&& ((info->flags3 & MF3_ISMONSTER)) )
 	{
 		return;
 	}
@@ -3553,7 +3703,22 @@
 	{
 		puff->SetState (puff->CrashState);
 	}
+	else if (attackrange == MELEERANGE && puff->MeleeState != NULL)
+	{
+		// handle the hard coded state jump of Doom's bullet puff
+		// in a more flexible manner.
+		puff->SetState (puff->MeleeState);
+	}
+
+	if (cl_pufftype && updown != 3 && puff->flags4&MF4_ALLOWPARTICLES)
+	{
+		P_DrawSplash2 (32, x, y, z, dir, updown, 1);
+		puff->renderflags |= RF_INVISIBLE;
+	}
+
 
+	/* This code assumes that no object can be used outside its own game.
+	   Since that is no longer the case it doesn't work anymore.
 	if (gameinfo.gametype == GAME_Doom)
 	{
 		// don't make punches spark on the wall
@@ -3572,7 +3737,7 @@
 			P_DrawSplash2 (32, x, y, z, dir, updown, 1);
 			puff->renderflags |= RF_INVISIBLE;
 		}
-	}
+	}*/
 
 	if (hitthing && puff->SeeSound)
 	{ // Hit thing sound
@@ -3588,19 +3753,20 @@
 }
 
 
-
 //
 // P_SpawnBlood
 // 
-void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, angle_t dir, int damage)
+void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, angle_t dir, int damage, AActor * originator)
 {
 	ABlood *th;
+	PalEntry bloodcolor = (PalEntry)originator->GetClass()->Meta.GetMetaInt(AMETA_BloodColor);
 
 	if (cl_bloodtype <= 1)
 	{
 		z += pr_spawnblood.Random2 () << 10;
 		th = Spawn<ABlood> (x, y, z);
 		th->momz = FRACUNIT*2;
+		th->angle = dir;
 		if (gameinfo.gametype == GAME_Doom)
 		{
 			th->tics -= pr_spawnblood() & 3;
@@ -3609,10 +3775,13 @@
 				th->tics = 1;
 		}
 		th->SetDamage (damage);
+
+		// colorize the blood!
+		if (bloodcolor!=0) th->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
 	}
 
 	if (cl_bloodtype >= 1)
-		P_DrawSplash2 (40, x, y, z, dir, 2, 0);
+		P_DrawSplash2 (40, x, y, z, dir, 2, bloodcolor);
 }
 
 // Blood splatter -----------------------------------------------------------
@@ -3651,6 +3820,8 @@
 
 void P_BloodSplatter (fixed_t x, fixed_t y, fixed_t z, AActor *originator)
 {
+	PalEntry bloodcolor = (PalEntry)originator->GetClass()->Meta.GetMetaInt(AMETA_BloodColor);
+
 	if (cl_bloodtype <= 1)
 	{
 		AActor *mo;
@@ -3667,10 +3838,13 @@
 		mo->momx = pr_splatter.Random2 () << 10;
 		mo->momy = pr_splatter.Random2 () << 10;
 		mo->momz = 3*FRACUNIT;
+
+		// colorize the blood!
+		if (bloodcolor!=0) mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
 	}
 	if (cl_bloodtype >= 1)
 	{
-		P_DrawSplash2 (40, x, y, z, R_PointToAngle (originator->x - x, originator->y - y), 2, 0);
+		P_DrawSplash2 (40, x, y, z, R_PointToAngle2 (originator->x , x, originator->y , y), 2, bloodcolor);
 	}
 }
 
@@ -3683,6 +3857,7 @@
 void P_RipperBlood (AActor *mo)
 {
 	fixed_t x, y, z;
+	PalEntry bloodcolor = (PalEntry)mo->GetClass()->Meta.GetMetaInt(AMETA_BloodColor);
 
 	x = mo->x + (pr_ripperblood.Random2 () << 12);
 	y = mo->y + (pr_ripperblood.Random2 () << 12);
@@ -3696,10 +3871,13 @@
 		th->momx = mo->momx >> 1;
 		th->momy = mo->momy >> 1;
 		th->tics += pr_ripperblood () & 3;
+
+		// colorize the blood!
+		if (bloodcolor!=0) th->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
 	}
 	if (cl_bloodtype >= 1)
 	{
-		P_DrawSplash2 (28, x, y, z, 0, 0, 0);
+		P_DrawSplash2 (28, x, y, z, 0, 0, bloodcolor);
 	}
 }
 
diff -durN 96/src/p_plats.cpp 96x/src/p_plats.cpp
--- 96/src/p_plats.cpp	2004-06-10 23:17:38.000000000 +0200
+++ 96x/src/p_plats.cpp	2005-06-22 15:18:33.000000000 +0200
@@ -26,6 +26,7 @@
 #include "m_random.h"
 #include "doomdef.h"
 #include "p_local.h"
+#include "p_lnspec.h"
 #include "s_sndseq.h"
 #include "doomstat.h"
 #include "r_state.h"
@@ -256,7 +257,7 @@
 			if (line)
 				sec->floorpic = sides[line->sidenum[0]].sector->floorpic;
 			if (change == 1)
-				sec->special = 0;	// Stop damage and other stuff, if any
+				sec->special &= SECRET_MASK;	// Stop damage and other stuff, if any
 		}
 
 		switch (type)
@@ -267,7 +268,7 @@
 			plat->m_Low = sec->floorplane.d;
 			plat->m_Status = DPlat::up;
 			plat->PlayPlatSound ("Floor");
-			sec->special = 0;		// NO MORE DAMAGE, IF APPLICABLE
+			sec->special &= SECRET_MASK;		// NO MORE DAMAGE, IF APPLICABLE
 			break;
 
 		case DPlat::platUpByValue:
diff -durN 96/src/p_pspr.cpp 96x/src/p_pspr.cpp
--- 96/src/p_pspr.cpp	2004-11-02 12:59:48.000000000 +0100
+++ 96x/src/p_pspr.cpp	2005-06-22 15:18:33.000000000 +0200
@@ -90,7 +90,7 @@
 		else
 			psp->tics = state->GetTics(); // could be 0
 
-		if (!(state->Frame&SF_WEAPONPARAM))
+		if (!(state->Frame&SF_STATEPARAM))
 		{
 			if (state->GetMisc1())
 			{ // Set coordinates.
@@ -103,20 +103,29 @@
 		}
 		else	// for parameterized action functions this must be done differently!
 		{
-			int index=state->GetMisc1_2();
-			FWeaponParam *wp=WeaponParams[index];
-
-			if (wp->wp_xoffset)
-			{ // Set coordinates.
-				psp->sx = wp->wp_xoffset<<FRACBITS;
-			}
-			if (wp->wp_yoffset)
+			size_t index=state->GetMisc1_2();
+			if (index>0 && index<StateParameters.Size()-2)
 			{
-				psp->sy = wp->wp_yoffset<<FRACBITS;
+				if (StateParameters[index])
+				{ // Set coordinates.
+					psp->sx = StateParameters[index]<<FRACBITS;
+				}
+				if (StateParameters[index+1])
+				{
+					psp->sy = StateParameters[index+1]<<FRACBITS;
+				}
 			}
 		}
 		if (state->GetAction())
-		{ // Call action routine.
+		{ 
+			// The parameterized action functions need access to the current state and
+			// if a function is supposed to work with both actors and weapons
+			// there is no real means to get to it reliably so I store it in a global variable here.
+			// Yes, I know this is truly awful but it is the only method I can think of 
+			// that does not involve changing stuff throughout the code. 
+			// Of course this should be rewritten ASAP.
+			CallingState=state;
+			// Call action routine.
 			state->GetAction() (player->mo);
 			if (!psp->state)
 			{
@@ -148,13 +157,32 @@
 		}
 		psp->state = state;
 		psp->tics = state->GetTics(); // could be 0
-		if (state->GetMisc1())
-		{ // Set coordinates.
-			psp->sx = state->GetMisc1()<<FRACBITS;
+
+		if (!(state->Frame&SF_STATEPARAM))
+		{
+			if (state->GetMisc1())
+			{ // Set coordinates.
+				psp->sx = state->GetMisc1()<<FRACBITS;
+			}
+			if (state->GetMisc2())
+			{
+				psp->sy = state->GetMisc2()<<FRACBITS;
+			}
 		}
-		if (state->GetMisc2())
+		else	// for parameterized action functions this must be done differently!
 		{
-			psp->sy = state->GetMisc2()<<FRACBITS;
+			size_t index=state->GetMisc1_2();
+			if (index>0 && index<StateParameters.Size()-2)
+			{
+				if (StateParameters[index])
+				{ // Set coordinates.
+					psp->sx = StateParameters[index]<<FRACBITS;
+				}
+				if (StateParameters[index+1])
+				{
+					psp->sy = StateParameters[index+1]<<FRACBITS;
+				}
+			}
 		}
 		state = psp->state->GetNextState();
 	} while (!psp->tics); // An initial state of 0 could cycle through.
@@ -244,6 +272,7 @@
 	{
 		player->mo->PlayAttacking ();
 	}
+	weapon->bAltFire = false;
 	P_SetPsprite (player, ps_weapon,
 		player->refire ? weapon->GetHoldAtkState() : weapon->GetAtkState());
 	if (!(weapon->WeaponFlags & WIF_NOALERT))
@@ -254,6 +283,46 @@
 
 //---------------------------------------------------------------------------
 //
+// PROC P_FireWeapon
+//
+//---------------------------------------------------------------------------
+
+void P_FireWeaponAlt (player_t *player)
+{
+	AWeapon *weapon;
+
+	// [SO] 9/2/02: People were able to do an awful lot of damage
+	// when they were observers...
+	if (!player->isbot && bot_observer)
+	{
+		return;
+	}
+
+	weapon = player->ReadyWeapon;
+	if (weapon == NULL || weapon->AltAtkState == NULL || !weapon->CheckAmmo (AWeapon::AltFire, true))
+	{
+		return;
+	}
+
+	if (gameinfo.gametype == GAME_Heretic)
+	{
+		player->mo->PlayAttacking2 ();
+	}
+	else
+	{
+		player->mo->PlayAttacking ();
+	}
+	weapon->bAltFire = true;
+	P_SetPsprite (player, ps_weapon,
+		player->refire ? weapon->AltHoldAtkState : weapon->AltAtkState);
+	if (!(weapon->WeaponFlags & WIF_NOALERT))
+	{
+		P_NoiseAlert (player->mo, player->mo, false);
+	}
+}
+
+//---------------------------------------------------------------------------
+//
 // PROC P_DropWeapon
 //
 // The player died, so put the weapon away.
@@ -287,7 +356,7 @@
 
 	weapon = player->ReadyWeapon;
 
-	if (weapon == NULL || weapon->flags & WIF_DONTBOB)
+	if (weapon == NULL || weapon->WeaponFlags & WIF_DONTBOB)
 	{
 		*x = *y = 0;
 		return;
@@ -407,13 +476,23 @@
 	// Check for fire. Some weapons do not auto fire.
 	if (player->cmd.ucmd.buttons & BT_ATTACK)
 	{
-		if (!(player->oldbuttons & BT_ATTACK) || !(weapon->flags & WIF_NOAUTOFIRE))
+		if (!(player->oldbuttons & BT_ATTACK) || !(weapon->WeaponFlags & WIF_NOAUTOFIRE))
 		{
 			player->oldbuttons |= BT_ATTACK;
 			P_FireWeapon (player);
 			return;
 		}
 	}
+
+	if (player->cmd.ucmd.buttons & BT_ALTATTACK)
+	{
+		if (!(player->oldbuttons & BT_ALTATTACK) || !(weapon->WeaponFlags & WIF_NOAUTOFIRE))
+		{
+			player->oldbuttons |= BT_ALTATTACK;
+			P_FireWeaponAlt (player);
+			return;
+		}
+	}
 }
 
 //---------------------------------------------------------------------------
@@ -432,12 +511,18 @@
 	{
 		return;
 	}
-	if ((player->cmd.ucmd.buttons&BT_ATTACK)
+	if ((player->cmd.ucmd.buttons&BT_ATTACK) && !player->ReadyWeapon->bAltFire 
 		&& player->PendingWeapon == WP_NOCHANGE && player->health)
 	{
 		player->refire++;
 		P_FireWeapon (player);
 	}
+	else if ((player->cmd.ucmd.buttons&BT_ALTATTACK) && player->ReadyWeapon->bAltFire
+		&& player->PendingWeapon == WP_NOCHANGE && player->health)
+	{
+		player->refire++;
+		P_FireWeaponAlt (player);
+	}
 	else
 	{
 		player->refire = 0;
diff -durN 96/src/p_setup.cpp 96x/src/p_setup.cpp
--- 96/src/p_setup.cpp	2004-12-13 22:00:33.000000000 +0100
+++ 96x/src/p_setup.cpp	2005-06-22 15:18:33.000000000 +0200
@@ -54,6 +54,7 @@
 #include "doomerrors.h"
 #include "gi.h"
 #include "p_conversation.h"
+#include "a_keys.h"
 
 extern void P_SpawnMapThing (mapthing2_t *mthing, int position);
 extern bool P_LoadBuildMap (BYTE *mapdata, size_t len, mapthing2_t **things, int *numthings);
@@ -1276,20 +1277,25 @@
 	}
 
 	// [RH] Set line id (as appropriate) here
-	if (ld->special == Line_SetIdentification)
+	// for Doom format maps this must be done in P_TranslateLinedef because
+	// the tag doesn't always go into the first arg!
+	if (HasBehavior)	
 	{
-		ld->id = ld->args[0];
-		if (ld->args[1] == 1)
+		if (ld->special == Line_SetIdentification)
 		{
-			ld->flags |= ML_ZONEBOUNDARY;
+			ld->id = ld->args[0];
+			if (ld->args[1] == 1)
+			{
+				ld->flags |= ML_ZONEBOUNDARY;
+			}
+		}
+		else if (!HasBehavior ||
+			ld->special == Teleport_Line ||
+			ld->special == TranslucentLine ||
+			ld->special == Scroll_Texture_Model)
+		{
+			ld->id = ld->args[0];
 		}
-	}
-	else if (!HasBehavior ||
-		ld->special == Teleport_Line ||
-		ld->special == TranslucentLine ||
-		ld->special == Scroll_Texture_Model)
-	{
-		ld->id = ld->args[0];
 	}
 }
 
@@ -1450,7 +1456,7 @@
 
 		ld->v1 = &vertexes[SHORT(mld->v1)];
 		ld->v2 = &vertexes[SHORT(mld->v2)];
-		ld->id = -1;
+		//ld->id = -1; ID has been assigned in P_TranslateLinedef.
 
 		P_SetSideNum (&ld->sidenum[0], SHORT(mld->sidenum[0]));
 		P_SetSideNum (&ld->sidenum[1], SHORT(mld->sidenum[1]));
@@ -1668,7 +1674,7 @@
 						rightline = &lines[sides[right].linenum];
 						if (rightline->frontsector != rightline->backsector)
 						{
-							ang2 = R_PointToAngle (rightline->dx, rightline->dy);
+							ang2 = R_PointToAngle2 (0, 0, rightline->dx, rightline->dy);
 							if (sidetemp[right].b.lineside)
 							{
 								ang2 += ANGLE_180;
@@ -3274,6 +3280,7 @@
 	P_InitSwitchList ();
 	P_InitTerrainTypes ();
 	R_InitSprites ();
+	P_InitKeyMessages();	// Must be done here so the cheating code knows about all valid keys!
 }
 
 #if 0
diff -durN 96/src/p_spec.cpp 96x/src/p_spec.cpp
--- 96/src/p_spec.cpp	2004-12-24 15:34:47.000000000 +0100
+++ 96x/src/p_spec.cpp	2005-07-24 20:12:09.000000000 +0200
@@ -70,6 +70,9 @@
 static FRandom pr_playerinspecialsector ("PlayerInSpecialSector");
 static FRandom pr_animatepictures ("AnimatePics");
 
+// [GrafZahl] Make this message changable by the user! ;)
+CVAR(String, secretmessage, "A Secret is revealed!", CVAR_ARCHIVE)
+
 IMPLEMENT_CLASS (DScroller)
 
 IMPLEMENT_POINTY_CLASS (DPusher)
@@ -184,9 +187,11 @@
 			{
 				P_ProcessSwitchDef ();
 			}
-			else if (SC_Compare ("warp"))
+			// [GRB] Added warping type 2
+			else if (SC_Compare ("warp") || SC_Compare ("warp2"))
 			{
 				bool isflat = false;
+				BOOL type2 = SC_Compare ("warp2");	// [GRB]
 				SC_MustGetString ();
 				if (SC_Compare ("flat"))
 				{
@@ -205,7 +210,11 @@
 				int picnum = TexMan.CheckForTexture (sc_String, isflat ? FTexture::TEX_Flat : FTexture::TEX_Wall, texflags);
 				if (picnum != -1)
 				{
-					FTexture *warper = new FWarpTexture (TexMan[picnum]);
+					FTexture *warper;
+					if (type2)	// [GRB]
+						warper = new FWarp2Texture (TexMan[picnum]);
+					else
+						warper = new FWarpTexture (TexMan[picnum]);
 					TexMan.ReplaceTexture (picnum, warper, false);
 
 					// No decals on warping textures, by default
@@ -236,7 +245,7 @@
 				SC_MustGetNumber ();
 				height = sc_Number;
 				int picnum = TexMan.CheckForTexture (picname, FTexture::TEX_Flat, texflags);
-				FTexture *viewer = new FCanvasTexture (sc_String, width, height);
+				FTexture *viewer = new FCanvasTexture (picname, width, height);
 				if (picnum != -1)
 				{
 					FTexture *oldtex = TexMan[picnum];
@@ -249,6 +258,9 @@
 				{
 					fitwidth = width;
 					fitheight = height;
+					// [GRB] No need for oldtex
+					viewer->UseType = FTexture::TEX_Wall;
+					TexMan.AddTexture (viewer);
 				}
 				delete[] picname;
 				if (SC_GetString())
@@ -751,7 +763,7 @@
 		!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->args[0] &&										// only if there's a tag (all types that are used by Doom-format maps use args[0] as tag)
+		line->id &&												// only if there's a tag (which is stored in the id field )
 		P_FindSectorFromTag (line->args[0], -1) == -1)			// only if no sector is tagged to this linedef
 	{
 		P_ChangeSwitchTexture (&sides[line->sidenum[0]], repeat, special);
@@ -913,6 +925,11 @@
 	{
 		switch (special)
 		{
+		case Sector_Heal:
+			// CoD's healing sector
+			if (!(level.time&0x1f)) P_GiveBody(player,1);
+			break;
+
 		case Damage_InstantDeath:
 			// Strife's instant death sector
 			P_DamageMobj (player->mo, NULL, NULL, 999, MOD_UNKNOWN);
@@ -1034,6 +1051,9 @@
 	}
 
 	// [RH] Apply terrain-based damage
+	// [GrafZahl] This code is not only redundant (see P_PlayerOnSpecialFlat),
+	// it also contains a bug and inflicts damage far too often!
+	/*
 	int terrainnum = TerrainTypes[sector->floorpic];
 
 	if (Terrains[terrainnum].DamageAmount &&
@@ -1042,6 +1062,7 @@
 		P_DamageMobj (player->mo, NULL, NULL, Terrains[terrainnum].DamageAmount,
 			Terrains[terrainnum].DamageMOD);
 	}
+	*/
 
 	if (sector->special & SECRET_MASK)
 	{
@@ -1050,7 +1071,7 @@
 		sector->special &= ~SECRET_MASK;
 		if (player->mo->CheckLocalView (consoleplayer))
 		{
-			C_MidPrint ("A secret is revealed!");
+			C_MidPrint (secretmessage);
 			S_Sound (CHAN_AUTO, "misc/secret", 1, ATTN_NORM);
 		}
 	}
@@ -1256,6 +1277,7 @@
 		for (secnum = -1; (secnum = P_FindSectorFromTag (target, secnum)) >= 0; )
 			sectors[secnum].CeilingFlags |= SECF_ABSLIGHTING;
 	}
+	ChangeStatNum(STAT_LIGHTTRANSFER);
 }
 
 void DLightTransfer::Tick ()
diff -durN 96/src/p_spec.h 96x/src/p_spec.h
--- 96/src/p_spec.h	2004-12-13 21:15:37.000000000 +0100
+++ 96x/src/p_spec.h	2005-06-22 15:18:33.000000000 +0200
@@ -423,6 +423,8 @@
 	void Serialize (FArchive &arc);
 	void Tick ();
 
+	bool IsLift() const { return m_Type == platDownWaitUpStay || m_Type == platDownWaitUpStayStone; }
+
 protected:
 	DPlat (sector_t *sector);
 
diff -durN 96/src/p_user.cpp 96x/src/p_user.cpp
--- 96/src/p_user.cpp	2004-12-24 16:57:21.000000000 +0100
+++ 96x/src/p_user.cpp	2005-06-22 16:34:32.000000000 +0200
@@ -45,6 +45,7 @@
 #include "cmdlib.h"
 #include "sbar.h"
 #include "f_finale.h"
+#include "c_console.h"
 
 static FRandom pr_healradius ("HealRadius");
 
@@ -109,6 +110,12 @@
 	{
 		FMemLump data = Wads.ReadLump (lumpnum);
 		SetLogText ((char *)data.GetMem());
+
+		// Print log text to console
+		
+		AddToConsole(-1, TEXTCOLOR_GOLD);
+		AddToConsole(-1, LogText);
+		AddToConsole(-1, "\n");
 	}
 }
 
@@ -120,6 +127,16 @@
 IMPLEMENT_ABSTRACT_ACTOR (APlayerPawn)
 IMPLEMENT_ABSTRACT_ACTOR (APlayerChunk)
 
+void APlayerPawn::Serialize (FArchive &arc)
+{
+	Super::Serialize (arc);
+
+	if (SaveVersion >= 228)
+	{
+		arc << JumpZ;
+	}
+}
+
 void APlayerPawn::BeginPlay ()
 {
 	Super::BeginPlay ();
@@ -1083,7 +1100,8 @@
 			}
 			else if (!(dmflags & DF_NO_JUMP) && onground && !player->jumpTics)
 			{
-				player->mo->momz += player->mo->GetJumpZ ()*35/TICRATE;
+				fixed_t JumpZ = (player->mo->JumpZ > 0 ? player->mo->JumpZ : player->mo->GetJumpZ ());	// [GRB]
+				player->mo->momz += JumpZ*35/TICRATE;
 				S_Sound (player->mo, CHAN_BODY, "*jump", 1, ATTN_NORM);
 				player->mo->flags2 &= ~MF2_ONMOBJ;
 				player->jumpTics = 18*TICRATE/35;
@@ -1187,15 +1205,18 @@
 		}
 
 		// Handle air supply
-		if (player->mo->waterlevel < 3 ||
-			(player->mo->flags2 & MF2_INVULNERABLE) ||
-			(player->cheats & CF_GODMODE))
-		{
-			player->air_finished = level.time + 10*TICRATE;
-		}
-		else if (player->air_finished <= level.time && !(level.time & 31))
+		if (level.airsupply>0)
 		{
-			P_DamageMobj (player->mo, NULL, NULL, 2 + 2*((level.time-player->air_finished)/TICRATE), MOD_WATER);
+			if (player->mo->waterlevel < 3 ||
+				(player->mo->flags2 & MF2_INVULNERABLE) ||
+				(player->cheats & CF_GODMODE))
+			{
+				player->air_finished = level.time + level.airsupply;
+			}
+			else if (player->air_finished <= level.time && !(level.time & 31))
+			{
+				P_DamageMobj (player->mo, NULL, NULL, 2 + 2*((level.time-player->air_finished)/TICRATE), MOD_WATER);
+			}
 		}
 	}
 
@@ -1386,6 +1407,7 @@
 		<< BlendA
 		<< accuracy << stamina
 		<< LogText;
+
 	for (i = 0; i < MAXPLAYERS; i++)
 		arc << frags[i];
 	for (i = 0; i < NUMPSPRITES; i++)
diff -durN 96/src/p_xlat.cpp 96x/src/p_xlat.cpp
--- 96/src/p_xlat.cpp	2004-11-04 09:23:29.000000000 +0100
+++ 96x/src/p_xlat.cpp	2005-06-22 15:18:33.000000000 +0200
@@ -71,6 +71,9 @@
 	BOOL passthrough;
 	int i;
 
+	// In Doom format map the tag is always the same as the line's id.
+	ld->id = tag;
+	
 	if (flags & ML_TRANSLUCENT_STRIFE)
 	{
 		ld->alpha = 255*3/4;
@@ -408,6 +411,13 @@
 {
 	int high;
 
+	// Allow any supported sector special by or-ing 0x8000 to it in Doom format maps
+	// That's for those who like to mess around with existing maps. ;)
+	if (special & 0x8000)
+	{
+		return special & 0x7fff;
+	}
+	
 	if (special == 9)
 	{
 		return SECRET_MASK;
@@ -427,6 +437,12 @@
 					return high | (special + 100);
 				}
 			}
+			else if (level.flags&LEVEL_CAVERNS_OF_DARKNESS)
+			{
+				// CoD uses 18 as an instant death sector type and 19 for healing the player
+				if (special == 18) return high | Damage_InstantDeath;
+				if (special == 19) return high | Sector_Heal;
+			}
 			return high | (special + 64);
 		}
 		else if (special < 40)
diff -durN 96/src/r_data.cpp 96x/src/r_data.cpp
--- 96/src/r_data.cpp	2004-12-18 16:45:56.000000000 +0100
+++ 96x/src/r_data.cpp	2005-06-22 15:18:33.000000000 +0200
@@ -294,6 +294,11 @@
 			out = new FPNGTexture (lumpnum, BELONG((int)width), BELONG((int)height),
 				bitdepth, colortype, interlace);
 		}
+		else if (usetype==FTexture::TEX_Flat)
+		{
+			// allow PNGs as flats but not Doom patches.
+			return -1;
+		}
 		else if ((gameinfo.flags & GI_PAGESARERAW) && data.GetLength() == 64000)
 		{
 			// This is probably a raw page graphic, but do some checking to be sure
@@ -432,7 +437,11 @@
 
 	for (i = firstflat; i <= lastflat; ++i)
 	{
-		AddTexture (new FFlatTexture (i));
+		// Support PNGs as flats!
+		if (CreateTexture (i, FTexture::TEX_Flat) == -1)
+		{
+			AddTexture (new FFlatTexture (i));
+		}
 	}
 }
 
@@ -698,7 +707,7 @@
 		{
 			// The very first texture is just a dummy. Copy its dimensions to texture 0.
 			// It still needs to be created in case someone uses it by name.
-			offset = LONG(directory[0]);
+			offset = LONG(directory[1]);
 			const maptexture_t *tex = (const maptexture_t *)((const BYTE *)maptex + offset);
 			FDummyTexture *tex0 = static_cast<FDummyTexture *>(Textures[0].Texture);
 			tex0->SetSize (SAFESHORT(tex->width), SAFESHORT(tex->height));
@@ -2442,6 +2451,137 @@
 }
 
 
+// [GRB] Eternity-like warping
+FWarp2Texture::FWarp2Texture (FTexture *source)
+: SourcePic (source), Pixels (0), Spans (0), GenTime (0)
+{
+	Width = source->GetWidth ();
+	Height = source->GetHeight ();
+	LeftOffset = source->LeftOffset;
+	TopOffset = source->TopOffset;
+	WidthBits = source->WidthBits;
+	HeightBits = source->HeightBits;
+	WidthMask = (1 << WidthBits) - 1;
+	ScaleX = source->ScaleX;
+	ScaleY = source->ScaleY;
+	bNoDecals = source->bNoDecals;
+	Rotations = source->Rotations;
+}
+
+FWarp2Texture::~FWarp2Texture ()
+{
+	Unload ();
+	delete SourcePic;
+}
+
+void FWarp2Texture::Unload ()
+{
+	if (Pixels != NULL)
+	{
+		delete[] Pixels;
+		Pixels = NULL;
+	}
+	if (Spans != NULL)
+	{
+		FreeSpans (Spans);
+		Spans = NULL;
+	}
+	SourcePic->Unload ();
+}
+
+bool FWarp2Texture::CheckModified ()
+{
+	return r_FrameTime != GenTime;
+}
+
+const BYTE *FWarp2Texture::GetPixels ()
+{
+	DWORD time = r_FrameTime;
+
+	if (Pixels == NULL || time != GenTime)
+	{
+		MakeTexture (time);
+	}
+	return Pixels;
+}
+
+const BYTE *FWarp2Texture::GetColumn (unsigned int column, const Span **spans_out)
+{
+	DWORD time = r_FrameTime;
+
+	if (Pixels == NULL || time != GenTime)
+	{
+		MakeTexture (time);
+	}
+	if ((unsigned)column >= (unsigned)Width)
+	{
+		if (WidthMask + 1 == Width)
+		{
+			column &= WidthMask;
+		}
+		else
+		{
+			column %= Width;
+		}
+	}
+	if (spans_out != NULL)
+	{
+		if (Spans == NULL)
+		{
+			Spans = CreateSpans (Pixels);
+		}
+		*spans_out = Spans[column];
+	}
+	return Pixels + column*Height;
+}
+
+void FWarp2Texture::MakeTexture (DWORD time)
+{
+	const BYTE *otherpix = SourcePic->GetPixels ();
+
+	if (Pixels == NULL)
+	{
+		Pixels = new BYTE[Width * Height];
+	}
+	if (Spans != NULL)
+	{
+		FreeSpans (Spans);
+		Spans = NULL;
+	}
+
+	GenTime = time;
+
+	int xsize = Width;
+	int ysize = Height;
+	int xmask = WidthMask;
+	int ymask = Height - 1;
+	int ybits = HeightBits;
+	int x, y;
+
+	if ((1 << ybits) > Height)
+	{
+		ybits--;
+	}
+
+	DWORD timebase = time * 40 / 28;
+	for (x = xsize-1; x >= 0; x--)
+	{
+		for (y = ysize-1; y >= 0; y--)
+		{
+			int xt = (x + 128
+				+ ((finesine[(y*128 + timebase*5 + 900) & 8191]*2)>>FRACBITS)
+				+ ((finesine[(x*256 + timebase*4 + 300) & 8191]*2)>>FRACBITS)) & xmask;
+			int yt = (y + 128
+				+ ((finesine[(y*128 + timebase*3 + 700) & 8191]*2)>>FRACBITS)
+				+ ((finesine[(x*256 + timebase*4 + 1200) & 8191]*2)>>FRACBITS)) & ymask;
+			const BYTE *source = otherpix + (xt << ybits) + yt;
+			BYTE *dest = Pixels + (x << ybits) + y;
+			*dest = *source;
+		}
+	}
+}
+
+
 FCanvasTexture::FCanvasTexture (const char *name, int width, int height)
 {
 	strncpy (Name, name, 8);
diff -durN 96/src/r_data.h 96x/src/r_data.h
--- 96/src/r_data.h	2004-11-25 16:26:38.000000000 +0100
+++ 96x/src/r_data.h	2005-06-22 15:18:33.000000000 +0200
@@ -233,6 +233,28 @@
 	void MakeTexture (DWORD time);
 };
 
+
+// [GRB] Eternity-like warping
+class FWarp2Texture : public FTexture
+{
+public:
+	FWarp2Texture (FTexture *source);
+	~FWarp2Texture ();
+
+	const BYTE *GetColumn (unsigned int column, const Span **spans_out);
+	const BYTE *GetPixels ();
+	void Unload ();
+	bool CheckModified ();
+
+protected:
+	FTexture *SourcePic;
+	BYTE *Pixels;
+	Span **Spans;
+	DWORD GenTime;
+
+	void MakeTexture (DWORD time);
+};
+
 // A texture that can be drawn to.
 class DSimpleCanvas;
 class FCanvasTexture : public FTexture
diff -durN 96/src/r_draw.cpp 96x/src/r_draw.cpp
--- 96/src/r_draw.cpp	2004-12-13 17:49:37.000000000 +0100
+++ 96x/src/r_draw.cpp	2005-06-22 15:18:33.000000000 +0200
@@ -1243,6 +1243,10 @@
 	translationtables[TRANSLATION_PlayerCorpses] =
 		translationtables[TRANSLATION_LevelScripted] + MAX_ACS_TRANSLATIONS*256;
 
+	extern byte decorate_translations[];
+	translationtables[TRANSLATION_Decorate] = decorate_translations;
+	translationtables[TRANSLATION_Blood] = decorate_translations + MAX_DECORATE_TRANSLATIONS*256;
+
 	// [RH] Each player now gets their own translation table. These are set
 	//		up during netgame arbitration and as-needed rather than in here.
 
diff -durN 96/src/r_draw.h 96x/src/r_draw.h
--- 96/src/r_draw.h	2004-12-03 21:36:41.000000000 +0100
+++ 96x/src/r_draw.h	2005-06-22 15:18:33.000000000 +0200
@@ -204,6 +204,8 @@
 	TRANSLATION_LevelScripted,
 	TRANSLATION_Decals,
 	TRANSLATION_PlayerCorpses,
+	TRANSLATION_Decorate,
+	TRANSLATION_Blood,
 
 	NUM_TRANSLATION_TABLES
 };
@@ -217,6 +219,7 @@
 }
 
 const int MAX_ACS_TRANSLATIONS = 255;
+const int MAX_DECORATE_TRANSLATIONS = 255;
 
 inline void R_CopyTranslation (WORD to, WORD from)
 {
diff -durN 96/src/r_things.cpp 96x/src/r_things.cpp
--- 96/src/r_things.cpp	2004-12-23 11:47:38.000000000 +0100
+++ 96x/src/r_things.cpp	2005-06-22 15:18:33.000000000 +0200
@@ -138,7 +138,7 @@
 	}
 
 	if (frame >= MAX_SPRITE_FRAMES || rotation > 16)
-		I_FatalError ("R_InstallSpriteLump: Bad frame characters in lump %i", lump);
+		I_FatalError ("R_InstallSpriteLump: Bad frame characters in lump %s", TexMan[lump]->Name);
 
 	if ((int)frame > maxframe)
 		maxframe = frame;
diff -durN 96/src/s_advsound.cpp 96x/src/s_advsound.cpp
--- 96/src/s_advsound.cpp	2004-12-01 23:02:06.000000000 +0100
+++ 96x/src/s_advsound.cpp	2005-07-24 20:12:09.000000000 +0200
@@ -100,6 +100,7 @@
 	SI_Map,
 	SI_Registered,
 	SI_ArchivePath,
+	SI_MusicVolume,
 	SI_IfDoom,
 	SI_IfHeretic,
 	SI_IfHexen,
@@ -119,6 +120,16 @@
 	char	RawName[9];	// name of RAW resource
 };
 
+
+// music volume multipliers
+struct FMusicVolume
+{
+	char * musicname;
+	float volume;
+};
+
+static TArray<FMusicVolume> MusicVolumes;
+
 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
 
 void S_StartNamedSound (AActor *ent, fixed_t *pt, int channel, 
@@ -170,6 +181,7 @@
 	"$map",
 	"$registered",
 	"$archivepath",
+	"$musicvolume",
 	"$ifdoom",
 	"$ifheretic",
 	"$ifhexen",
@@ -197,6 +209,25 @@
 
 //==========================================================================
 //
+// S_GetMusicVolume
+//
+// Gets the relative volume for the given music track
+//==========================================================================
+
+float S_GetMusicVolume(const char * music)
+{
+	for(int i=0; i<MusicVolumes.Size(); i++)
+	{
+		if (!stricmp(music, MusicVolumes[i].musicname))
+		{
+			return MusicVolumes[i].volume;
+		}
+	}
+	return 1.0f;
+}
+
+//==========================================================================
+//
 // S_HashSounds
 //
 // Fills in the next and index fields of S_sfx to form a working hash table.
@@ -841,6 +872,16 @@
 				}
 				}
 				break;
+				
+			case SI_MusicVolume: {
+				FMusicVolume mv;
+				SC_MustGetString();
+				mv.musicname = copystring(sc_String);
+				SC_MustGetFloat();
+				mv.volume = sc_Float;
+				MusicVolumes.Push(mv);
+				}
+				break;
 
 			case SI_IfDoom:
 				if (gameinfo.gametype != GAME_Doom)
diff -durN 96/src/s_sndseq.cpp 96x/src/s_sndseq.cpp
--- 96/src/s_sndseq.cpp	2004-03-13 22:51:03.000000000 +0100
+++ 96x/src/s_sndseq.cpp	2005-07-11 18:20:06.000000000 +0200
@@ -601,7 +601,18 @@
 static bool TwiddleSeqNum (int &sequence, seqtype_t type)
 {
 	if (type < SEQ_NUMSEQTYPES)
-		sequence = SeqTrans[sequence + type * 64];
+	{
+		// [GrafZahl] Needs some range checking:
+		// Sector_ChangeSound doesn't do it so this makes invalid sequences play nothing.
+		if (sequence >= 0 && sequence < 64)
+		{
+			sequence = SeqTrans[sequence + type * 64];
+		}
+		else
+		{
+			return false;
+		}
+	}
 
 	if (sequence == -1 || Sequences[sequence] == NULL)
 		return false;
diff -durN 96/src/s_sound.cpp 96x/src/s_sound.cpp
--- 96/src/s_sound.cpp	2004-12-21 16:11:19.000000000 +0100
+++ 96x/src/s_sound.cpp	2005-07-24 20:12:09.000000000 +0200
@@ -123,6 +123,8 @@
 
 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
 
+float S_GetMusicVolume(const char * music);
+
 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
 
 static fixed_t P_AproxDistance2 (fixed_t *listener, fixed_t x, fixed_t y);
@@ -1435,7 +1437,7 @@
 	if (mus_playing.handle != 0)
 	{ // play it
 		mus_playing.name = copystring (musicname);
-		I_PlaySong (mus_playing.handle, looping);
+		I_PlaySong (mus_playing.handle, looping, S_GetMusicVolume(musicname));
 		mus_playing.baseorder =
 			(I_SetSongPosition (mus_playing.handle, order) ? order : 0);
 		return true;
diff -durN 96/src/sc_man.cpp 96x/src/sc_man.cpp
--- 96/src/sc_man.cpp	2004-10-06 10:44:53.000000000 +0200
+++ 96x/src/sc_man.cpp	2005-07-22 13:36:23.000000000 +0200
@@ -340,6 +340,18 @@
 		if (CMode)
 		{
 			stopchars = "`~!@#$%^&*(){}[]/=\?+|;:<>,";
+
+			// '-' can be its own token, or it can be part of a negative number
+			if (*ScriptPtr == '-')
+			{
+				*text++ = '-';
+				ScriptPtr++;
+				if (ScriptPtr < ScriptEndPtr && *ScriptPtr >= '0' && *ScriptPtr <= '9')
+				{
+					goto grabtoken;
+				}
+				goto gottoken;
+			}
 		}
 		else
 		{
@@ -348,10 +360,14 @@
 		if (strchr (stopchars, *ScriptPtr))
 		{
 			*text++ = *ScriptPtr++;
+			// [GRB] Allow 2-char operators
+			if (CMode && strchr ("&=|<>", *ScriptPtr))
+				*text++ = *ScriptPtr++;
 		}
 		else
 		{
-			while ((*ScriptPtr > 32) && (strchr (stopchars, *ScriptPtr) == NULL)
+grabtoken:
+			while ((*ScriptPtr > ' ') && (strchr (stopchars, *ScriptPtr) == NULL)
 				&& (CMode || *ScriptPtr != ASCII_COMMENT)
 				&& !(ScriptPtr[0] == CPP_COMMENT && (ScriptPtr < ScriptEndPtr - 1) &&
 					 (ScriptPtr[1] == CPP_COMMENT || ScriptPtr[1] == C_COMMENT)))
@@ -365,6 +381,7 @@
 			}
 		}
 	}
+gottoken:
 	*text = 0;
 	sc_StringLen = text - sc_String;
 	return true;
diff -durN 96/src/sound/fmodsound.cpp 96x/src/sound/fmodsound.cpp
--- 96/src/sound/fmodsound.cpp	2004-12-18 16:47:07.000000000 +0100
+++ 96x/src/sound/fmodsound.cpp	2005-07-24 20:12:09.000000000 +0200
@@ -43,6 +43,7 @@
 #define TRUE 1
 #endif
 
+#include "templates.h"
 #include "fmodsound.h"
 #include "c_cvars.h"
 #include "i_system.h"
@@ -52,6 +53,7 @@
 #include "w_wad.h"
 #include "sample_flac.h"
 #include "i_music.h"
+#include "i_musicinterns.h"
 
 // killough 2/21/98: optionally use varying pitched sounds
 #define PITCH(f,x) (snd_pitched ? ((f)*(x))/128 : (f))
@@ -139,7 +141,7 @@
 		int channel = FSOUND_Stream_PlayEx (FSOUND_FREE, Stream, NULL, true);
 		if (channel != -1)
 		{
-			FSOUND_SetVolumeAbsolute (channel, int(volume * 255.f));
+			FSOUND_SetVolumeAbsolute (channel, clamp<int>((int)(volume * relative_volume * 255), 0, 255));
 			FSOUND_SetPan (channel, FSOUND_STEREOPAN);
 			FSOUND_SetPaused (channel, false);
 			return true;
@@ -170,7 +172,7 @@
 	{
 		if (Channel != -1)
 		{
-			FSOUND_SetVolumeAbsolute (Channel, int(volume * 255));
+			FSOUND_SetVolumeAbsolute (Channel, clamp<int>((int)(volume * relative_volume * 255), 0, 255));
 		}
 	}
 
@@ -215,7 +217,7 @@
 
 	void SetVolume (float volume)
 	{
-		FMUSIC_SetMasterVolume (Module, int(volume * 256.f));
+		FMUSIC_SetMasterVolume (Module, clamp<int>((int)(volume * relative_volume * 256), 0, 256));
 	}
 
 	bool SetPaused (bool paused)
diff -durN 96/src/sound/i_music.cpp 96x/src/sound/i_music.cpp
--- 96/src/sound/i_music.cpp	2004-12-02 13:26:07.000000000 +0100
+++ 96x/src/sound/i_music.cpp	2005-07-24 20:12:09.000000000 +0200
@@ -76,6 +76,7 @@
 
 #include <fmod.h>
 
+EXTERN_CVAR (Float, snd_midivolume)
 EXTERN_CVAR (Int, snd_samplerate)
 EXTERN_CVAR (Int, snd_mididevice)
 
@@ -86,6 +87,8 @@
 
 MusInfo *currSong;
 int		nomusic = 0;
+float	relative_volume = 1.0f;
+float	saved_relative_volume = 1.0f;	// this could be used to implement an ACS FadeMusic function
 
 //==========================================================================
 //
@@ -101,7 +104,7 @@
 	else if (self > 1.f)
 		self = 1.f;
 	else if (currSong != NULL && !currSong->IsMIDI ())
-		currSong->SetVolume (self);
+		currSong->SetVolume (clamp<float>(self*relative_volume, 0.0f, 1.0f));
 }
 
 MusInfo::~MusInfo ()
@@ -154,13 +157,14 @@
 }
 
 
-void I_PlaySong (void *handle, int _looping)
+void I_PlaySong (void *handle, int _looping, float rel_vol)
 {
 	MusInfo *info = (MusInfo *)handle;
 
 	if (!info || nomusic)
 		return;
 
+	saved_relative_volume = relative_volume = rel_vol;
 	info->Stop ();
 	info->Play (_looping ? true : false);
 	
@@ -382,3 +386,22 @@
 	return info ? info->SetPosition (order) : false;
 }
 
+
+// Sets relative music volume. Takes $musicvolume in SNDINFO into consideration
+void I_SetMusicVolume(float factor)
+{
+	factor=clamp<float>(factor, 0, 2.0f);
+	relative_volume = saved_relative_volume * factor;
+	snd_midivolume.Callback();
+	snd_musicvolume.Callback();
+}
+
+CCMD(testmusicvol)
+{
+	if (argv.argc()>1) 
+	{
+		relative_volume = (float)strtod(argv[1], NULL);
+		snd_midivolume.Callback();
+		snd_musicvolume.Callback();
+	}
+}
diff -durN 96/src/sound/i_music.h 96x/src/sound/i_music.h
--- 96/src/sound/i_music.h	2004-12-01 21:02:25.000000000 +0100
+++ 96x/src/sound/i_music.h	2005-07-24 20:12:09.000000000 +0200
@@ -46,8 +46,7 @@
 void I_BuildMIDIMenuList (struct value_s **values, float *numValues);
 
 // Volume.
-void I_SetMIDIVolume (float volume);
-void I_SetMusicVolume (int volume);
+void I_SetMusicVolume (float volume);
 
 // PAUSE game handling.
 void I_PauseSong (void *handle);
@@ -60,7 +59,7 @@
 // Called by anything that wishes to start music.
 //	Plays a song, and when the song is done,
 //	starts playing it again in an endless loop.
-void I_PlaySong (void *handle, int looping);
+void I_PlaySong (void *handle, int looping, float relative_vol=1.0f);
 
 // Stops a song.
 void I_StopSong (void *handle);
diff -durN 96/src/sound/i_musicinterns.h 96x/src/sound/i_musicinterns.h
--- 96/src/sound/i_musicinterns.h	2004-12-02 17:24:26.000000000 +0100
+++ 96x/src/sound/i_musicinterns.h	2005-07-24 20:12:09.000000000 +0200
@@ -23,6 +23,8 @@
 void I_InitMusicWin32 ();
 void I_ShutdownMusicWin32 ();
 
+extern float relative_volume;
+
 // The base music class. Everything is derived from this --------------------
 
 class MusInfo
diff -durN 96/src/sound/music_midi_midiout.cpp 96x/src/sound/music_midi_midiout.cpp
--- 96/src/sound/music_midi_midiout.cpp	2004-09-27 23:26:19.000000000 +0200
+++ 96x/src/sound/music_midi_midiout.cpp	2005-07-24 20:12:09.000000000 +0200
@@ -4,6 +4,8 @@
 #include "doomdef.h"
 #include "m_swap.h"
 
+EXTERN_CVAR (Float, snd_midivolume)
+
 struct MIDISong2::TrackInfo
 {
 	const BYTE *TrackBegin;
@@ -208,6 +210,7 @@
 		}
 	}
 
+	snd_midivolume.Callback();	// set volume to current music's properties
 	PlayerThread = CreateThread (NULL, 0, PlayerProc, this, 0, &tid);
 	if (PlayerThread == NULL)
 	{
diff -durN 96/src/sound/music_midi_stream.cpp 96x/src/sound/music_midi_stream.cpp
--- 96/src/sound/music_midi_stream.cpp	2004-09-27 23:26:31.000000000 +0200
+++ 96x/src/sound/music_midi_stream.cpp	2005-07-24 20:12:09.000000000 +0200
@@ -3,6 +3,7 @@
 #include "i_musicinterns.h"
 #include "i_music.h"
 
+#include "templates.h"
 #include "v_text.h"
 #include "m_menu.h"
 
@@ -27,11 +28,12 @@
 		self = 1.f;
 	else
 	{
-		DWORD onechanvol = (DWORD)(self * 65535.f);
+		float realvolume = clamp<float>(self * relative_volume, 0, 1.0f);
+		DWORD onechanvol = clamp<DWORD>((DWORD)(realvolume * 65535.f), 0, 65535);
 		midivolume = (onechanvol << 16) | onechanvol;
 		if (currSong && currSong->IsMIDI ())
 		{
-			currSong->SetVolume (self);
+			currSong->SetVolume (realvolume);
 		}
 	}
 }
diff -durN 96/src/sound/music_mus_midiout.cpp 96x/src/sound/music_mus_midiout.cpp
--- 96/src/sound/music_mus_midiout.cpp	2004-09-27 23:27:27.000000000 +0200
+++ 96x/src/sound/music_mus_midiout.cpp	2005-07-24 20:12:09.000000000 +0200
@@ -7,6 +7,8 @@
 extern DWORD midivolume;
 extern UINT mididevice;
 
+EXTERN_CVAR (Float, snd_midivolume)
+
 static const BYTE CtrlTranslate[15] =
 {
 	0,	// program change
@@ -137,6 +139,7 @@
 		}
 	}
 
+	snd_midivolume.Callback();	// set volume to current music's properties
 	PlayerThread = CreateThread (NULL, 0, PlayerProc, this, 0, &tid);
 	if (PlayerThread == NULL)
 	{
diff -durN 96/src/statnums.h 96x/src/statnums.h
--- 96/src/statnums.h	2004-03-24 15:11:41.000000000 +0100
+++ 96x/src/statnums.h	2005-07-24 20:12:09.000000000 +0200
@@ -51,4 +51,6 @@
 	STAT_LIGHTNING,							// The lightning thinker
 	STAT_DECALTHINKER,						// An object that thinks for a decal
 	STAT_INVENTORY,							// An inventory item
+	STAT_LIGHT,								// a sector light effect
+	STAT_LIGHTTRANSFER,						// a sector light transfer. These must always be ticked after the light effects!!!
 };
diff -durN 96/src/tarray.h 96x/src/tarray.h
--- 96/src/tarray.h	2004-10-27 10:28:49.000000000 +0200
+++ 96x/src/tarray.h	2005-06-22 15:18:33.000000000 +0200
@@ -101,8 +101,8 @@
 	void Delete (int index)
 	{
 		if (index < Count-1)
-			memmove (Array + index, Array + index + 1, (Count - index) * sizeof(T));
-		else if (index < Count)
+			memmove (Array + index, Array + index + 1, (Count - index - 1) * sizeof(T));
+		if (index < Count)
 			Count--;
 	}
 	void ShrinkToFit ()
diff -durN 96/src/thingdef.cpp 96x/src/thingdef.cpp
--- 96/src/thingdef.cpp	2004-11-13 23:13:32.000000000 +0100
+++ 96x/src/thingdef.cpp	2005-07-25 17:57:56.000000000 +0200
@@ -4,8 +4,8 @@
 ** Actor definitions
 **
 **---------------------------------------------------------------------------
-** Copyright 2002-2004 Christoph Oelckers
-** Copyright 2004 Randy Heit
+** Copyright 2002-2005 Christoph Oelckers
+** Copyright 2004-2005 Randy Heit
 ** All rights reserved.
 **
 ** Redistribution and use in source and binary forms, with or without
@@ -38,6 +38,7 @@
 **
 */
 
+#include "gi.h"
 #include "actor.h"
 #include "info.h"
 #include "sc_man.h"
@@ -57,177 +58,290 @@
 #include "autosegs.h"
 #include "i_system.h"
 #include "p_local.h"
+#include "p_effect.h"
+#include "v_palette.h"
 #include "doomerrors.h"
-#include "g_doom/a_doomglobal.h"
+#include "a_doomglobal.h"
+#include "a_weaponpiece.h"
 #include "p_conversation.h"
 
-static FRandom pr_camissile ("CustomActorfire");
-static FRandom pr_camelee ("CustomMelee");
-static FRandom pr_cabullet ("CustomBullet");
-static FRandom pr_cajump ("CustomJump");
-static FRandom pr_cwbullet ("CustomWpBullet");
-static FRandom pr_cwjump ("CustomWpJump");
-static FRandom pr_cwpunch ("CustomWpPunch");
 
-static const char * flagnames[]={
-	"*",			// Pickup items require special handling - don't set the flag explicitly
-	"SOLID",
-	"SHOOTABLE",
-	"NOSECTOR",
-	"NOBLOCKMAP",
-	"AMBUSH",
-	"JUSTHIT",
-	"JUSTATTACKED",
-	"SPAWNCEILING",
-	"NOGRAVITY",
-	"DROPOFF",
-	"*",			// MF_PICKUP does not make much sense
-	"NOCLIP",
-	"*",			// MF_SLIDE is completly unused
-	"FLOAT",
-	"TELEPORT",
-	"MISSILE",
-	"DROPPED",
-	"SHADOW",
-	"NOBLOOD",
-	"CORPSE",
-	"INFLOAT",
-	"COUNTKILL",
-	"COUNTITEM",
-	"SKULLFLY",
-	"NOTDMATCH",
-	"*",			// Unused
-	"FRIENDLY",
-	"*",			// MF_UNMORPHED - not to be used in an actor definition.
-	"NOLIFTDROP",
-	"STEALTH",
-	"ICECORPSE",
-	NULL
-};
-	
-static const char * flag2names[]={
-	"LOWGRAVITY",
-	"WINDTHRUST",
-	"HERETICBOUNCE",
-	"*",				// BLASTED
-	"*",				// MF2_FLY is for players only
-	"FLOORCLIP",
-	"SPAWNFLOAT",
-	"NOTELEPORT",
-	"RIPPER",
-	"PUSHABLE",
-	"SLIDESONWALLS",
-	"*",				// MF2_ONMOBJ is only used internally
-	"CANPASS",
-	"CANNOTPUSH",
-	"THRUGHOST",
-	"BOSS",
-	"*",
-	"NODAMAGETHRUST",
-	"TELESTOMP",
-	"FLOATBOB",
-	"HEXENBOUNCE",
-	"ACTIVATEIMPACT",
-	"CANPUSHWALLS",
-	"ACTIVATEMCROSS",
-	"ACTIVATEPCROSS",
-	"CANTLEAVEFLOORPIC",
-	"NONSHOOTABLE",
-	"INVULNERABLE",
-	"DORMANT",
-	"*",
-	"SEEKERMISSILE",
-	"REFLECTIVE",
-	NULL
-};
 
-static const char * flag3names[]=
+extern TArray<FActorInfo *> Decorations;
+
+// this stores the names of TypeInfo fields that have to be resolved after everything has been parsed.
+static TArray<char*> StringTypes;
+// 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;
+
+//==========================================================================
+//
+//
+//==========================================================================
+inline char * MS_Strdup(const char * s)
 {
-	"FLOORHUGGER",
-	"CEILINGHUGGER",
-	"NORADIUSDMG",
-	"GHOST",
-	"*",
-	"*",
-	"DONTSPLASH",
-	"*",					// NOSIGHTCHECK
-	"DONTOVERLAP",
-	"DONTMORPH",
-	"DONTSQUASH",
-	"*",					// EXPLOCOUNT
-	"FULLVOLACTIVE",
-	"ISMONSTER",
-	"SKYEXPLODE",
-	"STAYMORPHED",
-	"DONTBLAST",
-	"CANBLAST",
-	"NOTARGET",
-	"DONTGIB",
-	"NOBLOCKMONST",
-	"*",
-	"FULLVOLDEATH",
-	"CANBOUNCEWATER",
-	"NOWALLBOUNCESND",
-	"FOILINVUL",
-	"NOTELEOTHER",
-	"*",
-	"*",
-	"*",					// WARNBOT
-	"*",					// PUFFONACTORS
-	"*",					// HUNTPLAYERS
-	NULL
-};
+	return *s? strdup(s):NULL;
+}
 
-static const char * flag4names[]=
+//==========================================================================
+//
+// List of all flags
+//
+//==========================================================================
+
+#define DEFINE_FLAG(prefix, name, type, variable) { prefix##_##name, #name, RUNTIME_CLASS(type), offsetof(type, variable) }
+#define DEFINE_FLAG2(symbol, name, type, variable) { symbol, #name, RUNTIME_CLASS(type), offsetof(type, variable) }
+
+struct flagdef
 {
-	"*",					// NOHATEPLAYERS
-	"QUICKTORETALIATE",
-	"NOICEDEATH",
-	"*",					// BOSSDEATH
-	"RANDOMIZE",
-	"*",					// NOSKIN
-	"FIXMAPTHINGPOS",
-	"ACTLIKEBRIDGE",
-	"STRIFEDAMAGE",
-	"LONGMELEERANGE",		// new stuff
-	"MISSILEMORE",
-	"MISSILEEVENMORE",
-	"SHORTMISSILERANGE",
-	"DONTFALL",
-	"SEESDAGGERS",
-	"INCOMBAT",
-	"LOOKALLAROUND",
-	"STANDSTILL",
-	"SPECTRAL",
-	"FIRERESIST",
-	"NOSPLASHALERT",
-	NULL
+	int flagbit;
+	const char * name;
+	const TypeInfo * type;
+	int structoffset;
 };
 
-static const char *weaponflagnames[] =
+
+static flagdef ActorFlags[]=
 {
-	"noautofire",
-	"readysoundhalf",
-	"dontbob",
-	"*",
-	"firedamage",
-	"noalert",
-	"ammonotneeded"
+	DEFINE_FLAG(MF, SOLID, AActor, flags),
+	DEFINE_FLAG(MF, SHOOTABLE, AActor, flags),
+	DEFINE_FLAG(MF, NOSECTOR, AActor, flags),
+	DEFINE_FLAG(MF, NOBLOCKMAP, AActor, flags),
+	DEFINE_FLAG(MF, AMBUSH, AActor, flags),
+	DEFINE_FLAG(MF, JUSTHIT, AActor, flags),
+	DEFINE_FLAG(MF, JUSTATTACKED, AActor, flags),
+	DEFINE_FLAG(MF, SPAWNCEILING, AActor, flags),
+	DEFINE_FLAG(MF, NOGRAVITY, AActor, flags),
+	DEFINE_FLAG(MF, DROPOFF, AActor, flags),
+	DEFINE_FLAG(MF, NOCLIP, AActor, flags),
+	DEFINE_FLAG(MF, FLOAT, AActor, flags),
+	DEFINE_FLAG(MF, TELEPORT, AActor, flags),
+	DEFINE_FLAG(MF, MISSILE, AActor, flags),
+	DEFINE_FLAG(MF, DROPPED, AActor, flags),
+	DEFINE_FLAG(MF, SHADOW, AActor, flags),
+	DEFINE_FLAG(MF, NOBLOOD, AActor, flags),
+	DEFINE_FLAG(MF, CORPSE, AActor, flags),
+	DEFINE_FLAG(MF, INFLOAT, AActor, flags),
+	DEFINE_FLAG(MF, COUNTKILL, AActor, flags),
+	DEFINE_FLAG(MF, COUNTITEM, AActor, flags),
+	DEFINE_FLAG(MF, SKULLFLY, AActor, flags),
+	DEFINE_FLAG(MF, NOTDMATCH, AActor, flags),
+	DEFINE_FLAG(MF, FRIENDLY, AActor, flags),
+	DEFINE_FLAG(MF, NOLIFTDROP, AActor, flags),
+	DEFINE_FLAG(MF, STEALTH, AActor, flags),
+	DEFINE_FLAG(MF, ICECORPSE, AActor, flags),
+	DEFINE_FLAG2(MF2_LOGRAV, LOWGRAVITY, AActor, flags2),
+	DEFINE_FLAG(MF2, WINDTHRUST, AActor, flags2),
+	DEFINE_FLAG(MF2, HERETICBOUNCE , AActor, flags2),
+	DEFINE_FLAG(MF2, FLOORCLIP, AActor, flags2),
+	DEFINE_FLAG(MF2, SPAWNFLOAT, AActor, flags2),
+	DEFINE_FLAG(MF2, NOTELEPORT, AActor, flags2),
+	DEFINE_FLAG2(MF2_RIP, RIPPER, AActor, flags2),
+	DEFINE_FLAG(MF2, PUSHABLE, AActor, flags2),
+	DEFINE_FLAG2(MF2_SLIDE, SLIDESONWALLS, AActor, flags2),
+	DEFINE_FLAG2(MF2_PASSMOBJ, CANPASS, AActor, flags2),
+	DEFINE_FLAG(MF2, CANNOTPUSH, AActor, flags2),
+	DEFINE_FLAG(MF2, THRUGHOST, AActor, flags2),
+	DEFINE_FLAG(MF2, BOSS, AActor, flags2),
+	DEFINE_FLAG2(MF2_NODMGTHRUST, NODAMAGETHRUST, AActor, flags2),
+	DEFINE_FLAG(MF2, TELESTOMP, AActor, flags2),
+	DEFINE_FLAG(MF2, FLOATBOB, AActor, flags2),
+	DEFINE_FLAG(MF2, HEXENBOUNCE, AActor, flags2),
+	DEFINE_FLAG(MF2, DOOMBOUNCE, AActor, flags2),
+	DEFINE_FLAG2(MF2_IMPACT, ACTIVATEIMPACT, AActor, flags2),
+	DEFINE_FLAG2(MF2_PUSHWALL, CANPUSHWALLS, AActor, flags2),
+	DEFINE_FLAG2(MF2_MCROSS, ACTIVATEMCROSS, AActor, flags2),
+	DEFINE_FLAG2(MF2_PCROSS, ACTIVATEPCROSS, AActor, flags2),
+	DEFINE_FLAG(MF2, CANTLEAVEFLOORPIC, AActor, flags2),
+	DEFINE_FLAG(MF2, NONSHOOTABLE, AActor, flags2),
+	DEFINE_FLAG(MF2, INVULNERABLE, AActor, flags2),
+	DEFINE_FLAG(MF2, DORMANT, AActor, flags2),
+	DEFINE_FLAG(MF2, SEEKERMISSILE, AActor, flags2),
+	DEFINE_FLAG(MF2, REFLECTIVE, AActor, flags2),
+	DEFINE_FLAG(MF3, FLOORHUGGER, AActor, flags3),
+	DEFINE_FLAG(MF3, CEILINGHUGGER, AActor, flags3),
+	DEFINE_FLAG(MF3, NORADIUSDMG, AActor, flags3),
+	DEFINE_FLAG(MF3, GHOST, AActor, flags3),
+	DEFINE_FLAG(MF3, ALWAYSPUFF, AActor, flags3),
+	DEFINE_FLAG(MF3, DONTSPLASH, AActor, flags3),
+	DEFINE_FLAG(MF3, DONTOVERLAP, AActor, flags3),
+	DEFINE_FLAG(MF3, DONTMORPH, AActor, flags3),
+	DEFINE_FLAG(MF3, DONTSQUASH, AActor, flags3),
+	DEFINE_FLAG(MF3, FULLVOLACTIVE, AActor, flags3),
+	DEFINE_FLAG(MF3, ISMONSTER, AActor, flags3),
+	DEFINE_FLAG(MF3, SKYEXPLODE, AActor, flags3),
+	DEFINE_FLAG(MF3, STAYMORPHED, AActor, flags3),
+	DEFINE_FLAG(MF3, DONTBLAST, AActor, flags3),
+	DEFINE_FLAG(MF3, CANBLAST, AActor, flags3),
+	DEFINE_FLAG(MF3, NOTARGET, AActor, flags3),
+	DEFINE_FLAG(MF3, DONTGIB, AActor, flags3),
+	DEFINE_FLAG(MF3, NOBLOCKMONST, AActor, flags3),
+	DEFINE_FLAG(MF3, FULLVOLDEATH, AActor, flags3),
+	DEFINE_FLAG(MF3, CANBOUNCEWATER, AActor, flags3),
+	DEFINE_FLAG(MF3, NOWALLBOUNCESND, AActor, flags3),
+	DEFINE_FLAG(MF3, FOILINVUL, AActor, flags3),
+	DEFINE_FLAG(MF3, NOTELEOTHER, AActor, flags3),
+	DEFINE_FLAG(MF3, BLOODLESSIMPACT, AActor, flags3),
+	DEFINE_FLAG(MF3, NOEXPLODEFLOOR, AActor, flags3),
+	DEFINE_FLAG(MF3, PUFFONACTORS, AActor, flags3),
+	DEFINE_FLAG(MF4, QUICKTORETALIATE, AActor, flags4),
+	DEFINE_FLAG(MF4, NOICEDEATH, AActor, flags4),
+	DEFINE_FLAG(MF4, RANDOMIZE, AActor, flags4),
+	DEFINE_FLAG(MF4, FIXMAPTHINGPOS , AActor, flags4),
+	DEFINE_FLAG(MF4, ACTLIKEBRIDGE, AActor, flags4),
+	DEFINE_FLAG(MF4, STRIFEDAMAGE, AActor, flags4),
+	DEFINE_FLAG(MF4, LONGMELEERANGE, AActor, flags4),
+	DEFINE_FLAG(MF4, MISSILEMORE, AActor, flags4),
+	DEFINE_FLAG(MF4, MISSILEEVENMORE, AActor, flags4),
+	DEFINE_FLAG(MF4, SHORTMISSILERANGE, AActor, flags4),
+	DEFINE_FLAG(MF4, DONTFALL, AActor, flags4),
+	DEFINE_FLAG(MF4, SEESDAGGERS, AActor, flags4),
+	DEFINE_FLAG(MF4, INCOMBAT, AActor, flags4),
+	DEFINE_FLAG(MF4, LOOKALLAROUND, AActor, flags4),
+	DEFINE_FLAG(MF4, STANDSTILL, AActor, flags4),
+	DEFINE_FLAG(MF4, SPECTRAL, AActor, flags4),
+	DEFINE_FLAG(MF4, FIRERESIST, AActor, flags4),
+	DEFINE_FLAG(MF4, NOSPLASHALERT, AActor, flags4),
+	DEFINE_FLAG(MF4, SYNCHRONIZED, AActor, flags4),
+	DEFINE_FLAG(MF4, NOTARGETSWITCH, AActor, flags4),
+	DEFINE_FLAG(MF4, DONTHURTSPECIES, AActor, flags4),
+	DEFINE_FLAG(MF4, SHIELDREFLECT, AActor, flags4),
+	DEFINE_FLAG(MF4, DEFLECT, AActor, flags4),
+	DEFINE_FLAG(MF4, ALLOWPARTICLES, AActor, flags4),
+	DEFINE_FLAG(MF4, EXTREMEDEATH, AActor, flags4),
+	DEFINE_FLAG(MF4, NOEXTREMEDEATH, AActor, flags4),
+
+	// Effect flags
+	DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects),
+	DEFINE_FLAG2(FX_ROCKET, ROCKETTRAIL, AActor, effects),
+	DEFINE_FLAG2(FX_GRENADE, GRENADETRAIL, AActor, effects),
+
+	// Inventory flags
+	DEFINE_FLAG(IF, QUIET, AInventory, ItemFlags),
+	DEFINE_FLAG(IF, AUTOACTIVATE, AInventory, ItemFlags),
+	DEFINE_FLAG(IF, UNDROPPABLE, AInventory, ItemFlags),
+	DEFINE_FLAG(IF, INVBAR, AInventory, ItemFlags),
+	DEFINE_FLAG(IF, HUBPOWER, AInventory, ItemFlags),
+	DEFINE_FLAG(IF, INTERHUBSTRIP, AInventory, ItemFlags),
+	DEFINE_FLAG(IF, PICKUPFLASH, AInventory, ItemFlags),
+	DEFINE_FLAG(IF, ALWAYSPICKUP, AInventory, ItemFlags),
+	DEFINE_FLAG(IF, FANCYPICKUPSOUND, AInventory, ItemFlags),
+	DEFINE_FLAG(IF, CHEATNOTWEAPON, AWeapon, ItemFlags),		// this should really be a weapon flag, not an inventory flag!
+	// Weapon flags
+	DEFINE_FLAG(WIF, NOAUTOFIRE, AWeapon, WeaponFlags),
+	DEFINE_FLAG(WIF, READYSNDHALF, AWeapon, WeaponFlags),
+	DEFINE_FLAG(WIF, DONTBOB, AWeapon, WeaponFlags),
+	DEFINE_FLAG(WIF, AXEBLOOD, AWeapon, WeaponFlags),
+	DEFINE_FLAG(WIF, NOALERT, AWeapon, WeaponFlags),
+	DEFINE_FLAG(WIF, AMMO_OPTIONAL, AWeapon, WeaponFlags),
+	DEFINE_FLAG(WIF, ALT_AMMO_OPTIONAL, AWeapon, WeaponFlags),
+	DEFINE_FLAG(WIF, PRIMARY_USES_BOTH, AWeapon, WeaponFlags),
+	DEFINE_FLAG(WIF, WIMPY_WEAPON, AWeapon, WeaponFlags),
+	DEFINE_FLAG(WIF, POWERED_UP, AWeapon, WeaponFlags),
+	//DEFINE_FLAG(WIF, EXTREME_DEATH, AWeapon, WeaponFlags),	// this should be removed now!
+	DEFINE_FLAG(WIF, STAFF2_KICKBACK, AWeapon, WeaponFlags),
+	DEFINE_FLAG(WIF_BOT, EXPLOSIVE, AWeapon, WeaponFlags),
+	DEFINE_FLAG2(WIF_BOT_MELEE, MELEEWEAPON, AWeapon, WeaponFlags),
+	DEFINE_FLAG(WIF_BOT, BFG, AWeapon, WeaponFlags),
+	//WIF_BOT_REACTION_SKILL_THING = 1<<31, // I don't understand this
+	//DEFINE_FLAG(WIF , HITS_GHOSTS, AWeapon, WeaponFlags),	// I think it would be smarter to remap the THRUGHOST flag to this
 };
 
+//==========================================================================
+//
+// Find a flag by name using a binary search
+//
+//==========================================================================
+static int STACK_ARGS flagcmp(const void * a, const void * b)
+{
+	return stricmp( ((flagdef*)a)->name, ((flagdef*)b)->name);
+}
+
+static flagdef * FindFlag(char * string)
+{
+	static bool flagsorted=false;
+
+	if (!flagsorted) 
+	{
+		qsort(ActorFlags, sizeof(ActorFlags)/sizeof(ActorFlags[0]), sizeof(ActorFlags[0]), flagcmp);
+		flagsorted=true;
+	}
+	strupr(string);
+
+	int min = 0, max = sizeof(ActorFlags)/sizeof(ActorFlags[0])-1;
+
+	while (min <= max)
+	{
+		int mid = (min + max) / 2;
+		int lexval = strcmp (string, ActorFlags[mid].name);
+		if (lexval == 0)
+		{
+			return &ActorFlags[mid];
+		}
+		else if (lexval > 0)
+		{
+			min = mid + 1;
+		}
+		else
+		{
+			max = mid - 1;
+		}
+	}
+	return NULL;
+}
+
+int EvalExpressionI (int id, AActor *self);
+//===========================================================================
+//
+// A_ChangeFlag
+//
+// This cannot be placed in thingdef_codeptr because it needs the flag table
+//
+//===========================================================================
+void A_ChangeFlag(AActor * self)
+{
+	int index=CheckIndex(2);
+	char * flagname = (char *)StateParameters[index];	// the string is changed but that doesn't really matter
+	int expression = EvalExpressionI (StateParameters[index+1], self);
+
+	flagdef * fd = FindFlag(flagname);
+
+	if (fd && self->IsKindOf(fd->type))
+	{
+		int * flagp = (int*) (((char*)self) + fd->structoffset);
+
+		if (expression) *flagp|= fd->flagbit;
+		else *flagp&=~fd->flagbit;
+	}
+	else
+	{
+		Printf("Unknown flag '%s' in '%s'\n", flagname, self->GetClass()->Name+1);
+	}
+}
+
+
+//==========================================================================
+//
+// Action functions
+//
+//==========================================================================
+
+
 #include "thingdef_specials.h"
 
 struct AFuncDesc
 {
 	const char * Name;
 	actionf_p Function;
+	const char * parameters;
 };
 
 
 // Prototype the code pointers
 #define WEAPON(x)	void A_##x(AActor*);	
 #define ACTOR(x)	void A_##x(AActor*);
-WEAPON(FireProjectile)
 ACTOR(MeleeAttack)
 ACTOR(MissileAttack)
 ACTOR(ComboAttack)
@@ -236,414 +350,518 @@
 ACTOR(ActiveAndUnblock)
 ACTOR(ActiveSound)
 ACTOR(FastChase)
+ACTOR(CentaurDefend)
 ACTOR(FreezeDeath)
 ACTOR(FreezeDeathChunks)
 ACTOR(GenericFreezeDeath)
 ACTOR(IceGuyDie)
 ACTOR(M_Saw)
+ACTOR(Wander)
+ACTOR(Look2)
+ACTOR(TossGib)
+ACTOR(SentinelBob)
+ACTOR(SentinelRefire)
+ACTOR(Tracer2)
+ACTOR(SetShadow)
+ACTOR(ClearShadow)
+ACTOR(GetHurt)
+ACTOR(TurretLook)
+ACTOR(KlaxonBlare)
+ACTOR(20e10)
+ACTOR(Countdown)
+ACTOR(AlertMonsters)
+ACTOR(PlaySound)
+ACTOR(PlayWeaponSound)
+ACTOR(LoopActiveSound)
+ACTOR(StopSound)
+ACTOR(SeekerMissile)
+ACTOR(Jump)
+ACTOR(ExplodeParms)
+ACTOR(CallSpecial)
+ACTOR(CustomMissile)
+ACTOR(CustomBulletAttack)
+ACTOR(JumpIfHealthLower)
+ACTOR(JumpIfCloser)
+ACTOR(JumpIfNoAmmo)
+ACTOR(JumpIfInventory)
+ACTOR(CustomPunch)
+ACTOR(FireBullets)
+ACTOR(FireCustomMissile)
+ACTOR(RailAttack)
+ACTOR(CustomRailgun)
+ACTOR(LightInverse)
+ACTOR(GiveInventory)
+ACTOR(TakeInventory)
+ACTOR(SpawnItem)
+ACTOR(ThrowGrenade)
+ACTOR(Recoil)
+ACTOR(SelectWeapon)
+ACTOR(Print)
+ACTOR(SetTranslucent)
+ACTOR(FadeOut)
+ACTOR(SpawnDebris)
+ACTOR(SetSolid)
+ACTOR(UnsetSolid)
+ACTOR(SetFloat)
+ACTOR(UnsetFloat)
+ACTOR(BishopMissileWeave)
+ACTOR(CStaffMissileSlither)
+ACTOR(CheckSight)
+ACTOR(ExtChase)
+ACTOR(Jiggle)
+ACTOR(DropInventory)
+ACTOR(SetBlend)
+ACTOR(JumpIf)
+ACTOR(SetUserVar)
+ACTOR(SetUserVarRandom)
+ACTOR(KillMaster)
+ACTOR(KillChildren)
+
+
 #include "d_dehackedactions.h"
 
+#define FUNC(name, parm) { #name, name, parm },
 // Declare the code pointer table
 AFuncDesc AFTable[]=
 {
-#define WEAPON(x)	
-#define ACTOR(x)	{"A_"#x, A_##x},
-ACTOR(BulletAttack)
-ACTOR(ScreamAndUnblock)
-ACTOR(ActiveAndUnblock)
-ACTOR(ActiveSound)
-ACTOR(FastChase)
-ACTOR(FreezeDeath)
-ACTOR(FreezeDeathChunks)
-ACTOR(GenericFreezeDeath)
-ACTOR(IceGuyDie)			// useful for bursting a monster into ice chunks without delay
-ACTOR(M_Saw)
-#include "d_dehackedactions.h"
-	{"A_Fall", A_NoBlocking},	// Allow Doom's old name, too, for this function
-	{NULL,NULL}
-};
 
-static const char *BasicAttackNames[4] =
-{
-	"A_MeleeAttack",
-	"A_MissileAttack",
-	"A_ComboAttack",
-	NULL
-};
-static const actionf_p BasicAttacks[3] =
-{
-	A_MeleeAttack,
-	A_MissileAttack,
-	A_ComboAttack
-};
+	// most of the functions available in Dehacked
+	FUNC(A_MonsterRail, NULL)
+	FUNC(A_BFGSpray, "mx")
+	FUNC(A_Pain, NULL)
+	FUNC(A_NoBlocking, NULL)
+	FUNC(A_XScream, NULL)
+	FUNC(A_Look, NULL)
+	FUNC(A_Chase, NULL)
+	FUNC(A_FaceTarget, NULL)
+	FUNC(A_PosAttack, NULL)
+	FUNC(A_Scream, NULL)
+	FUNC(A_SPosAttack, NULL)
+	FUNC(A_VileChase, NULL)
+	FUNC(A_VileStart, NULL)
+	FUNC(A_VileTarget, NULL)
+	FUNC(A_VileAttack, NULL)
+	FUNC(A_StartFire, NULL)
+	FUNC(A_Fire, NULL)
+	FUNC(A_FireCrackle, NULL)
+	FUNC(A_Tracer, NULL)
+	FUNC(A_SkelWhoosh, NULL)
+	FUNC(A_SkelFist, NULL)
+	FUNC(A_SkelMissile, NULL)
+	FUNC(A_FatRaise, NULL)
+	FUNC(A_FatAttack1, "m")
+	FUNC(A_FatAttack2, "m")
+	FUNC(A_FatAttack3, "m")
+	FUNC(A_BossDeath, NULL)
+	FUNC(A_CPosAttack, NULL)
+	FUNC(A_CPosRefire, NULL)
+	FUNC(A_TroopAttack, NULL)
+	FUNC(A_SargAttack, NULL)
+	FUNC(A_HeadAttack, NULL)
+	FUNC(A_BruisAttack, NULL)
+	FUNC(A_SkullAttack, NULL)
+	FUNC(A_Metal, NULL)
+	FUNC(A_SpidRefire, NULL)
+	FUNC(A_BabyMetal, NULL)
+	FUNC(A_BspiAttack, NULL)
+	FUNC(A_Hoof, NULL)
+	FUNC(A_CyberAttack, NULL)
+	FUNC(A_PainAttack, "m")
+	FUNC(A_PainDie, "m")
+	FUNC(A_KeenDie, NULL)
+	FUNC(A_BrainPain, NULL)
+	FUNC(A_BrainScream, NULL)
+	FUNC(A_BrainDie, NULL)
+	FUNC(A_BrainAwake, NULL)
+	FUNC(A_BrainSpit, NULL)
+	FUNC(A_SpawnSound, NULL)
+	FUNC(A_SpawnFly, NULL)
+	FUNC(A_BrainExplode, NULL)
+	FUNC(A_Die, NULL)
+	FUNC(A_Detonate, NULL)
+	FUNC(A_Mushroom, "mx")
 
-#define WEAPON(x)	{"A_"#x, A_##x},
+	FUNC(A_SetFloorClip, NULL)
+	FUNC(A_UnSetFloorClip, NULL)
+	FUNC(A_HideThing, NULL)
+	FUNC(A_UnHideThing, NULL)
+	FUNC(A_SetInvulnerable, NULL)
+	FUNC(A_UnSetInvulnerable, NULL)
+	FUNC(A_SetReflective, NULL)
+	FUNC(A_UnSetReflective, NULL)
+	FUNC(A_SetReflectiveInvulnerable, NULL)
+	FUNC(A_UnSetReflectiveInvulnerable, NULL)
+	FUNC(A_SetShootable, NULL)
+	FUNC(A_UnSetShootable, NULL)
+	FUNC(A_NoGravity, NULL)
+	FUNC(A_Gravity, NULL)
+	FUNC(A_LowGravity, NULL)
+	{"A_Fall", A_NoBlocking, NULL},		// Allow Doom's old name, too, for this function
+	FUNC(A_SetSolid, NULL)
+	FUNC(A_UnsetSolid, NULL)
+	FUNC(A_SetFloat, NULL)
+	FUNC(A_UnsetFloat, NULL)
+	FUNC(A_BishopMissileWeave, NULL)
+	FUNC(A_CStaffMissileSlither, NULL)
 
-AFuncDesc WFTable[]=
-{
-	WEAPON(Light0)
-	WEAPON(Light1)
-	WEAPON(Light2)
-	WEAPON(WeaponReady)
-	WEAPON(Lower)
-	WEAPON(Raise)
-	WEAPON(ReFire)
-	WEAPON(Punch)
-	WEAPON(CheckReload)
-	WEAPON(GunFlash)
-	WEAPON(Saw)
-	WEAPON(FireProjectile)
-	{NULL,NULL}
-};
+	// For better chainsaw attacks
+	FUNC(A_M_Saw, NULL)
 
-extern TArray<FActorInfo *> Decorations;
+	// some functions from the old DECORATE parser
+	FUNC(A_BulletAttack, NULL)
+	FUNC(A_ScreamAndUnblock, NULL)
+	FUNC(A_ActiveAndUnblock, NULL)
+	FUNC(A_ActiveSound, NULL)
 
+	// useful functions from Hexen
+	FUNC(A_FastChase, NULL)
+	FUNC(A_FreezeDeath, NULL)
+	FUNC(A_FreezeDeathChunks, NULL)
+	FUNC(A_GenericFreezeDeath, NULL)
+	FUNC(A_IceGuyDie, NULL)			// useful for bursting a monster into ice chunks without delay
+	FUNC(A_CentaurDefend, NULL)
+	FUNC(A_BishopMissileWeave, NULL)
+	FUNC(A_CStaffMissileSlither, NULL)
 
-struct FDropItem 
-{
-	const char * Name;
-	int probability;
-	int amount;
-	FDropItem * Next;
-};
-TArray<FDropItem *> DropItemList;
+	// useful functions from Strife
+	FUNC(A_Wander, NULL)
+	FUNC(A_Look2, NULL)
+	FUNC(A_TossGib, NULL)
+	FUNC(A_SentinelBob, NULL)
+	FUNC(A_SentinelRefire, NULL)
+	FUNC(A_Tracer2, NULL)
+	FUNC(A_SetShadow, NULL)
+	FUNC(A_ClearShadow, NULL)
+	FUNC(A_GetHurt, NULL)
+	FUNC(A_TurretLook, NULL)
+	FUNC(A_KlaxonBlare, NULL)
+	FUNC(A_Countdown, NULL)
+	FUNC(A_AlertMonsters, NULL)
+	{"A_CheckTerrain", A_20e10, NULL },	// This needs a better name!
 
-struct FBasicAttack
-{
-	int MeleeDamage;
-	int MeleeSound;
-	const char *MissileName;
-	fixed_t MissileHeight;
-};
-TArray<FBasicAttack> BasicAttackList;
+	// Only selected original weapon functions will be available. 
+	// All the attack pointers are somewhat tricky due to the way the flash state is handled
+	FUNC(A_Light0, NULL)
+	FUNC(A_Light1, NULL)
+	FUNC(A_Light2, NULL)
+	FUNC(A_LightInverse, NULL)
+	FUNC(A_WeaponReady, NULL)
+	FUNC(A_Lower, NULL)
+	FUNC(A_Raise, NULL)
+	FUNC(A_ReFire, NULL)
+	FUNC(A_Punch, NULL)
+	FUNC(A_CheckReload, NULL)
+	FUNC(A_GunFlash, NULL)
+	FUNC(A_Saw, NULL)
 
-struct FMissileAttack
-{
-	const char *MissileName;
-	fixed_t SpawnHeight;
-	fixed_t Spawnofs_XY;
-	angle_t Angle;
-	bool aimparallel;
-};
+	// DECORATE specific functions
+	FUNC(A_BulletAttack, NULL)
+	FUNC(A_PlaySound, "S" )
+	FUNC(A_PlayWeaponSound, "S" )
+	FUNC(A_LoopActiveSound, NULL )
+	FUNC(A_StopSound, NULL )
+	FUNC(A_SeekerMissile, "XX" )
+	FUNC(A_Jump, "XL" )
+	FUNC(A_CustomMissile, "MXXxxx" )
+	FUNC(A_CustomBulletAttack, "XXXXmx" )
+	FUNC(A_CustomRailgun, "Xxccxxx" )
+	FUNC(A_JumpIfHealthLower, "XL" )
+	FUNC(A_JumpIfCloser, "XL" )
+	FUNC(A_JumpIfInventory, "MXL" )
+	FUNC(A_GiveInventory, "Mx" )
+	FUNC(A_TakeInventory, "Mx" )
+	FUNC(A_SpawnItem, "Mxxx" )
+	FUNC(A_ThrowGrenade, "Mxxxx" )
+	FUNC(A_SelectWeapon, "M")
+	FUNC(A_Print, "T")
+	FUNC(A_SetTranslucent, "Xx")
+	FUNC(A_FadeOut, "x")
+	FUNC(A_SpawnDebris, "M")
+	FUNC(A_CheckSight, "L")
+	FUNC(A_ExtChase, "XXxx")
+	FUNC(A_Jiggle, "XX")
+	FUNC(A_DropInventory, "M")
+	FUNC(A_SetBlend, "CXX")
+	FUNC(A_ChangeFlag, "TX")
+	FUNC(A_JumpIf, "XL")
+	FUNC(A_SetUserVar, "IX")
+	FUNC(A_KillMaster, NULL)
+	FUNC(A_KillChildren, NULL)
+	{"A_BasicAttack", A_ComboAttack, "ISMF" },
 
-struct FBulletAttack
-{
-	angle_t Spread_XY;
-	angle_t Spread_Z;
-	int NumBullets;
-	int DamagePerBullet;
-	const char * PuffType;
+	// Weapon only functions
+	FUNC(A_JumpIfNoAmmo, "L" )
+	FUNC(A_CustomPunch, "Xxxmx" )
+	FUNC(A_FireBullets, "XXXXmxx" )
+	FUNC(A_FireCustomMissile, "Mxxxx" )
+	FUNC(A_RailAttack, "Xxxccxx" )
+	FUNC(A_Recoil, "X")
 };
 
-struct FJump
+//==========================================================================
+//
+// Find a function by name using a binary search
+//
+//==========================================================================
+static int STACK_ARGS funccmp(const void * a, const void * b)
 {
-	int compare;
-	int offset;
-};
+	return stricmp( ((AFuncDesc*)a)->Name, ((AFuncDesc*)b)->Name);
+}
 
-struct FLnSpec
+static AFuncDesc * FindFunction(char * string)
 {
-	int special;
-	int args[5];
-};
+	static bool funcsorted=false;
 
-struct FWeaponSingleParam : public FWeaponParam
-{
-	int param;
-};
+	if (!funcsorted) 
+	{
+		qsort(AFTable, sizeof(AFTable)/sizeof(AFTable[0]), sizeof(AFTable[0]), funccmp);
+		funcsorted=true;
+	}
 
-struct FWeaponJump : public FWeaponParam
-{
-	int compare;
-	int offset;
-};
+	int min = 0, max = sizeof(AFTable)/sizeof(AFTable[0])-1;
 
-struct FWeaponMissileAttack : public FWeaponParam
-{
-	const char * MissileName;
-	fixed_t SpawnHeight;
-	fixed_t Spawnofs_XY;
-	angle_t Angle;
-	bool dontuseammo;
-};
+	while (min <= max)
+	{
+		int mid = (min + max) / 2;
+		int lexval = stricmp (string, AFTable[mid].Name);
+		if (lexval == 0)
+		{
+			return &AFTable[mid];
+		}
+		else if (lexval > 0)
+		{
+			min = mid + 1;
+		}
+		else
+		{
+			max = mid - 1;
+		}
+	}
+	return NULL;
+}
 
-struct FWeaponBulletAttack : public FWeaponParam
-{
-	angle_t Spread_XY;
-	angle_t Spread_Z;
-	int NumBullets;
-	int DamagePerBullet;
-};
 
-struct FRailAttack : public FWeaponParam
+static const char *BasicAttackNames[4] =
 {
-	int damage;
-	int Spawnofs_XY;
+	"A_MeleeAttack",
+	"A_MissileAttack",
+	"A_ComboAttack",
+	NULL
 };
-
-struct FExplodeParms
+static const actionf_p BasicAttacks[3] =
 {
-	bool HurtShooter;
-	int ExplosionRadius, ExplosionDamage;
+	A_MeleeAttack,
+	A_MissileAttack,
+	A_ComboAttack
 };
 
-static TArray<FMissileAttack> AttackList;
-static TArray<FBulletAttack> BulletAttackList;
-static TArray<FJump> JumpList;
-static TArray<FLnSpec> LineSpecialList;
-static TArray<FExplodeParms> ExplodeList;
-TArray<FWeaponParam *> WeaponParams;
 
-static TArray<FState> StateArray;
-static int StateRangeEnd[3];
-static int StateRangeType[3];
-enum
-{
-	SRT_Actor,
-	SRT_WeaponLevel1,
-	SRT_WeaponLevel2
-};
-int NumRanges;
 
-//==========================================================================
+//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
 //
-// Extra info maintained while defining an actor. The original
-// implementation stored these in a CustomActor class. They have all been
-// moved into action function parameters so that no special CustomActor
-// class is necessary.
+// Translation parsing
 //
-//==========================================================================
-
-struct Baggage
-{
-	FActorInfo *Info;
-	bool DropItemSet;
-	bool StateSet;
-	int CurrentState;
-
-	FExplodeParms EParms;
-	FDropItem *DropItemList;
-	FBasicAttack BAttack;
-
-	const char *PuffType;
-	const char *HitPuffType;
-	int AttackSound;
-	int AmmoGive;
-
-	int WeapNum;
-};
+//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
+static int NumUsedTranslations;
+static int NumUsedBloodTranslations;
+byte decorate_translations[256*256*2];
+PalEntry BloodTranslations[256];
 
-enum
+void InitDecorateTranslations()
 {
-	ACMETA_BASE				= 0x83000,
-	ACMETA_DropItems,		// Int (index into DropItemList)
-};
-
-struct ActorProps { const char *name; void (*Handler)(AActor *defaults, Baggage &bag); };
-
-typedef ActorProps (*ActorPropHandler) (register const char *str, register unsigned int len);
-
-static const ActorProps *is_actorprop (const char *str);
-static const ActorProps *is_weaponprop (const char *str);
-static const ActorProps *is_weapondefprop (const char *str);
+	// The translation tables haven't been allocated yet so we may as easily use a static buffer instead!
+	NumUsedBloodTranslations = NumUsedTranslations = 0;
+	for(int i=0;i<256*256*2;i++) decorate_translations[i]=i&255;
+}
 
-//==========================================================================
-//
-// Customizable attack functions which use actor parameters.
-// I think this is among the most requested stuff ever ;-)
-//
-//==========================================================================
-static void DoAttack (AActor *self, bool domelee, bool domissile)
+static bool Check(char *& range,  char c, bool error=true)
 {
-	if (self->target == NULL)
-		return;
-
-	size_t index = self->state->GetMisc1_2();
-	if (index >= BasicAttackList.Size())
-		return;
-
-	const FBasicAttack *cust = &BasicAttackList[index];
-				
-	A_FaceTarget (self);
-	if (domelee && self->CheckMeleeRange ())
+	while (isspace(*range)) range++;
+	if (*range==c)
 	{
-		int damage = pr_camelee.HitDice(cust->MeleeDamage);
-		if (cust->MeleeSound) S_SoundID (self, CHAN_WEAPON, cust->MeleeSound, 1, ATTN_NORM);
-		P_DamageMobj (self->target, self, self, damage, MOD_HIT);
-		P_TraceBleed (damage, self->target, self);
+		range++;
+		return true;
 	}
-	else if (domissile)
+	if (error)
 	{
-		const TypeInfo * ti=TypeInfo::FindType(cust->MissileName);
-		if (ti) 
-		{
-#if 1
-			// Hmm, the result of this hack (taken from A_SkelMissile) is
-			// significantly more desirable than using P_SpawnMissileZ!
-			self->z+=cust->MissileHeight-32*FRACUNIT;
-			AActor * missile = P_SpawnMissile (self, self->target, ti);
-			self->z-=cust->MissileHeight-32*FRACUNIT;
-#else
-			// ZDoom has a P_SpawnMissile Z to do this.
-			// Edit: why is the aiming of this so much worse than the hack I used previously???
-			AActor * missile = P_SpawnMissileZ(self, self->z+cust->MissileHeight, self->target, ti);
-#endif
-
-			// automatic handling of seeker missiles
-			if (missile && missile->flags2&MF2_SEEKERMISSILE)
-			{
-				missile->tracer=self->target;
-			}
-		}
+		//SC_ScriptError("Invalid syntax in translation specification: '%c' expected", c);
 	}
+	return false;
 }
 
-void A_MeleeAttack(AActor * self)
-{
-	DoAttack(self, true, false);
-}
-
-void A_MissileAttack(AActor * self)
-{
-	DoAttack(self, false, true);
-}
-
-void A_ComboAttack(AActor * self)
-{
-	DoAttack(self, true, true);
-}
-
-//==========================================================================
-//
-// Custom sound functions. These use misc1 and misc2 in the state structure
-//
-//==========================================================================
-void A_PlaySound(AActor * self)
-{
-	int soundid = self->state->GetMisc1_2();
-	S_SoundID (self, CHAN_BODY, soundid, 1, ATTN_NORM);
-}
 
-void A_PlayWeaponSound(AActor * self)
+static void AddToTranslation(unsigned char * translation, char * range)
 {
-	int soundid = self->state->GetMisc1_2();
-	S_SoundID (self, CHAN_WEAPON, soundid, 1, ATTN_NORM);
-}
+	int start,end;
 
-//==========================================================================
-//
-// Generic seeker missile function
-//
-//==========================================================================
-void A_SeekerMissile(AActor * self)
-{
-	P_SeekerMissile(self, self->state->GetMisc1()*ANGLE_1, self->state->GetMisc2()*ANGLE_1);
-}
+	start=strtol(range, &range, 10);
+	if (!Check(range, ':')) return;
+	end=strtol(range, &range, 10);
+	if (!Check(range, '=')) return;
+	if (!Check(range, '[', false))
+	{
+		int pal1,pal2;
+		fixed_t palcol, palstep;
 
-//==========================================================================
-//
-// Hitscan attack with a customizable amount of bullets (specified in damage)
-//
-//==========================================================================
-void A_BulletAttack (AActor *self)
-{
-	int i;
-	int bangle;
-	int slope;
-		
-	if (!self->target)
-		return;
+		pal1=strtol(range, &range, 10);
+		if (!Check(range, ':')) return;
+		pal2=strtol(range, &range, 10);
 
-	A_FaceTarget (self);
-	bangle = self->angle;
+		if (start > end)
+		{
+			swap (start, end);
+			swap (pal1, pal2);
+		}
+		else if (start == end)
+		{
+			translation[start] = pal1;
+			return;
+		}
+		palcol = pal1 << FRACBITS;
+		palstep = ((pal2 << FRACBITS) - palcol) / (end - start);
+		for (int i = start; i <= end; palcol += palstep, ++i)
+		{
+			translation[i] = palcol >> FRACBITS;
+		}
+	}
+	else
+	{ 
+		// translation using RGB values
+		int r1;
+		int g1;
+		int b1;
+		int r2;
+		int g2;
+		int b2;
+		int r,g,b;
+		int rs,gs,bs;
 
-	slope = P_AimLineAttack (self, bangle, MISSILERANGE);
+		r1=strtol(range, &range, 10);
+		if (!Check(range, ',')) return;
+		g1=strtol(range, &range, 10);
+		if (!Check(range, ',')) return;
+		b1=strtol(range, &range, 10);
+		if (!Check(range, ']')) return;
+		if (!Check(range, ':')) return;
+		if (!Check(range, '[')) return;
+		r2=strtol(range, &range, 10);
+		if (!Check(range, ',')) return;
+		g2=strtol(range, &range, 10);
+		if (!Check(range, ',')) return;
+		b2=strtol(range, &range, 10);
+		if (!Check(range, ']')) return;
 
-	S_SoundID (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM);
-	for (i=0 ; i<self->damage ; i++)
-    {
-		int angle = bangle + (pr_cabullet.Random2() << 20);
-		int damage = ((pr_cabullet()%5)+1)*3;
-		P_LineAttack(self, angle, MISSILERANGE, slope, damage,
-			GetDefaultByType(RUNTIME_CLASS(ABulletPuff))->DamageType, RUNTIME_CLASS(ABulletPuff));
-    }
-}
+		if (start > end)
+		{
+			swap (start, end);
+			r = r2;
+			g = g2;
+			b = b2;
+			rs = r1 - r2;
+			gs = g1 - g2;
+			bs = b1 - b2;
+		}
+		else
+		{
+			r = r1;
+			g = g1;
+			b = b1;
+			rs = r2 - r1;
+			gs = g2 - g1;
+			bs = b2 - b1;
+		}
+		if (start == end)
+		{
+			translation[start] = ColorMatcher.Pick
+				(r >> FRACBITS, g >> FRACBITS, b >> FRACBITS);
+			return;
+		}
+		rs /= (end - start);
+		gs /= (end - start);
+		bs /= (end - start);
+		for (int i = start; i <= end; ++i)
+		{
+			translation[i] = ColorMatcher.Pick
+				(r >> FRACBITS, g >> FRACBITS, b >> FRACBITS);
+			r += rs;
+			g += gs;
+			b += bs;
+		}
 
-//==========================================================================
-//
-// State jump function
-//
-//==========================================================================
-void A_Jump(AActor * self)
-{
-	if (pr_cajump() < self->state->GetMisc2())
-		self->SetState (self->state + self->state->GetMisc1());
+	}
 }
 
-//==========================================================================
-//
-// State jump function
-//
-//==========================================================================
-void A_JumpIfHealthLower(AActor * self)
+static int StoreTranslation(const unsigned char * translation)
 {
-	int index=self->state->GetMisc1_2();
-	FJump * jmp=&JumpList[index];
-
-	if (self->health<jmp->compare)
-		self->SetState(self->state+jmp->offset);
+	for (int i = 0; i < NumUsedTranslations; i++)
+	{
+		if (!memcmp(translation, decorate_translations + i*256, 256))
+		{
+			// A duplicate of this translation already exists
+			return TRANSLATION(TRANSLATION_Decorate, i);
+		}
+	}
+	if (NumUsedTranslations>=MAX_DECORATE_TRANSLATIONS)
+	{
+		SC_ScriptError("Too many translations in DECORATE");
+	}
+	memcpy(decorate_translations + NumUsedTranslations*256, translation, 256);
+	NumUsedTranslations++;
+	return TRANSLATION(TRANSLATION_Decorate, NumUsedTranslations-1);
 }
 
-//==========================================================================
-//
-// State jump function
-//
-//==========================================================================
-void A_JumpIfCloser(AActor * self)
+static int CreateBloodTranslation(PalEntry color)
 {
-	if (!self->target) return;
+	int i;
 
-	int index=self->state->GetMisc1_2();
-	FJump * jmp=&JumpList[index];
+	for (i = 0; i < NumUsedBloodTranslations; i++)
+	{
+		if (color.r == BloodTranslations[i].r &&
+			color.g == BloodTranslations[i].g &&
+			color.b == BloodTranslations[i].b)
+		{
+			// A duplicate of this translation already exists
+			return i;
+		}
+	}
+	if (NumUsedBloodTranslations>=MAX_DECORATE_TRANSLATIONS)
+	{
+		SC_ScriptError("Too many blood colors in DECORATE");
+	}
+	for (i = 0; i < 256; i++)
+	{
+		int bright = MAX(MAX(GPalette.BaseColors[i].r, GPalette.BaseColors[i].g), GPalette.BaseColors[i].b);
+		int entry = ColorMatcher.Pick(color.r*bright/255, color.g*bright/255, color.b*bright/255);
 
-	if (P_AproxDistance(self->x-self->target->x, self->y-self->target->y)<jmp->compare<<FRACBITS)
-		self->SetState(self->state+jmp->offset);
+		*(decorate_translations + MAX_DECORATE_TRANSLATIONS*256 + NumUsedBloodTranslations*256 + i)=entry;
+	}
+	BloodTranslations[NumUsedBloodTranslations]=color;
+	return NumUsedBloodTranslations++;
 }
 
-//==========================================================================
+//----------------------------------------------------------------------------
 //
-// Parameterized version of A_Explode
+// DropItem handling
 //
-//==========================================================================
+//----------------------------------------------------------------------------
 
-void A_ExplodeParms (AActor *self)
+enum
 {
-	int damage = 128;
-	int distance = 128;
-	bool hurtSource = true;
-	size_t index = self->state->GetMisc1_2();
+	ACMETA_BASE				= 0x83000,
+	ACMETA_DropItems,		// Int (index into DropItemList)
+};
 
-	if (index < ExplodeList.Size())
-	{
-		FExplodeParms *parms = &ExplodeList[index];
 
-		if (parms->ExplosionDamage != 0)
-		{
-			damage = parms->ExplosionDamage;
-		}
-		if (parms->ExplosionRadius != 0)
-		{
-			distance = parms->ExplosionRadius;
-		}
-		hurtSource = parms->HurtShooter;
-	}
-
-	P_RadiusAttack (self, self->target, damage, distance, self->DamageType, hurtSource);
-	if (self->z <= self->floorz + (distance<<FRACBITS))
-	{
-		P_HitFloor (self);
-	}
-}
+struct FDropItem 
+{
+	const char * Name;
+	int probability;
+	int amount;
+	FDropItem * Next;
+};
+TArray<FDropItem *> DropItemList;
 
 //----------------------------------------------------------------------------
 //
@@ -697,480 +915,78 @@
 
 //==========================================================================
 //
-// Execute a line special / script
-//
-//==========================================================================
-void A_CallSpecial(AActor * self)
-{
-	int index=self->state->GetMisc1_2();
-	FLnSpec * lspc=&LineSpecialList[index];
-
-	LineSpecials[lspc->special](NULL, self, false, lspc->args[0], lspc->args[1], 
-								lspc->args[2], lspc->args[3], lspc->args[4]);
-}
-
-//==========================================================================
-//
-// The ultimate code pointer: Fully customizable missiles!
-//
-//==========================================================================
-void A_CustomMissile(AActor * self)
-{
-	size_t index = self->state->GetMisc1_2();
-	AActor * targ;
-	AActor * missile;
-
-	if (self->target != NULL && index>=0 && index<AttackList.Size())
-	{
-		FMissileAttack * att=&AttackList[index];
-
-		const TypeInfo * ti=TypeInfo::FindType(att->MissileName);
-		if (ti) 
-		{
-			angle_t ang = (self->angle - ANGLE_90) >> ANGLETOFINESHIFT;
-			fixed_t x = att->Spawnofs_XY * finecosine[ang];
-			fixed_t y = att->Spawnofs_XY * finesine[ang];
-			fixed_t z = att->SpawnHeight-32*FRACUNIT;
-
-			if (!att->aimparallel)
-			{
-				// same adjustment as above (in all 3 directions this time) - for better aiming!
-				self->x+=x;
-				self->y+=y;
-				self->z+=z;
-				missile = P_SpawnMissile(self, self->target, ti);
-				self->x-=x;
-				self->y-=y;
-				self->z-=z;
-			}
-			else
-			{
-				missile = P_SpawnMissileXYZ(self->x+x, self->y+y, self->z+att->SpawnHeight, self, self->target, ti);
-			}
-
-			if (missile)
-			{
-				missile->angle += att->Angle;
-				ang = missile->angle >> ANGLETOFINESHIFT;
-				missile->momx = FixedMul (missile->Speed, finecosine[ang]);
-				missile->momy = FixedMul (missile->Speed, finesine[ang]);
-	
-				// handle projectile shooting projectiles - track the
-				// links back to a real owner
-                if (self->flags&MF_MISSILE)
-                {
-                	AActor * owner=self->target;
-                	while (owner->flags&MF_MISSILE && owner->target) owner=owner->target;
-                	targ=owner;
-                	missile->target=owner;
-					// automatic handling of seeker missiles
-					if (self->flags & missile->flags2 & MF2_SEEKERMISSILE)
-					{
-						missile->tracer=self->tracer;
-					}
-                }
-				else if (missile->flags2&MF2_SEEKERMISSILE)
-				{
-					// automatic handling of seeker missiles
-					missile->tracer=self->target;
-				}
-			}
-		}
-	}
-}
-
-//==========================================================================
-//
-// An even more customizable hitscan attack
-//
-//==========================================================================
-void A_CustomBulletAttack (AActor *self)
-{
-	int i;
-	int bangle;
-	int bslope;
-	const TypeInfo *pufftype;
-
-	size_t index=self->state->GetMisc1_2();
-
-	if (self->target && index>=0 && index<BulletAttackList.Size())
-	{
-		FBulletAttack * att=&BulletAttackList[index];
-
-		if (!self->target) return;
-
-		A_FaceTarget (self);
-		bangle = self->angle;
-
-		pufftype = TypeInfo::FindType(att->PuffType);
-		if (!pufftype) pufftype=RUNTIME_CLASS(ABulletPuff);
-
-		bslope = P_AimLineAttack (self, bangle, MISSILERANGE);
-
-		S_SoundID (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM);
-		for (i=0 ; i<att->NumBullets ; i++)
-		{
-			int angle = bangle + pr_cabullet.Random2() * att->Spread_XY * (ANGLE_1/255);
-			int slope = bslope + pr_cabullet.Random2() * att->Spread_Z * (ANGLE_1/255);
-			int damage = ((pr_cabullet()%3)+1)*att->DamagePerBullet;
-			P_LineAttack(self, angle, MISSILERANGE, slope, damage, GetDefaultByType(pufftype)->DamageType, pufftype);
-		}
-    }
-}
-
-//==========================================================================
-//
-// Weapon functions
-//
-//==========================================================================
-void A_WeaponPlaySound(AActor * self)
-{
-	/*
-	unsigned index=psp->state->GetMisc1_2();
-	if (index>=WeaponParams.Size()) return;
-
-	FWeaponSingleParam * p=static_cast<FWeaponSingleParam *>(WeaponParams[index]);
-	S_SoundID (self, CHAN_WEAPON, p->param, 1, ATTN_NORM);
-	*/
-}
-
-//==========================================================================
-//
-// A_FireProjectile
-//
-//==========================================================================
-void A_FireProjectile (AActor *actor)
-{
-	/*
-	player_t *player;
-	ACustomWeapon *info;
-
-	if (NULL == (player = actor->player))
-	{
-		return;
-	}
-	int index=!!(player->powers[pw_weaponlevel2] && !deathmatch);
-
-	player->UseAmmo (true);
-
-	info = (ACustomWeapon*)(wpnlev1info[player->readyweapon]->type->ActorInfo->Defaults);
-
-	if (info->w[index].missiletype)
-	{
-		const TypeInfo * ti=TypeInfo::FindType(info->w[index].missiletype);
-		if (ti) 
-		{
-			AActor * misl=P_SpawnPlayerMissile (actor, ti);
-			// automatic handling of seeker missiles
-			if (misl && linetarget && misl->flags2&MF2_SEEKERMISSILE) misl->tracer=linetarget;
-		}
-	}
-	*/
-}
-
-//==========================================================================
-//
-// State jump function
+// Extra info maintained while defining an actor. The original
+// implementation stored these in a CustomActor class. They have all been
+// moved into action function parameters so that no special CustomActor
+// class is necessary.
 //
 //==========================================================================
-void A_WeaponJump(AActor * self)
-{
-	/*
-	player_t *player;
-
-	if (NULL == (player = self->player))
-	{
-		return;
-	}
-
-	unsigned index=psp->state->GetMisc1_2();
-	if (index>=WeaponParams.Size()) return;
-
-	FWeaponJump * p=static_cast<FWeaponJump *>(WeaponParams[index]);
-
-	if (pr_cwjump()<p->compare)
-		P_SetPsprite(player, psp-player->psprites, psp->state + p->offset);
-	*/
-}
 
-
-//==========================================================================
-//
-// State jump function
-//
-//==========================================================================
-void A_WeaponJumpIfCloser(AActor * self)
+struct FExplodeParms
 {
-	/*
-	player_t *player;
-
-	if (NULL == (player = self->player))
-	{
-		return;
-	}
-
-	P_BulletSlope(self);
-	if (!linetarget) return;
-
-	unsigned index=psp->state->GetMisc1_2();
-	if (index>=WeaponParams.Size()) return;
-	FWeaponJump * jmp=static_cast<FWeaponJump *>(WeaponParams[index]);
-
-	if (P_AproxDistance(self->x-linetarget->x, self->y-linetarget->y)<jmp->compare<<FRACBITS)
-		P_SetPsprite(player, psp-player->psprites, psp->state + jmp->offset);
-	*/
-}
+	bool HurtShooter;
+	int ExplosionRadius, ExplosionDamage;
+};
 
-//==========================================================================
-//
-// State jump function
-//
-//==========================================================================
-void A_WeaponJumpIfNoAmmo(AActor * self)
+struct FBasicAttack
 {
-	/*
-	player_t *player;
-
-	if (NULL == (player = self->player))
-	{
-		return;
-	}
-
-	unsigned index=psp->state->GetMisc1_2();
-	if (index>=WeaponParams.Size()) return;
-
-	FWeaponSingleParam * p=static_cast<FWeaponSingleParam *>(WeaponParams[index]);
-
-
-	if (!CheckAmmo(player))
-		P_SetPsprite(player, psp-player->psprites, psp->state + p->param);
-	*/
-}
-
+	int MeleeDamage;
+	int MeleeSound;
+	const char *MissileName;
+	fixed_t MissileHeight;
+};
 
-//==========================================================================
-//
-// An even more customizable hitscan attack
-//
-//==========================================================================
-void A_FireBullets (AActor *self)
+struct Baggage
 {
-	/*
-	int i;
-	int bangle;
-	int bslope;
-
-	player_t *player;
-	ACustomWeapon * info;
-	FWeaponInfo ** wpinfo;
-	ACustomWeapon::CWInfo * cwi;
-
-
-	if (NULL == (player = self->player))
-	{
-		return;
-	}
-	info = (ACustomWeapon*)(wpnlev1info[player->readyweapon]->type->ActorInfo->Defaults);
-
-	if (player->powers[pw_weaponlevel2] && !deathmatch)
-	{
-		wpinfo = wpnlev2info;
-		cwi=&info->w[1];
-	}
-	else
-	{
-		wpinfo = wpnlev1info;
-		cwi=&info->w[0];
-	}
-
-
-	unsigned index=psp->state->GetMisc1_2();
-	if (index>=WeaponParams.Size()) return;
-
-	FWeaponBulletAttack * att=static_cast<FWeaponBulletAttack *>(WeaponParams[index]);
-
-	S_SoundID (self, CHAN_WEAPON, cwi->attacksound, 1, ATTN_NORM);
-
-	player->UseAmmo (true);
-
-	static_cast<APlayerPawn *>(self)->PlayAttacking2 ();
-	P_BulletSlope(self);
+	FActorInfo *Info;
+	bool DropItemSet;
+	bool StateSet;
+	int CurrentState;
 
-	bangle = self->angle;
-	bslope = bulletpitch;
+	FExplodeParms EParms;
+	FDropItem *DropItemList;
+	FBasicAttack BAttack;
 
-	PuffType=NULL;
-	if (cwi->pufftype) PuffType = TypeInfo::FindType(cwi->pufftype);
-	if (!PuffType) PuffType=RUNTIME_CLASS(ABulletPuff);
+	const char *PuffType;
+	const char *HitPuffType;
+	int AttackSound;
+	int AmmoGive;
 
-	HitPuffType=NULL;
-	if (cwi->hitpufftype) HitPuffType = TypeInfo::FindType(cwi->hitpufftype);
+	int WeapNum;
 
-	if (att->NumBullets==1 && !player->refire)
-	{
-		int damage = ((pr_cwbullet()%3)+1)*att->DamagePerBullet;
-		P_LineAttack(self, bangle, MISSILERANGE, bslope, damage);
-	}
-	else for (i=0 ; i<att->NumBullets ; i++)
-	{
-		int angle = bangle + pr_cwbullet.Random2() * att->Spread_XY * (ANGLE_1/255);
-		int slope = bslope + pr_cwbullet.Random2() * att->Spread_Z * (ANGLE_1/255);
-		int damage = ((pr_cwbullet()%3)+1)*att->DamagePerBullet;
-		P_LineAttack(self, angle, MISSILERANGE, slope, damage);
-	}
-	HitPuffType=NULL;
-	*/
-}
+};
 
 
 //==========================================================================
 //
-// A_FireProjectile
 //
 //==========================================================================
-void A_FireCustomMissile (AActor *actor)
-{
-	/*
-	player_t *player;
-	ACustomWeapon *info;
-
-	if (NULL == (player = actor->player))
-	{
-		return;
-	}
-
-	unsigned index=psp->state->GetMisc1_2();
-	if (index>=WeaponParams.Size()) return;
-	FWeaponMissileAttack * att=static_cast<FWeaponMissileAttack *>(WeaponParams[index]);
 
-	if (!att->dontuseammo) player->UseAmmo (true);
-
-	info = (ACustomWeapon*)(wpnlev1info[player->readyweapon]->type->ActorInfo->Defaults);
-
-	const TypeInfo * ti=TypeInfo::FindType(att->MissileName);
-	if (ti) 
-	{
-		angle_t ang = (actor->angle - ANGLE_90) >> ANGLETOFINESHIFT;
-		fixed_t x = att->Spawnofs_XY * finecosine[ang];
-		fixed_t y = att->Spawnofs_XY * finesine[ang];
-		fixed_t z = att->SpawnHeight;
-
-		AActor * misl=P_SpawnPlayerMissile (actor, actor->x+x, actor->y+y, actor->z+z, ti, actor->angle);
-		// automatic handling of seeker missiles
-		if (misl)
-		{
-			if (linetarget && misl->flags2&MF2_SEEKERMISSILE) misl->tracer=linetarget;
-			misl->angle += att->Angle;
-			angle_t an = misl->angle >> ANGLETOFINESHIFT;
-			misl->momx = FixedMul (misl->Speed, finecosine[an]);
-			misl->momy = FixedMul (misl->Speed, finesine[an]);
-		}
-	}
-	*/
-}
-
-
-//==========================================================================
-//
-// A_Punch
-//
-//==========================================================================
-void A_CustomPunch (AActor *actor)
+static TArray<FState> StateArray;
+static int StateRangeEnd[3];
+static int StateRangeType[3];
+enum
 {
-	/*
-	player_t * player;
-	angle_t 	angle;
-	int 		damage;
-	int 		pitch;
-	FWeaponInfo ** wpinfo;
-	ACustomWeapon::CWInfo * cwi;
-	ACustomWeapon * info;
-
-
-	unsigned index=psp->state->GetMisc1_2();
-	if (index>=WeaponParams.Size()) return;
-
-	if (NULL == (player = actor->player))
-	{
-		return;
-	}
-
-	FWeaponSingleParam * p=static_cast<FWeaponSingleParam *>(WeaponParams[index]);
-
-	info = (ACustomWeapon*)(wpnlev1info[player->readyweapon]->type->ActorInfo->Defaults);
-	if (player->powers[pw_weaponlevel2] && !deathmatch)
-	{
-		wpinfo = wpnlev2info;
-		cwi=&info->w[1];
-	}
-	else
-	{
-		wpinfo = wpnlev1info;
-		cwi=&info->w[0];
-	}
-
-	damage = p->param * (pr_cwpunch()%8+1);
-
-	if (player->powers[pw_strength])	damage *= 10;
-
-	angle = actor->angle + (pr_cwpunch.Random2() << 18);
-	pitch = P_AimLineAttack (actor, angle, MELEERANGE);
+	SRT_Actor,
+	SRT_Weapon,	// Is this really necessary? I don't think so.
+};
+int NumRanges;
 
-	// only use ammo when actually hitting something!
-	if (linetarget)	player->UseAmmo (true);
 
-	PuffType=NULL;
-	if (cwi->pufftype) PuffType = TypeInfo::FindType(cwi->pufftype);
-	if (!PuffType) PuffType=RUNTIME_CLASS(ABulletPuff);
 
-	HitPuffType=NULL;
-	if (cwi->hitpufftype) HitPuffType = TypeInfo::FindType(cwi->hitpufftype);
+typedef void (*ActorPropFunction) (AActor *defaults, Baggage &bag);
 
-	P_LineAttack (actor, angle, MELEERANGE, pitch, damage);
+struct ActorProps { const char *name; ActorPropFunction Handler; const TypeInfo * type; };
 
-	HitPuffType=NULL;
+typedef ActorProps (*ActorPropHandler) (register const char *str, register unsigned int len);
 
-	// turn to face target
-	if (linetarget)
-	{
-		ACustomWeapon * info = (ACustomWeapon*)(wpinfo[player->readyweapon]->type->ActorInfo->Defaults);
-		S_SoundID (actor, CHAN_WEAPON, cwi->attacksound, 1, ATTN_NORM);
+static const ActorProps *is_actorprop (const char *str);
 
-		actor->angle = R_PointToAngle2 (actor->x,
-										actor->y,
-										linetarget->x,
-										linetarget->y);
-	}
-	*/
-}
+int ParseExpression ();
 
 
 //==========================================================================
 //
-// customizable railgun attack function
-//
-//==========================================================================
-void A_RailAttack (AActor *actor)
-{
-	/*
-	player_t *player;
-
-	unsigned index=psp->state->GetMisc1_2();
-	if (index>=WeaponParams.Size()) return;
-
-	if (NULL == (player = actor->player))
-	{
-		return;
-	}
-
-	FRailAttack * p=static_cast<FRailAttack *>(WeaponParams[index]);
-	player->UseAmmo ();
-	P_RailAttack (actor, p->damage, p->Spawnofs_XY);
-	*/
-}
-
-//==========================================================================
-//
 // SC_CheckNumber
 // similar to SC_GetNumber but ungets the token if it isn't a number 
 // and does not print an error
@@ -1206,6 +1022,34 @@
 	}
 }
 
+//==========================================================================
+//
+// SC_CheckFloat
+// [GRB] Same as SC_CheckNumber, only for floats
+//
+//==========================================================================
+
+BOOL SC_CheckFloat (void)
+{
+	char *stopper;
+
+	//CheckOpen ();
+	if (SC_GetString())
+	{
+		sc_Float = strtod (sc_String, &stopper);
+		if (*stopper != 0)
+		{
+			SC_UnGet();
+			return false;
+		}
+		return true;
+	}
+	else
+	{
+		return false;
+	}
+}
+
 
 //==========================================================================
 //
@@ -1240,24 +1084,26 @@
 //==========================================================================
 
 // These strings must be in the same order as the respective variables in the actor struct!
-static const char * mobj_statenames[]={"SPAWN","SEE","PAIN","MELEE","MISSILE","CRASH",
-							"DEATH","XDEATH", "BURN","ICE","DISINTEGRATE","RAISE","WOUND",
-							NULL};
+static const char * actor_statenames[]={"SPAWN","SEE","PAIN","MELEE","MISSILE","CRASH", "DEATH",
+										"XDEATH", "BURN","ICE","DISINTEGRATE","RAISE","WOUND","HEAL",
+										"CRUSH", "YES", "NO", "GREETINGS", NULL};
 
-static const char * weapon_statenames[]={"up","down","ready","attack","holdattack",
-	"altattack","altholdattack","flash",NULL};
+static const char * weapon_statenames[]={"SELECT", "DESELECT", "READY", "FIRE", "HOLD",
+										 "ALTFIRE", "ALTHOLD", "FLASH", NULL };
+
+static const char * inventory_statenames[]={"USE", "PICKUP", "DROP", NULL };
 
 
-FState ** FindState(AActor * actor, const char * name)
+FState ** FindState(AActor * actor, const TypeInfo * type, const char * name)
 {
 	int i;
 
-	for(i=0;mobj_statenames[i];i++)
+	for(i=0;actor_statenames[i];i++)
 	{
-		if (!stricmp(mobj_statenames[i],name))
+		if (!stricmp(actor_statenames[i],name))
 			return (&actor->SpawnState)+i;
 	}
-	if (actor->IsKindOf (RUNTIME_CLASS(AWeapon)))
+	if (type->IsDescendantOf (RUNTIME_CLASS(AWeapon)))
 	{
 		for(i=0;weapon_statenames[i];i++)
 		{
@@ -1265,6 +1111,14 @@
 				return (&static_cast<AWeapon*>(actor)->UpState)+i;
 		}
 	}
+	if (type->IsDescendantOf (RUNTIME_CLASS(AInventory)))
+	{
+		for(i=0;inventory_statenames[i];i++)
+		{
+			if (!stricmp(inventory_statenames[i],name))
+				return (&static_cast<AInventory*>(actor)->UseState)+i;
+		}
+	}
 	return NULL;
 }
 
@@ -1359,45 +1213,88 @@
 		if (sc_Number>=-1 && sc_Number<32768) info->DoomEdNum=sc_Number;
 		else SC_ScriptError ("DoomEdNum must be in the range [-1,32767]");
 	}
+	if (ti->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
+	{
+		// preinitialize kickback to the default for the game
+		((AWeapon*)(info->Defaults))->Kickback=gameinfo.defKickback;
+	}
 
 	return info;
 }
 
 //==========================================================================
 //
-// State processing
+// PrepareStateParameters
+// creates an empty parameter list for a parameterized function call
 //
 //==========================================================================
+int PrepareStateParameters(FState * state, int numparams)
+{
+	int paramindex=StateParameters.Size();
+	int i, v;
+
+	if (state->Frame&SF_BIGTIC)
+	{
+		SC_ScriptError("You cannot use a parameterized code pointer with a state duration larger than 254!");
+	}
 
+	v=state->Misc1;
+	StateParameters.Push(v);
+	v=state->Misc2;
+	StateParameters.Push(v);
+	v=0;
+	for(i=0;i<numparams;i++) StateParameters.Push(v);
+	state->SetMisc1_2(paramindex);
+	state->Frame|=SF_STATEPARAM;
+
+	return paramindex+2;	// return the index of the first actual state parameter
+}
+
+//==========================================================================
+//
+// Returns the index of the given line special
+//
+//==========================================================================
+int FindLineSpecial(const char * string)
+{
+	const ACSspecials *spec;
+
+	spec = is_special(string, strlen(string));
+	if (spec) return spec->Special;
+	return 0;
+}
+
+//==========================================================================
+//
+// DoSpecialFunctions
+// handles special functions that can't be dealth with by the generic routine
+//
+//==========================================================================
 bool DoSpecialFunctions(FState & state, bool multistate, int * statecount, Baggage &bag)
 {
 	int i;
 	const ACSspecials *spec;
 
-	// These functions are special because they require parameters
-
 	if ((spec = is_special (sc_String, sc_StringLen)) != NULL)
 	{
-		FLnSpec ls = { spec->Special };
 
-		if (state.Frame&SF_BIGTIC)
-		{
-			SC_ScriptError("You cannot use a special with a state duration larger than 254!");
-		}
-		SC_MustGetStringName("(");
-		for (i = 0; i < 5; )
+		int paramindex=PrepareStateParameters(&state, 6);
+
+		StateParameters[paramindex]=spec->Special;
+
+		// Make this consistent with all other parameter parsing
+		if (SC_CheckString("("))
 		{
-			if (SC_CheckNumber ())
-			{
-				ls.args[i++] = sc_Number;
-			}
-			else if (!TestCom ())
+			for (i = 0; i < 5;)
 			{
-				SC_UnGet ();
-				break;
+				StateParameters[paramindex+i+1]=ParseExpression ();
+				i++;
+				if (!TestCom()) break;
 			}
+			SC_MustGetStringName (")");
 		}
-		SC_MustGetStringName (")");
+		else i=0;
+
 		if (i < spec->MinArgs)
 		{
 			SC_ScriptError ("Too few arguments to %s", spec->name);
@@ -1406,7 +1303,6 @@
 		{
 			SC_ScriptError ("Too many arguments to %s", spec->name);
 		}
-		state.SetMisc1_2 (LineSpecialList.Push(ls));
 		state.Action = A_CallSpecial;
 		return true;
 	}
@@ -1414,30 +1310,28 @@
 	// Override the normal A_Explode with a parameterized version
 	if (SC_Compare ("A_Explode"))
 	{
+		int paramindex=PrepareStateParameters(&state, 3);
+
 		FExplodeParms local_eparms = bag.EParms;
 
-		if (state.Frame & SF_BIGTIC)
-		{
-			SC_ScriptError("You cannot use A_Explode with a state duration larger than 254!");
-		}
-		SC_MustGetString ();
-		if (SC_Compare ("("))	// Parameters are optional
+		if (SC_CheckString ("("))	// Parameters are optional
 		{
 			SC_MustGetNumber ();
 			local_eparms.ExplosionDamage = sc_Number;
 			SC_MustGetStringName (",");
 			SC_MustGetNumber ();
 			local_eparms.ExplosionRadius = sc_Number;
-			SC_MustGetStringName (",");
-			SC_MustGetNumber ();
-			local_eparms.HurtShooter = !!sc_Number;
+			if (SC_CheckString(","))
+			{
+				SC_MustGetNumber ();
+				local_eparms.HurtShooter = !!sc_Number;
+			}
 			SC_MustGetStringName (")");
 		}
-		else
-		{
-			SC_UnGet ();
-		}
-		state.SetMisc1_2 (ExplodeList.Push (local_eparms));
+
+		StateParameters[paramindex] = local_eparms.ExplosionDamage;
+		StateParameters[paramindex+1] = local_eparms.ExplosionRadius;
+		StateParameters[paramindex+2] = local_eparms.HurtShooter;
 		state.Action = A_ExplodeParms;
 		return true;
 	}
@@ -1446,471 +1340,25 @@
 	int batk = SC_MatchString (BasicAttackNames);
 	if (batk >= 0)
 	{
-		if (state.Frame & SF_BIGTIC)
-		{
-			SC_ScriptError ("You cannot use %s with a state duration larger than 254!", sc_String);
-		}
-		state.SetMisc1_2 (BasicAttackList.Push (bag.BAttack));
-		state.Action = BasicAttacks[batk];
-		return true;
-	}
-
-	if (SC_Compare("A_PlaySound"))
-	{
-		if (state.Frame&SF_BIGTIC)
-		{
-			SC_ScriptError("You cannot use A_PlaySound with a state duration larger than 254!");
-		}
-		SC_MustGetStringName ("(");
-		SC_MustGetString();
-		state.SetMisc1_2 (S_FindSound(sc_String));
-		SC_MustGetStringName (")");
-		state.Action = A_PlaySound;
-		return true;
-	}
-	if (SC_Compare("A_PlayWeaponSound"))
-	{
-		if (state.Frame&SF_BIGTIC)
-		{
-			SC_ScriptError("You cannot use A_PlayWeaponSound with a state duration larger than 254!");
-		}
-		SC_MustGetStringName ("(");
-		SC_MustGetString();
-		state.SetMisc1_2 (S_FindSound(sc_String));
-		SC_MustGetStringName (")");
-		state.Action = A_PlayWeaponSound;
-		return true;
-	}
-	if (SC_Compare("A_SeekerMissile"))
-	{
-		if (state.Frame&SF_BIGTIC)
-		{
-			SC_ScriptError("You cannot use A_SeekerMissile with a state duration larger than 254!");
-		}
-		SC_MustGetStringName ("(");
-		SC_MustGetNumber();
-		if (sc_Number<0 || sc_Number>90)
-		{
-			SC_ScriptError("A_SeekerMissile parameters must be in the range [0..90]");
-		}
-		state.Misc1=sc_Number;
-		SC_MustGetStringName (",");
-		SC_MustGetNumber();
-		if (sc_Number<0 || sc_Number>90)
-		{
-			SC_ScriptError("A_SeekerMissile parameters must be in the range [0..90]");
-		}
-		state.Misc2=sc_Number;
-		SC_MustGetStringName (")");
-		state.Action = A_SeekerMissile;
-		return true;
-	}
-	if (SC_Compare("A_Jump"))
-	{
-		if (multistate)
-		{
-			SC_ScriptError("A_Jump may not be used on a multiple state definition!");
-		}
-		if (state.Frame&SF_BIGTIC)
-		{
-			SC_ScriptError("You cannot use A_Jump with a state duration larger than 254!");
-		}
-		SC_MustGetStringName("(");
-		SC_MustGetNumber();
-		state.Misc2=sc_Number;
-		if (sc_Number<0 || sc_Number>255)
-		{
-			SC_ScriptError("The first parameter of A_Jump must be in the range [0..255]");
-		}
-		SC_MustGetStringName (",");
-		SC_MustGetNumber();
-		if (sc_Number<=0)
-		{
-			SC_ScriptError("The second parameter of A_Jump must be greater than 0");
-		}
-		state.Misc1=sc_Number;
-		SC_MustGetStringName (")");
-		*statecount+=state.Misc1;
-		state.Action = A_Jump;
-		return true;
-	}
-	if (SC_Compare("A_CustomMissile"))
-	{
-		FMissileAttack att;
-
-		if (state.Frame&SF_BIGTIC)
-		{
-			SC_ScriptError("You cannot use A_CustomMissile with a state duration larger than 254!");
-		}
-		SC_MustGetStringName("(");
-		SC_MustGetString();
-		att.MissileName=strdup(sc_String);
-		SC_MustGetStringName (",");
-		SC_MustGetNumber();
-		att.SpawnHeight=sc_Number*FRACUNIT;
-		SC_MustGetStringName (",");
-		SC_MustGetNumber();
-		att.Spawnofs_XY=sc_Number;
-		SC_MustGetStringName (",");
-		SC_MustGetNumber();
-		att.Angle=sc_Number*ANGLE_1;
-
-		if (TestCom()) 
-		{
-			SC_MustGetNumber();
-			att.aimparallel=!!sc_Number;
-		}
-		else att.aimparallel=false;
-
-
-		SC_MustGetStringName (")");
-
-		state.SetMisc1_2 (AttackList.Push(att));
-		state.Action = A_CustomMissile;
-
-		return true;
-	}
-	if (SC_Compare("A_CustomBulletAttack"))
-	{
-		FBulletAttack att;
-
-		if (state.Frame&SF_BIGTIC)
-		{
-			SC_ScriptError("You cannot use A_CustomBulletAttack with a state duration larger than 254!");
-		}
-		SC_MustGetStringName("(");
-		SC_MustGetNumber();
-		att.Spread_XY=sc_Number;
-		SC_MustGetStringName (",");
-		SC_MustGetNumber();
-		att.Spread_Z=sc_Number;
-		SC_MustGetStringName (",");
-		SC_MustGetNumber();
-		att.NumBullets=sc_Number;
-		SC_MustGetStringName (",");
-		SC_MustGetNumber();
-		att.DamagePerBullet=sc_Number;
-		if (TestCom())
-		{
-			SC_MustGetString();
-			att.PuffType=strdup(sc_String);
-		}
-		else att.PuffType="BulletPuff";
-		SC_MustGetStringName (")");
-
-		state.SetMisc1_2 (BulletAttackList.Push(att));
-		state.Action = A_CustomBulletAttack;
-		return true;
-	}
-	if (SC_Compare("A_JumpIfHealthLower"))
-	{
-		FJump jmp;
-
-		if (multistate)
-		{
-			SC_ScriptError("A_JumpIfHealthLower may not be used on a multiple state definition!");
-		}
-		if (state.Frame&SF_BIGTIC)
-		{
-			SC_ScriptError("You cannot use A_JumpIfHealthLower with a state duration larger than 254!");
-		}
-		SC_MustGetStringName("(");
-		SC_MustGetNumber();
-		jmp.compare=sc_Number;
-		SC_MustGetStringName (",");
-		SC_MustGetNumber();
-		if (sc_Number<=0)
-		{
-			SC_ScriptError("The second parameter of A_JumpIfHealthLower must be greater than 0");
-		}
-		jmp.offset=sc_Number;
-		SC_MustGetStringName (")");
-		*statecount+=jmp.offset;
-		state.SetMisc1_2 (JumpList.Push(jmp));
-		state.Action = A_JumpIfHealthLower;
-		return true;
-	}
-	if (SC_Compare("A_JumpIfCloser"))
-	{
-		FJump jmp;
+		int paramindex=PrepareStateParameters(&state, 4);
 
-		if (multistate)
-		{
-			SC_ScriptError("A_JumpIfCloser may not be used on a multiple state definition!");
-		}
-		if (state.Frame&SF_BIGTIC)
-		{
-			SC_ScriptError("You cannot use A_JumpIfCloser with a state duration larger than 254!");
-		}
-		SC_MustGetStringName("(");
-		SC_MustGetNumber();
-		jmp.compare=sc_Number;
-		SC_MustGetStringName (",");
-		SC_MustGetNumber();
-		if (sc_Number<=0)
-		{
-			SC_ScriptError("The second parameter of A_JumpIfCloser must be greater than 0");
-		}
-		jmp.offset=sc_Number;
-		SC_MustGetStringName (")");
-		*statecount+=jmp.offset;
-		state.SetMisc1_2 (JumpList.Push(jmp));
-		state.Action = A_JumpIfCloser;
+		StateParameters[paramindex] = bag.BAttack.MeleeDamage;
+		StateParameters[paramindex+1] = bag.BAttack.MeleeSound;
+		StateParameters[paramindex+2] = (int)bag.BAttack.MissileName;
+		StateParameters[paramindex+3] = bag.BAttack.MissileHeight;
+		state.Action = BasicAttacks[batk];
 		return true;
 	}
 	return false;
 }
 
-bool DoSpecialWeaponFunctions(FState & state, bool multistate, int * statecount)
-{
-	// These functions are special because they require a parameter
-	if (SC_Compare("A_PlaySound"))
-	{
-		if (state.Frame&SF_BIGTIC)
-		{
-			SC_ScriptError("You cannot use A_PlaySound with a state duration larger than 254!");
-		}
-		FWeaponSingleParam * parm=new FWeaponSingleParam;
-		parm->wp_xoffset=state.Misc1;
-		parm->wp_yoffset=state.Misc2;
-		state.Frame|=SF_WEAPONPARAM;
-
-		SC_MustGetStringName ("(");
-		SC_MustGetString();
-		parm->param=S_FindSound(sc_String);
-		SC_MustGetStringName (")");
-
-		int v=WeaponParams.Push(parm);
-		state.Misc2=v&255;
-		state.Misc1=v>>8;
-		state.Action = A_WeaponPlaySound;
-		return true;
-	}
-	if (SC_Compare("A_Jump"))
-	{
-		if (multistate)
-		{
-			SC_ScriptError("A_Jump may not be used on a multiple state definition!");
-		}
-		if (state.Frame&SF_BIGTIC)
-		{
-			SC_ScriptError("You cannot use A_Jump with a state duration larger than 254!");
-		}
-		FWeaponJump * parm=new FWeaponJump;
-		parm->wp_xoffset=state.Misc1;
-		parm->wp_yoffset=state.Misc2;
-		state.Frame|=SF_WEAPONPARAM;
-
-		SC_MustGetStringName("(");
-		SC_MustGetNumber();
-		parm->compare=sc_Number;
-		if (sc_Number<0 || sc_Number>255)
-		{
-			SC_ScriptError("The first parameter of A_Jump must be in the range [0..255]");
-		}
-		SC_MustGetStringName (",");
-		SC_MustGetNumber();
-		if (sc_Number<=0)
-		{
-			SC_ScriptError("The second parameter of A_Jump must be greater than 0");
-		}
-		parm->offset=sc_Number;
-		SC_MustGetStringName (")");
-		*statecount+=parm->offset;
-
-		int v=WeaponParams.Push(parm);
-		state.Misc2=v&255;
-		state.Misc1=v>>8;
-		state.Action = A_WeaponJump;
-		return true;
-	}
-	if (SC_Compare("A_JumpIfCloser"))
-	{
-		if (multistate)
-		{
-			SC_ScriptError("A_JumpIfCloser may not be used on a multiple state definition!");
-		}
-		if (state.Frame&SF_BIGTIC)
-		{
-			SC_ScriptError("You cannot use A_JumpIfCloser with a state duration larger than 254!");
-		}
-		FWeaponJump * parm=new FWeaponJump;
-		parm->wp_xoffset=state.Misc1;
-		parm->wp_yoffset=state.Misc2;
-		state.Frame|=SF_WEAPONPARAM;
-
-		SC_MustGetStringName("(");
-		SC_MustGetNumber();
-		parm->compare=sc_Number;
-		SC_MustGetStringName (",");
-		SC_MustGetNumber();
-		if (sc_Number<=0)
-		{
-			SC_ScriptError("The second parameter of A_JumpIfCloser must be greater than 0");
-		}
-		parm->offset=sc_Number;
-		SC_MustGetStringName (")");
-		*statecount+=parm->offset;
-
-		int v=WeaponParams.Push(parm);
-		state.Misc2=v&255;
-		state.Misc1=v>>8;
-		state.Action = A_WeaponJumpIfCloser;
-		return true;
-	}
-	if (SC_Compare("A_JumpIfNoAmmo"))
-	{
-		if (multistate)
-		{
-			SC_ScriptError("A_JumpIfCloser may not be used on a multiple state definition!");
-		}
-		if (state.Frame&SF_BIGTIC)
-		{
-			SC_ScriptError("You cannot use A_JumpIfCloser with a state duration larger than 254!");
-		}
-		FWeaponSingleParam * parm=new FWeaponSingleParam;
-		parm->wp_xoffset=state.Misc1;
-		parm->wp_yoffset=state.Misc2;
-		state.Frame|=SF_WEAPONPARAM;
-
-		SC_MustGetStringName("(");
-		SC_MustGetNumber();
-		parm->param=sc_Number;
-		SC_MustGetStringName (")");
-		*statecount+=parm->param;
 
-		int v=WeaponParams.Push(parm);
-		state.Misc2=v&255;
-		state.Misc1=v>>8;
-		state.Action = A_WeaponJumpIfNoAmmo;
-		return true;
-	}
-	if (SC_Compare("A_CustomPunch"))
-	{
-		if (state.Frame&SF_BIGTIC)
-		{
-			SC_ScriptError("You cannot use A_CustomPunch with a state duration larger than 254!");
-		}
-		FWeaponSingleParam * parm=new FWeaponSingleParam;
-		parm->wp_xoffset=state.Misc1;
-		parm->wp_yoffset=state.Misc2;
-		state.Frame|=SF_WEAPONPARAM;
-
-		SC_MustGetStringName ("(");
-		SC_MustGetNumber();
-		parm->param=sc_Number;
-		SC_MustGetStringName (")");
-
-		int v=WeaponParams.Push(parm);
-		state.Misc2=v&255;
-		state.Misc1=v>>8;
-		state.Action = A_CustomPunch;
-		return true;
-	}
-	if (SC_Compare("A_FireBullets"))
-	{
-		if (state.Frame&SF_BIGTIC)
-		{
-			SC_ScriptError("You cannot use A_FireBullets with a state duration larger than 254!");
-		}
-		FWeaponBulletAttack * att=new FWeaponBulletAttack;
-		att->wp_xoffset=state.Misc1;
-		att->wp_yoffset=state.Misc2;
-		state.Frame|=SF_WEAPONPARAM;
-
-		SC_MustGetStringName("(");
-		SC_MustGetNumber();
-		att->Spread_XY=sc_Number;
-		SC_MustGetStringName (",");
-		SC_MustGetNumber();
-		att->Spread_Z=sc_Number;
-		SC_MustGetStringName (",");
-		SC_MustGetNumber();
-		att->NumBullets=sc_Number;
-		SC_MustGetStringName (",");
-		SC_MustGetNumber();
-		att->DamagePerBullet=sc_Number;
-		SC_MustGetStringName (")");
-
-		int v=WeaponParams.Push(att);
-		state.Misc2=v&255;
-		state.Misc1=v>>8;
-		state.Action = A_FireBullets;
-		return true;
-	}
-	if (SC_Compare("A_FireCustomMissile"))
-	{
-		if (state.Frame&SF_BIGTIC)
-		{
-			SC_ScriptError("You cannot use A_FireCustomMissile with a state duration larger than 254!");
-		}
-		FWeaponMissileAttack * att=new FWeaponMissileAttack;
-		memset(att,0,sizeof(FWeaponMissileAttack));
-		att->wp_xoffset=state.Misc1;
-		att->wp_yoffset=state.Misc2;
-		state.Frame|=SF_WEAPONPARAM;
-
-		SC_MustGetStringName("(");
-		SC_MustGetString();
-		att->MissileName=strdup(sc_String);
-		if (TestCom())
-		{
-			SC_MustGetNumber();
-			att->Angle=sc_Number*ANGLE_1;
-			if (TestCom())
-			{
-				SC_MustGetNumber();
-				att->dontuseammo=!!sc_Number;
-				if (TestCom())
-				{
-					SC_MustGetNumber();
-					att->Spawnofs_XY=sc_Number;
-					if (TestCom())
-					{
-						SC_MustGetNumber();
-						att->SpawnHeight=sc_Number*FRACUNIT;
-					}
-				}
-			}
-		}
-
-		SC_MustGetStringName (")");
-
-		int v=WeaponParams.Push(att);
-		state.Misc2=v&255;
-		state.Misc1=v>>8;
-		state.Action = A_FireCustomMissile;
-		return true;
-	}
-	if (SC_Compare("A_RailAttack"))
-	{
-		if (state.Frame&SF_BIGTIC)
-		{
-			SC_ScriptError("You cannot use A_RailAttack with a state duration larger than 254!");
-		}
-		FRailAttack * att=new FRailAttack;
-		att->wp_xoffset=state.Misc1;
-		att->wp_yoffset=state.Misc2;
-		state.Frame|=SF_WEAPONPARAM;
-
-		SC_MustGetStringName("(");
-		SC_MustGetNumber();
-		att->damage=sc_Number;
-		if (TestCom())
-		{
-			SC_MustGetNumber();
-			att->Spawnofs_XY=sc_Number;
-		}
-		else att->Spawnofs_XY=0;
-		SC_MustGetStringName (")");
-		int v=WeaponParams.Push(att);
-		state.Misc2=v&255;
-		state.Misc1=v>>8;
-		state.Action = A_RailAttack;
-		return true;
-	}
-	return false;
-}
+//==========================================================================
+//
+// ProcessStates
+// processes a state block
+//
+//==========================================================================
 
 static int ProcessStates(FActorInfo * actor, AActor * defaults, Baggage &bag, int rangetype)
 {
@@ -1958,11 +1406,11 @@
 			}
 			laststate->NextState=(FState*)-1;
 		}
-		else if (SC_Compare("WAIT"))
+		else if (SC_Compare("WAIT") || SC_Compare("FAIL"))
 		{
 			if (!laststate) 
 			{
-				SC_ScriptError("WAIT before first state");
+				SC_ScriptError("%s before first state", sc_String);
 				continue;
 			}
 			laststate->NextState=(FState*)-2;
@@ -1986,7 +1434,7 @@
 			while (SC_Compare (":"))
 			{
 				lastlabel = count;
-				stp = FindState(defaults, statestring);
+				stp = FindState(defaults, bag.Info->Class, statestring);
 				if (stp) *stp=(FState *) (count+1);
 				else 
 					SC_ScriptError("Unknown state label %s", statestring);
@@ -2003,6 +1451,7 @@
 			}
 
 			memcpy(state.sprite.name, statestring, 4);
+			state.Misc1=state.Misc2=0;
 			SC_MustGetString();
 			strncpy(statestring, sc_String + 1, 255);
 			statestrp = statestring;
@@ -2026,6 +1475,22 @@
 					state.Frame|=SF_FULLBRIGHT;
 					continue;
 				}
+				if (SC_Compare("OFFSET"))
+				{
+					if (state.Frame&SF_BIGTIC)
+					{
+						SC_ScriptError("You cannot use OFFSET with a state duration larger than 254!");
+					}
+					// specify a weapon offset
+					SC_MustGetStringName("(");
+					SC_MustGetNumber();
+					state.Misc1=sc_Number;
+					ChkCom();
+					SC_MustGetNumber();
+					state.Misc2=sc_Number;
+					SC_MustGetStringName(")");
+					continue;
+				}
 
 				// Make the action name lowercase to satisfy the gperf hashers
 				strlwr (sc_String);
@@ -2037,12 +1502,141 @@
 					goto endofstate;
 				}
 
-				for(int i=0;AFTable[i].Name;i++) if (SC_Compare(AFTable[i].Name))
+				AFuncDesc * afd = FindFunction(sc_String);
+				if (afd != NULL)
 				{
-					state.Action = AFTable[i].Function;
+					state.Action = afd->Function;
+					if (afd->parameters !=NULL)
+					{
+						const char * params=afd->parameters;
+						int numparams=strlen(params);
+						int paramindex=PrepareStateParameters(&state, numparams);
+						int v;
+
+						if (!islower(*params))
+						{
+							SC_MustGetStringName("(");
+						}
+						else
+						{
+							if (!SC_CheckString("(")) goto endofstate;
+						}
+						while (*params)
+						{
+							switch(*params)
+							{
+							case 'A':
+							case 'a':		// Angle
+								SC_MustGetFloat();
+								v=sc_Float*ANGLE_1;
+								break;
+
+							case 'B':
+							case 'b':		// Byte
+								SC_MustGetNumber();
+								v=clamp<int>(sc_Number, 0, 255);
+								break;
+
+							case '9':		// 90 degree angle as integer
+								SC_MustGetNumber();
+								v=clamp<int>(sc_Number, 0, 90);
+								break;
+
+							case 'I':
+							case 'i':		// Integer
+								SC_MustGetNumber();
+								v=sc_Number;
+								break;
+
+							case 'F':
+							case 'f':		// Fixed point
+								SC_MustGetFloat();
+								v=sc_Float*FRACUNIT;
+								break;
+
+							case 'S':
+							case 's':		// Sound name
+								SC_MustGetString();
+								v=S_FindSound(sc_String);
+								break;
+
+							case 'M':
+							case 'm':		// Actor name
+							case 'T':
+							case 't':		// String
+								SC_MustGetString();
+								v=(int)MS_Strdup(sc_String);
+								break;
+
+							case 'L':
+							case 'l':		// Jump label
+
+								if (strlen(statestring)>0)
+								{
+									SC_ScriptError("You cannot use A_Jump commands on multistate definitions\n");
+								}
+
+								SC_MustGetNumber();
+								v=sc_Number;
+								if (v<1)
+								{
+									SC_ScriptError("Negative jump offsets are not allowed");
+								}
+
+								{
+									int minreq=count+v;
+									if (minreq>minrequiredstate) minrequiredstate=minreq;
+								}
+
+								break;
+
+							case '!':		// not boolean (to simulate parameters which default to 1)
+								SC_MustGetNumber();
+								v=!sc_Number;
+								break;
+
+							case 'C':
+							case 'c':
+								SC_MustGetString ();
+								if (SC_Compare("none"))
+								{
+									v = -1;
+								}
+								else
+								{
+									int c = V_GetColor (NULL, sc_String);
+									// 0 needs to be the default so we have to add 1 here!
+									v = 1 + ColorMatcher.Pick (RPART (c), GPART (c), BPART (c));
+								}
+								break;
+
+							case 'X':
+							case 'x':
+								v = ParseExpression ();
+								break;
+							}
+							StateParameters[paramindex++]=v;
+							params++;
+							if (*params)
+							{
+								if ((islower(*params) || *params=='!') && SC_CheckString(")")) goto endofstate;
+								ChkCom();
+							}
+						}
+						SC_MustGetStringName(")");
+					}
+					else 
+					{
+						SC_MustGetString();
+						if (SC_Compare("("))
+						{
+							SC_ScriptError("You cannot pass parameters to '%s'\n",sc_String);
+						}
+						SC_UnGet();
+					}
 					goto endofstate;
 				}
-				SC_ScriptError("Invalid state parameter");
+				SC_ScriptError("Invalid state parameter %s\n", sc_String);
 			}
 			SC_UnGet();
 endofstate:
@@ -2057,7 +1651,7 @@
 					frame=0;
 				}
 
-				state.Frame=(state.Frame&(SF_FULLBRIGHT|SF_BIGTIC))|frame;
+				state.Frame=(state.Frame&(SF_FULLBRIGHT|SF_BIGTIC|SF_STATEPARAM))|frame;
 				StateArray.Push(state);
 				count++;
 			}
@@ -2073,6 +1667,12 @@
 	return count;
 }
 
+//==========================================================================
+//
+// FinishStates
+// copies a state block and fixes all state links
+//
+//==========================================================================
 static int FinishStates (FActorInfo *actor, AActor *defaults, Baggage &bag)
 {
 	FState **stp;
@@ -2086,11 +1686,32 @@
 	actor->NumOwnedStates=count;
 
 	// adjust the state pointers
-	for(stp=&defaults->SpawnState;stp<=&defaults->RaiseState;stp++)
+	// In the case new states are added these must be adjusted, too!
+	for(stp=&defaults->SpawnState;stp<=&defaults->GreetingsState;stp++)
 	{
 		int v=(int)*stp;
 		if (v>=1 && v<0x10000) *stp=actor->OwnedStates+v-1;
 	}
+	if (bag.Info->Class->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
+	{
+		AWeapon * weapon=(AWeapon*)defaults;
+
+		for(stp=&weapon->UpState;stp<=&weapon->FlashState;stp++)
+		{
+			int v=(int)*stp;
+			if (v>=1 && v<0x10000) *stp=actor->OwnedStates+v-1;
+		}
+	}
+	if (bag.Info->Class->IsDescendantOf(RUNTIME_CLASS(AInventory)))
+	{
+		AInventory * item=(AInventory*)defaults;
+
+		for(stp=&item->UseState;stp<=&item->DropState;stp++)
+		{
+			int v=(int)*stp;
+			if (v>=1 && v<0x10000) *stp=actor->OwnedStates+v-1;
+		}
+	}
 
 	for(i = currange = 0; i < count; i++)
 	{
@@ -2121,7 +1742,7 @@
 				char *offset = strtok (NULL, "+");
 				int v = offset ? strtol (offset, NULL, 0) : 0;
 
-				stp = FindState (defaults, label);
+				stp = FindState (defaults, bag.Info->Class, label);
 				if (stp)
 				{
 					if (*stp) realstates[i].NextState=*stp+v;
@@ -2165,7 +1786,7 @@
 			SC_MustGetString();
 
 			FState * basestate;
-			FState ** stp=FindState((AActor*)type->ParentType->ActorInfo->Defaults, sc_String);
+			FState ** stp=FindState((AActor*)type->ParentType->ActorInfo->Defaults, type, sc_String);
 			int v = 0;
 
 			if (stp) basestate =*stp;
@@ -2205,16 +1826,12 @@
 
 
 
+
 //==========================================================================
 //
-// Reads an actor definition
+// Handle actor properties
 //
 //==========================================================================
-inline char * MS_Strdup(const char * s)
-{
-	return *s? strdup(s):NULL;
-}
-
 void ParseActorProperties (Baggage &bag)
 {
 	const TypeInfo *info;
@@ -2229,31 +1846,42 @@
 		// Walk the ancestors of this type and see if any of them know
 		// about the property.
 		info = bag.Info->Class;
-		prop = NULL;
-		do
+		prop = is_actorprop (sc_String);
+
+		if (prop != NULL)
 		{
-			info = info->ParentType;
-			if (info == RUNTIME_CLASS(AWeapon))
+			if (!info->IsDescendantOf(prop->type))
 			{
-				//prop = is_weaponprop (sc_String);
-				prop = NULL;
+				SC_ScriptError("\"%s\" requires an actor of type \"%s\"\n", sc_String, prop->type->Name+1);
 			}
-			else if (info == RUNTIME_CLASS(AActor))
+			else
 			{
-				prop = is_actorprop (sc_String);
+				prop->Handler ((AActor *)bag.Info->Defaults, bag);
 			}
-		} while (info != RUNTIME_CLASS(AActor) && prop == NULL);
-		if (prop != NULL)
-		{
-			prop->Handler ((AActor *)bag.Info->Defaults, bag);
 		}
 		else
 		{
 			SC_ScriptError("\"%s\" is an unknown actor property\n", sc_String);
 		}
 	}
+
+	// copy the THRUGHOST flag to the HITS_GHOSTS weapon flag
+	// Their meaning is synonymous so it really doesn't make sense
+	// to have two different flags for this
+	if (bag.Info->Class->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
+	{
+		AWeapon * weapon=(AWeapon*)bag.Info->Defaults;
+
+		if (weapon->flags2&MF2_THRUGHOST) weapon->WeaponFlags|=WIF_HITS_GHOSTS;
+		else weapon->WeaponFlags&=~WIF_HITS_GHOSTS;
+	}
 }
 
+//==========================================================================
+//
+// Reads an actor definition
+//
+//==========================================================================
 void ProcessActor(void (*process)(FState *, int))
 {
 	FActorInfo * info=NULL;
@@ -2314,121 +1942,211 @@
 	SC_SetCMode (false);
 }
 
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//==========================================================================
+//
+// Property parsers
+//
+//==========================================================================
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorSkipSuper (AActor *defaults, Baggage &bag)
 {
 	ResetActor(defaults, &bag);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorSpawnID (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetNumber();
 	if (sc_Number<0 || sc_Number>255)
 	{
-		SC_ScriptError ("SpawnNum must be in the range [0,255]");
+		SC_ScriptError ("SpawnID must be in the range [0,255]");
 	}
 	else bag.Info->SpawnID=(BYTE)sc_Number;
 }
 
+//==========================================================================
+//
+//==========================================================================
+static void ActorConversationID (AActor *defaults, Baggage &bag)
+{
+	SC_MustGetNumber();
+	if (sc_Number<0 || sc_Number>344)
+	{
+		SC_ScriptError ("ConversationID must be in the range [0,344]");
+	}
+	else StrifeTypes[sc_Number] = bag.Info->Class;
+}
+
+//==========================================================================
+//
+//==========================================================================
 static void ActorHealth (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetNumber();
 	defaults->health=sc_Number;
 }
 
+//==========================================================================
+//
+//==========================================================================
+static void ActorGibHealth (AActor *defaults, Baggage &bag)
+{
+	SC_MustGetNumber();
+	bag.Info->Class->Meta.SetMetaInt (AMETA_GibHealth, sc_Number);
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void ActorWoundHealth (AActor *defaults, Baggage &bag)
+{
+	SC_MustGetNumber();
+	bag.Info->Class->Meta.SetMetaInt (AMETA_WoundHealth, sc_Number);
+}
+
+//==========================================================================
+//
+//==========================================================================
 static void ActorReactionTime (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetNumber();
 	defaults->reactiontime=sc_Number;
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorPainChance (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetNumber();
 	defaults->PainChance=sc_Number;
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorDamage (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetNumber();
 	defaults->damage=sc_Number;
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorSpeed (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetFloat();
 	defaults->Speed=fixed_t(sc_Float*FRACUNIT);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorRadius (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetFloat();
 	defaults->radius=fixed_t(sc_Float*FRACUNIT);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorHeight (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetFloat();
 	defaults->height=fixed_t(sc_Float*FRACUNIT);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorMass (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetNumber();
 	defaults->Mass=sc_Number;
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorXScale (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetFloat();
 	defaults->xscale=BYTE(sc_Float*64-1);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorYScale (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetFloat();
 	defaults->yscale=BYTE(sc_Float*64-1);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorScale (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetFloat();
 	defaults->xscale=defaults->yscale=BYTE(sc_Float*64-1);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorSeeSound (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetString();
 	defaults->SeeSound=S_FindSound(sc_String);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorAttackSound (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetString();
 	defaults->AttackSound=S_FindSound(sc_String);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorPainSound (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetString();
 	defaults->PainSound=S_FindSound(sc_String);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorDeathSound (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetString();
 	defaults->DeathSound=S_FindSound(sc_String);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorActiveSound (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetString();
 	defaults->ActiveSound=S_FindSound(sc_String);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorDropItem (AActor *defaults, Baggage &bag)
 {
 	// create a linked list of dropitems
@@ -2456,61 +2174,153 @@
 	bag.DropItemList = di;
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorSpawnState (AActor *defaults, Baggage &bag)
 {
 	defaults->SpawnState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorSeeState (AActor *defaults, Baggage &bag)
 {
 	defaults->SeeState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorMeleeState (AActor *defaults, Baggage &bag)
 {
 	defaults->MeleeState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorMissileState (AActor *defaults, Baggage &bag)
 {
 	defaults->MissileState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorPainState (AActor *defaults, Baggage &bag)
 {
 	defaults->PainState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorDeathState (AActor *defaults, Baggage &bag)
 {
 	defaults->DeathState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorXDeathState (AActor *defaults, Baggage &bag)
 {
 	defaults->XDeathState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorBurnState (AActor *defaults, Baggage &bag)
 {
 	defaults->BDeathState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorIceState (AActor *defaults, Baggage &bag)
 {
 	defaults->IDeathState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorRaiseState (AActor *defaults, Baggage &bag)
 {
 	defaults->RaiseState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorCrashState (AActor *defaults, Baggage &bag)
 {
 	defaults->CrashState=CheckState (bag.CurrentState, bag.Info->Class);
 }
 
+//==========================================================================
+//
+//==========================================================================
+static void ActorCrushState (AActor *defaults, Baggage &bag)
+{
+	defaults->CrushState=CheckState (bag.CurrentState, bag.Info->Class);
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void ActorWoundState (AActor *defaults, Baggage &bag)
+{
+	defaults->WoundState=CheckState (bag.CurrentState, bag.Info->Class);
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void ActorDisintegrateState (AActor *defaults, Baggage &bag)
+{
+	defaults->EDeathState=CheckState (bag.CurrentState, bag.Info->Class);
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void ActorHealState (AActor *defaults, Baggage &bag)
+{
+	defaults->HealState=CheckState (bag.CurrentState, bag.Info->Class);
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void ActorYesState (AActor *defaults, Baggage &bag)
+{
+	defaults->YesState=CheckState (bag.CurrentState, bag.Info->Class);
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void ActorNoState (AActor *defaults, Baggage &bag)
+{
+	defaults->NoState=CheckState (bag.CurrentState, bag.Info->Class);
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void ActorGreetingsState (AActor *defaults, Baggage &bag)
+{
+	defaults->GreetingsState=CheckState (bag.CurrentState, bag.Info->Class);
+}
+
+//==========================================================================
+//
+//==========================================================================
 static void ActorStates (AActor *defaults, Baggage &bag)
 {
 	if (!bag.StateSet) ProcessStates(bag.Info, defaults, bag, SRT_Actor);
@@ -2518,6 +2328,9 @@
 	bag.StateSet=true;
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorRenderStyle (AActor *defaults, Baggage &bag)
 {
 	static const char * renderstyles[]={
@@ -2531,41 +2344,62 @@
 	defaults->RenderStyle=renderstyle_values[SC_MustMatchString(renderstyles)];
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorAlpha (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetFloat();
 	defaults->alpha=fixed_t(sc_Float*FRACUNIT);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorObituary (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetString();
 	bag.Info->Class->Meta.SetMetaString (AMETA_Obituary, sc_String);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorHitObituary (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetString();
 	bag.Info->Class->Meta.SetMetaString (AMETA_HitObituary, sc_String);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorDontHurtShooter (AActor *defaults, Baggage &bag)
 {
 	bag.EParms.HurtShooter=false;
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorExplosionRadius (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetNumber();
 	bag.EParms.ExplosionRadius=sc_Number;
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorExplosionDamage (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetNumber();
 	bag.EParms.ExplosionDamage=sc_Number;
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorDeathHeight (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetFloat();
@@ -2575,6 +2409,9 @@
 	bag.Info->Class->Meta.SetMetaFixed (AMETA_DeathHeight, h <= 0 ? -1 : h);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorBurnHeight (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetFloat();
@@ -2583,45 +2420,151 @@
 	bag.Info->Class->Meta.SetMetaFixed (AMETA_BurnHeight, h <= 0 ? -1 : h);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorMeleeDamage (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetNumber();
 	bag.BAttack.MeleeDamage = sc_Number;
 }
 
+//==========================================================================
+//
+//==========================================================================
+static void ActorMeleeRange (AActor *defaults, Baggage &bag)
+{
+	SC_MustGetNumber();
+	defaults->meleerange = sc_Number;
+}
+
+//==========================================================================
+//
+//==========================================================================
 static void ActorMeleeSound (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetString();
 	bag.BAttack.MeleeSound = S_FindSound(sc_String);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorMissileType (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetString();
 	bag.BAttack.MissileName = MS_Strdup(sc_String);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorMissileHeight (AActor *defaults, Baggage &bag)
 {
 	SC_MustGetFloat();
 	bag.BAttack.MissileHeight = fixed_t(sc_Float*FRACUNIT);
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorTranslation (AActor *defaults, Baggage &bag)
 {
-	SC_MustGetNumber ();
-	if (sc_Number < 0 || sc_Number > 2)
+	if (SC_CheckNumber())
 	{
-		SC_ScriptError ("Translation must be in the range [0,2]");
+		int max = gameinfo.gametype==GAME_Strife? 6:2;
+		if (sc_Number < 0 || sc_Number > max)
+		{
+			SC_ScriptError ("Translation must be in the range [0,%d]", max);
+		}
+		defaults->Translation = TRANSLATION(TRANSLATION_Standard, sc_Number);
+	}
+	else
+	{
+		unsigned char translation[256];
+		for(int i=0;i<256;i++) translation[i]=i;
+		do
+		{
+			SC_GetString();
+			AddToTranslation(translation,sc_String);
+		}
+		while (SC_CheckString(","));
+		defaults->Translation = StoreTranslation (translation);
 	}
-	defaults->Translation = TRANSLATION(TRANSLATION_Standard, sc_Number);
 }
 
+//==========================================================================
+//
+//==========================================================================
+static void ActorBloodColor (AActor *defaults, Baggage &bag)
+{
+	int r,g,b;
+
+	if (SC_CheckNumber())
+	{
+		SC_MustGetNumber();
+		r=clamp<int>(sc_Number, 0, 255);
+		SC_MustGetNumber();
+		g=clamp<int>(sc_Number, 0, 255);
+		SC_MustGetNumber();
+		b=clamp<int>(sc_Number, 0, 255);
+	}
+	else
+	{
+		SC_MustGetString();
+		int c = V_GetColor(NULL, sc_String);
+		r=RPART(c);
+		g=GPART(c);
+		b=BPART(c);
+	}
+	PalEntry pe = MAKERGB(r,g,b);
+	pe.a = CreateBloodTranslation(pe);
+	bag.Info->Class->Meta.SetMetaInt (AMETA_BloodColor, pe);
+}
+
+
+//==========================================================================
+//
+//==========================================================================
+static void ActorMinMissileChance (AActor *defaults, Baggage &bag)
+{
+	SC_MustGetNumber ();
+	defaults->MinMissileChance=sc_Number;
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void ActorDamageType (AActor *defaults, Baggage &bag)
+{
+	SC_MustGetString ();   
+	if (SC_Compare("Normal")) defaults->DamageType=MOD_UNKNOWN;
+	else if (SC_Compare("Fire")) defaults->DamageType=MOD_FIRE;
+	else if (SC_Compare("Ice")) defaults->DamageType=MOD_ICE;
+	else if (SC_Compare("Disintegrate")) defaults->DamageType=MOD_DISINTEGRATE;
+	else SC_ScriptError("Unknown damage type '%s'\n", sc_String);
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void ActorDecal (AActor *defaults, Baggage &bag)
+{
+	SC_MustGetString();
+	defaults->DecalGenerator = (FDecalBase *) (DecalNames.Push(copystring(sc_String))+1);
+}
+
+//==========================================================================
+//
+//==========================================================================
 static void ActorClearFlags (AActor *defaults, Baggage &bag)
 {
 	defaults->flags=defaults->flags2=defaults->flags3=defaults->flags4=0;
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorMonster (AActor *defaults, Baggage &bag)
 {
 	// sets the standard flag for a monster
@@ -2630,6 +2573,9 @@
 	defaults->flags3|=MF3_ISMONSTER;
 }
 
+//==========================================================================
+//
+//==========================================================================
 static void ActorProjectile (AActor *defaults, Baggage &bag)
 {
 	// sets the standard flags for a projectile
@@ -2637,20 +2583,33 @@
 	defaults->flags2|=MF2_IMPACT|MF2_PCROSS|MF2_NOTELEPORT;
 }
 
+//==========================================================================
+//
+//==========================================================================
+static void ActorUser (AActor *defaults, Baggage &bag)
+{
+	SC_MustGetNumber ();
+	if (sc_Number < 1 || sc_Number > 8)
+	{
+		SC_ScriptError("User variable index bust be in range [1..8]");
+	}
+	int i=sc_Number-1;
+	SC_MustGetNumber ();
+	defaults->user[i] = sc_Number;
+}
+
+//==========================================================================
+//
+//==========================================================================
 static void ActorFlagSetOrReset (AActor *defaults, Baggage &bag)
 {
 	char mod = sc_String[0];
-	int v;
+	flagdef * fd;
 
 	SC_MustGetString ();
-	// This isn't a real flag but should be usable as one!
-	if (SC_Compare ("DOOMBOUNCE"))
-	{
-		if (mod=='+') defaults->flags2|=MF2_DOOMBOUNCE;
-		else defaults->flags2&=~MF2_DOOMBOUNCE;
-	}
+
 	// Fire and ice damage were once flags but now are not.
-	else if (SC_Compare ("FIREDAMAGE"))
+	if (SC_Compare ("FIREDAMAGE"))
 	{
 		if (mod=='+') defaults->DamageType = MOD_FIRE;
 		else defaults->DamageType = MOD_UNKNOWN;
@@ -2660,33 +2619,383 @@
 		if (mod=='+') defaults->DamageType = MOD_ICE;
 		else defaults->DamageType = MOD_UNKNOWN;
 	}
-	else if ((v=SC_MatchString(flagnames))!=-1)
+	else if (fd=FindFlag(sc_String))
 	{
-		unsigned fmod=1<<v;
-		if (mod=='+') defaults->flags|=fmod;
-		else defaults->flags&=~fmod;
+		DWORD * flagvar = (DWORD*) ((char*)defaults + fd->structoffset);
+		if (mod=='+') *flagvar|=fd->flagbit;
+		else *flagvar&=~fd->flagbit;
 	}
-	else if ((v=SC_MatchString(flag2names))!=-1)
+	else SC_ScriptError("\"%s\" is an unknown flag\n", sc_String);
+}
+
+//==========================================================================
+//
+// Special inventory properties
+//
+//==========================================================================
+
+//==========================================================================
+//
+//==========================================================================
+static void AmmoBackpackAmount (AAmmo *defaults, Baggage &bag)
+{
+	SC_MustGetNumber();
+	defaults->BackpackAmount=sc_Number;
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void AmmoBackpackMaxAmount (AAmmo *defaults, Baggage &bag)
+{
+	SC_MustGetNumber();
+	defaults->BackpackMaxAmount=sc_Number;
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void ArmorMaxSaveAmount (ABasicArmorBonus *defaults, Baggage &bag)
+{
+	SC_MustGetNumber();
+	defaults->MaxSaveAmount=sc_Number;
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void ArmorSaveAmount (AActor *defaults, Baggage &bag)
+{
+	SC_MustGetNumber();
+	// Special case here because this property has to work for 2 unrelated classes
+	if (bag.Info->Class->IsDescendantOf(RUNTIME_CLASS(ABasicArmorPickup)))
 	{
-		unsigned long fmod=1<<v;
-		if (mod=='+') defaults->flags2|=fmod;
-		else defaults->flags2&=~fmod;
+		((ABasicArmorPickup*)defaults)->SaveAmount=sc_Number;
 	}
-	else if ((v=SC_MatchString(flag3names))!=-1)
+	else if (bag.Info->Class->IsDescendantOf(RUNTIME_CLASS(ABasicArmorBonus)))
 	{
-		unsigned long fmod=1<<v;
-		if (mod=='+') defaults->flags3|=fmod;
-		else defaults->flags3&=~fmod;
+		((ABasicArmorBonus*)defaults)->SaveAmount=sc_Number;
 	}
-	else if ((v=SC_MatchString(flag4names))!=-1)
+	else
 	{
-		unsigned long fmod=1<<v;
-		if (mod=='+') defaults->flags4|=fmod;
-		else defaults->flags4&=~fmod;
+		SC_ScriptError("\"%s\" requires an actor of type \"Armor\"\n", sc_String);
 	}
-	else SC_ScriptError("\"%s\" is an unknown flag\n", sc_String);
 }
 
+//==========================================================================
+//
+//==========================================================================
+static void ArmorSavePercent (AActor *defaults, Baggage &bag)
+{
+	SC_MustGetFloat();
+	if (sc_Float<0.0f) sc_Float=0.0f;
+	if (sc_Float>100.0f) sc_Float=100.0f;
+	// Special case here because this property has to work for 2 unrelated classes
+	if (bag.Info->Class->IsDescendantOf(RUNTIME_CLASS(ABasicArmorPickup)))
+	{
+		((ABasicArmorPickup*)defaults)->SavePercent=sc_Float*FRACUNIT/100.0f;
+	}
+	else if (bag.Info->Class->IsDescendantOf(RUNTIME_CLASS(ABasicArmorBonus)))
+	{
+		((ABasicArmorBonus*)defaults)->SavePercent=sc_Float*FRACUNIT/100.0f;
+	}
+	else
+	{
+		SC_ScriptError("\"%s\" requires an actor of type \"Armor\"\n", sc_String);
+	}
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void InventoryAmount (AInventory *defaults, Baggage &bag)
+{
+	SC_MustGetNumber();
+	defaults->Amount=sc_Number;
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void InventoryIcon (AInventory *defaults, Baggage &bag)
+{
+	SC_MustGetString();
+	defaults->Icon = TexMan.AddPatch (sc_String);
+	if (defaults->Icon <= 0)
+	{
+		defaults->Icon = TexMan.AddPatch (sc_String, ns_sprites);
+		if (defaults->Icon<=0)
+		{
+			Printf("Icon '%s' for '%s' not found\n", sc_String, bag.Info->Class->Name+1);
+		}
+	}
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void InventoryMaxAmount (AInventory *defaults, Baggage &bag)
+{
+	SC_MustGetNumber();
+	defaults->MaxAmount=sc_Number;
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void InventoryDefMaxAmount (AInventory *defaults, Baggage &bag)
+{
+	defaults->MaxAmount = gameinfo.gametype == GAME_Heretic ? 16 : 25;
+}
+
+
+//==========================================================================
+//
+//==========================================================================
+static void InventoryPickupmsg (AInventory *defaults, Baggage &bag)
+{
+	SC_MustGetString();
+	bag.Info->Class->Meta.SetMetaString(AIMETA_PickupMessage, sc_String);
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void InventoryPickupsound (AInventory *defaults, Baggage &bag)
+{
+	SC_MustGetString();
+	defaults->PickupSound=S_FindSound(sc_String);
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void InventoryRespawntics (AInventory *defaults, Baggage &bag)
+{
+	SC_MustGetNumber();
+	defaults->RespawnTics=sc_Number;
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void InventoryUsesound (AInventory *defaults, Baggage &bag)
+{
+	SC_MustGetString();
+	defaults->UseSound=S_FindSound(sc_String);
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void PuzzleitemNumber (APuzzleItem *defaults, Baggage &bag)
+{
+	SC_MustGetNumber();
+	defaults->PuzzleItemNumber=sc_Number;
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void WeaponAmmoGive1 (AWeapon *defaults, Baggage &bag)
+{
+	SC_MustGetNumber();
+	defaults->AmmoGive1=sc_Number;
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void WeaponAmmoGive2 (AWeapon *defaults, Baggage &bag)
+{
+	SC_MustGetNumber();
+	defaults->AmmoGive2=sc_Number;
+}
+
+//==========================================================================
+//
+// Passing these parameters is really tricky to allow proper inheritance
+// and forward declarations. Here only an index into a string table is
+// stored which must be resolved after everything has been declared
+//
+//==========================================================================
+static void WeaponAmmoType1 (AWeapon *defaults, Baggage &bag)
+{
+	SC_MustGetString();
+	char * str=copystring(sc_String);
+	defaults->AmmoType1=(const TypeInfo*)(StringTypes.Push(str)+1);
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void WeaponAmmoType2 (AWeapon *defaults, Baggage &bag)
+{
+	SC_MustGetString();
+	char * str=copystring(sc_String);
+	defaults->AmmoType2=(const TypeInfo*)(StringTypes.Push(str)+1);
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void WeaponAmmoUse1 (AWeapon *defaults, Baggage &bag)
+{
+	SC_MustGetNumber();
+	defaults->AmmoUse1=sc_Number;
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void WeaponAmmoUse2 (AWeapon *defaults, Baggage &bag)
+{
+	SC_MustGetNumber();
+	defaults->AmmoUse2=sc_Number;
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void WeaponKickback (AWeapon *defaults, Baggage &bag)
+{
+	SC_MustGetNumber();
+	defaults->Kickback=sc_Number;
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void WeaponReadySound (AWeapon *defaults, Baggage &bag)
+{
+	SC_MustGetString();
+	defaults->ReadySound=S_FindSound(sc_String);
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void WeaponSelectionOrder (AWeapon *defaults, Baggage &bag)
+{
+	SC_MustGetNumber();
+	defaults->SelectionOrder=sc_Number;
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void WeaponSisterWeapon (AWeapon *defaults, Baggage &bag)
+{
+	SC_MustGetString();
+	char * str=copystring(sc_String);
+	defaults->SisterWeaponType=(const TypeInfo*)(StringTypes.Push(str)+1);
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void WeaponUpSound (AWeapon *defaults, Baggage &bag)
+{
+	SC_MustGetString();
+	defaults->UpSound=S_FindSound(sc_String);
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void WeaponYAdjust (AWeapon *defaults, Baggage &bag)
+{
+	SC_MustGetFloat();
+	defaults->YAdjust=sc_Float * FRACUNIT;
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void WPieceValue (AWeaponPiece *defaults, Baggage &bag)
+{
+	SC_MustGetNumber();
+	defaults->PieceValue = 1 << (sc_Number-1);
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void WPieceWeapon (AWeaponPiece *defaults, Baggage &bag)
+{
+	SC_MustGetString();
+	char * str=copystring(sc_String);
+	defaults->WeaponClass = (const TypeInfo*)(StringTypes.Push(str)+1);
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void PowerupColor (APowerupGiver *defaults, Baggage &bag)
+{
+	int r;
+	int g;
+	int b;
+	int alpha;
+
+	if (SC_CheckNumber())
+	{
+		r=clamp<int>(sc_Number, 0, 255);
+		SC_MustGetNumber();
+		g=clamp<int>(sc_Number, 0, 255);
+		SC_MustGetNumber();
+		b=clamp<int>(sc_Number, 0, 255);
+	}
+	else
+	{
+		SC_MustGetString();
+		int c = V_GetColor(NULL, sc_String);
+		r=RPART(c);
+		g=GPART(c);
+		b=BPART(c);
+	}
+	SC_MustGetFloat();
+	alpha=sc_Float*255;
+	alpha=clamp<int>(alpha, 0, 255);
+	defaults->BlendColor = MAKEARGB(alpha, r, g, b);
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void PowerupDuration (APowerupGiver *defaults, Baggage &bag)
+{
+	SC_MustGetNumber();
+	defaults->EffectTics = sc_Number;
+}
+
+//==========================================================================
+//
+//==========================================================================
+static void PowerupType (APowerupGiver *defaults, Baggage &bag)
+{
+	char typestr[256];
+	SC_MustGetString();
+	sc_String[249]=0;
+	sprintf(typestr, "Power%s", sc_String);
+	const TypeInfo * powertype=TypeInfo::FindType(typestr);
+	if (!powertype)
+	{
+		SC_ScriptError("Unknown powerup type '%s' in '%s'\n", sc_String, bag.Info->Class->Name+1);
+	}
+	else if (!powertype->IsDescendantOf(RUNTIME_CLASS(APowerup)))
+	{
+		SC_ScriptError("Invalid powerup type '%s' in '%s'\n", sc_String, bag.Info->Class->Name+1);
+	}
+	else
+	{
+		defaults->PowerupType=powertype;
+	}
+}
+
+//==========================================================================
+//
+//==========================================================================
 static const ActorProps *APropSearch (const char *str, const ActorProps *props, int numprops)
 {
 	int min = 0, max = numprops - 1;
@@ -2711,70 +3020,226 @@
 	return NULL;
 }
 
+//==========================================================================
+//
+// all actor properties
+//
+//==========================================================================
+#define apf ActorPropFunction
 static const ActorProps *is_actorprop (const char *str)
 {
 	static const ActorProps props[] =
 	{
-		{ "+",					ActorFlagSetOrReset },
-		{ "-",					ActorFlagSetOrReset },
-		{ "activesound",		ActorActiveSound },
-		{ "alpha",				ActorAlpha },
-		{ "attacksound",		ActorAttackSound },
-		{ "burn",				ActorBurnState },
-		{ "burnheight",			ActorBurnHeight },
-		{ "clearflags",			ActorClearFlags },
-		{ "crash",				ActorCrashState },
-		{ "damage",				ActorDamage },
-		{ "death",				ActorDeathState },
-		{ "deathheight",		ActorDeathHeight },
-		{ "deathsound",			ActorDeathSound },
-		{ "donthurtshooter",	ActorDontHurtShooter },
-		{ "dropitem",			ActorDropItem },
-		{ "explosiondamage",	ActorExplosionDamage },
-		{ "explosionradius",	ActorExplosionRadius },
-		{ "health",				ActorHealth },
-		{ "height",				ActorHeight },
-		{ "hitobituary",		ActorHitObituary },
-		{ "ice",				ActorIceState },
-		{ "mass",				ActorMass },
-		{ "melee",				ActorMeleeState },
-		{ "meleedamage",		ActorMeleeDamage },
-		{ "meleesound",			ActorMeleeSound },
-		{ "missile",			ActorMissileState },
-		{ "missileheight",		ActorMissileHeight },
-		{ "missiletype",		ActorMissileType },
-		{ "monster",			ActorMonster },
-		{ "obituary",			ActorObituary },
-		{ "pain",				ActorPainState },
-		{ "painchance",			ActorPainChance },
-		{ "painsound",			ActorPainSound },
-		{ "projectile",			ActorProjectile },
-		{ "radius",				ActorRadius },
-		{ "raise",				ActorRaiseState },
-		{ "reactiontime",		ActorReactionTime },
-		{ "renderstyle",		ActorRenderStyle },
-		{ "scale",				ActorScale },
-		{ "see",				ActorSeeState },
-		{ "seesound",			ActorSeeSound },
-		{ "skip_super",			ActorSkipSuper },
-		{ "spawn",				ActorSpawnState },
-		{ "spawnid",			ActorSpawnID },
-		{ "speed",				ActorSpeed },
-		{ "states",				ActorStates },
-		{ "translation",		ActorTranslation },
-		{ "xdeath",				ActorXDeathState },
-		{ "xscale",				ActorXScale },
-		{ "yscale",				ActorYScale },
+		{ "+",							ActorFlagSetOrReset,		RUNTIME_CLASS(AActor) },
+		{ "-",							ActorFlagSetOrReset,		RUNTIME_CLASS(AActor) },
+		{ "activesound",				ActorActiveSound,			RUNTIME_CLASS(AActor) },
+		{ "alpha",						ActorAlpha,					RUNTIME_CLASS(AActor) },
+		{ "ammo.backpackamount",		(apf)AmmoBackpackAmount,	RUNTIME_CLASS(AAmmo) },
+		{ "ammo.backpackmaxamount",		(apf)AmmoBackpackMaxAmount,	RUNTIME_CLASS(AAmmo) },
+		{ "armor.maxsaveamount",		(apf)ArmorMaxSaveAmount,	RUNTIME_CLASS(ABasicArmorBonus) },
+		{ "armor.saveamount",			(apf)ArmorSaveAmount,		RUNTIME_CLASS(AActor) },
+		{ "armor.savepercent",			(apf)ArmorSavePercent,		RUNTIME_CLASS(AActor) },
+		{ "attacksound",				ActorAttackSound,			RUNTIME_CLASS(AActor) },
+		{ "bloodcolor",					ActorBloodColor,			RUNTIME_CLASS(AActor) },
+		{ "burn",						ActorBurnState,				RUNTIME_CLASS(AActor) },
+		{ "burnheight",					ActorBurnHeight,			RUNTIME_CLASS(AActor) },
+		{ "clearflags",					ActorClearFlags,			RUNTIME_CLASS(AActor) },
+		{ "conversationid",				ActorConversationID,		RUNTIME_CLASS(AActor) },
+		{ "crash",						ActorCrashState,			RUNTIME_CLASS(AActor) },
+		{ "crush",						ActorCrushState,			RUNTIME_CLASS(AActor) },
+		{ "damage",						ActorDamage,				RUNTIME_CLASS(AActor) },
+		{ "damagetype",					ActorDamageType,			RUNTIME_CLASS(AActor) },
+		{ "death",						ActorDeathState,			RUNTIME_CLASS(AActor) },
+		{ "deathheight",				ActorDeathHeight,			RUNTIME_CLASS(AActor) },
+		{ "deathsound",					ActorDeathSound,			RUNTIME_CLASS(AActor) },
+		{ "decal",						ActorDecal,					RUNTIME_CLASS(AActor) },
+		{ "disintegrate",				ActorDisintegrateState,		RUNTIME_CLASS(AActor) },
+		{ "donthurtshooter",			ActorDontHurtShooter,		RUNTIME_CLASS(AActor) },
+		{ "dropitem",					ActorDropItem,				RUNTIME_CLASS(AActor) },
+		{ "explosiondamage",			ActorExplosionDamage,		RUNTIME_CLASS(AActor) },
+		{ "explosionradius",			ActorExplosionRadius,		RUNTIME_CLASS(AActor) },
+		{ "gibhealth",					ActorGibHealth,				RUNTIME_CLASS(AActor) },
+		{ "greetings",					ActorGreetingsState,		RUNTIME_CLASS(AActor) },
+		{ "heal",						ActorHealState,				RUNTIME_CLASS(AActor) },
+		{ "health",						ActorHealth,				RUNTIME_CLASS(AActor) },
+		{ "height",						ActorHeight,				RUNTIME_CLASS(AActor) },
+		{ "hitobituary",				ActorHitObituary,			RUNTIME_CLASS(AActor) },
+		{ "ice",						ActorIceState,				RUNTIME_CLASS(AActor) },
+		{ "inventory.amount",			(apf)InventoryAmount,		RUNTIME_CLASS(AInventory) },
+		{ "inventory.defmaxamount",		(apf)InventoryDefMaxAmount,	RUNTIME_CLASS(AInventory) },
+		{ "inventory.icon",				(apf)InventoryIcon,			RUNTIME_CLASS(AInventory) },
+		{ "inventory.maxamount",		(apf)InventoryMaxAmount,	RUNTIME_CLASS(AInventory) },
+		{ "inventory.pickupmessage",	(apf)InventoryPickupmsg,	RUNTIME_CLASS(AInventory) },
+		{ "inventory.pickupsound",		(apf)InventoryPickupsound,	RUNTIME_CLASS(AInventory) },
+		{ "inventory.respawntics",		(apf)InventoryRespawntics,	RUNTIME_CLASS(AInventory) },
+		{ "inventory.usesound",			(apf)InventoryUsesound,		RUNTIME_CLASS(AInventory) },
+		{ "mass",						ActorMass,					RUNTIME_CLASS(AActor) },
+		{ "melee",						ActorMeleeState,			RUNTIME_CLASS(AActor) },
+		{ "meleedamage",				ActorMeleeDamage,			RUNTIME_CLASS(AActor) },
+		{ "meleerange",					ActorMeleeRange,			RUNTIME_CLASS(AActor) },
+		{ "meleesound",					ActorMeleeSound,			RUNTIME_CLASS(AActor) },
+		{ "minmissilechance",			ActorMinMissileChance,		RUNTIME_CLASS(AActor) },
+		{ "missile",					ActorMissileState,			RUNTIME_CLASS(AActor) },
+		{ "missileheight",				ActorMissileHeight,			RUNTIME_CLASS(AActor) },
+		{ "missiletype",				ActorMissileType,			RUNTIME_CLASS(AActor) },
+		{ "monster",					ActorMonster,				RUNTIME_CLASS(AActor) },
+		{ "no",							ActorNoState,				RUNTIME_CLASS(AActor) },
+		{ "obituary",					ActorObituary,				RUNTIME_CLASS(AActor) },
+		{ "pain",						ActorPainState,				RUNTIME_CLASS(AActor) },
+		{ "painchance",					ActorPainChance,			RUNTIME_CLASS(AActor) },
+		{ "painsound",					ActorPainSound,				RUNTIME_CLASS(AActor) },
+		{ "powerup.color",				(apf)PowerupColor,			RUNTIME_CLASS(APowerupGiver) },
+		{ "powerup.duration",			(apf)PowerupDuration,		RUNTIME_CLASS(APowerupGiver) },
+		{ "powerup.type",				(apf)PowerupType,			RUNTIME_CLASS(APowerupGiver) },
+		{ "projectile",					ActorProjectile,			RUNTIME_CLASS(AActor) },
+		{ "puzzleitem.number",			(apf)PuzzleitemNumber,		RUNTIME_CLASS(APuzzleItem) },
+		{ "radius",						ActorRadius,				RUNTIME_CLASS(AActor) },
+		{ "raise",						ActorRaiseState,			RUNTIME_CLASS(AActor) },
+		{ "reactiontime",				ActorReactionTime,			RUNTIME_CLASS(AActor) },
+		{ "renderstyle",				ActorRenderStyle,			RUNTIME_CLASS(AActor) },
+		{ "scale",						ActorScale,					RUNTIME_CLASS(AActor) },
+		{ "see",						ActorSeeState,				RUNTIME_CLASS(AActor) },
+		{ "seesound",					ActorSeeSound,				RUNTIME_CLASS(AActor) },
+		{ "skip_super",					ActorSkipSuper,				RUNTIME_CLASS(AActor) },
+		{ "spawn",						ActorSpawnState,			RUNTIME_CLASS(AActor) },
+		{ "spawnid",					ActorSpawnID,				RUNTIME_CLASS(AActor) },
+		{ "speed",						ActorSpeed,					RUNTIME_CLASS(AActor) },
+		{ "states",						ActorStates,				RUNTIME_CLASS(AActor) },
+		{ "translation",				ActorTranslation,			RUNTIME_CLASS(AActor) },
+		{ "user",						ActorUser, 					RUNTIME_CLASS(AActor) },
+		{ "weapon.ammogive",			(apf)WeaponAmmoGive1,		RUNTIME_CLASS(AWeapon) },
+		{ "weapon.ammogive1",			(apf)WeaponAmmoGive1,		RUNTIME_CLASS(AWeapon) },
+		{ "weapon.ammogive2",			(apf)WeaponAmmoGive2,		RUNTIME_CLASS(AWeapon) },
+		{ "weapon.ammotype",			(apf)WeaponAmmoType1,		RUNTIME_CLASS(AWeapon) },
+		{ "weapon.ammotype1",			(apf)WeaponAmmoType1,		RUNTIME_CLASS(AWeapon) },
+		{ "weapon.ammotype2",			(apf)WeaponAmmoType2,		RUNTIME_CLASS(AWeapon) },
+		{ "weapon.ammouse",				(apf)WeaponAmmoUse1,		RUNTIME_CLASS(AWeapon) },
+		{ "weapon.ammouse1",			(apf)WeaponAmmoUse1,		RUNTIME_CLASS(AWeapon) },
+		{ "weapon.ammouse2",			(apf)WeaponAmmoUse2,		RUNTIME_CLASS(AWeapon) },
+		{ "weapon.kickback",			(apf)WeaponKickback,		RUNTIME_CLASS(AWeapon) },
+		{ "weapon.readysound",			(apf)WeaponReadySound,		RUNTIME_CLASS(AWeapon) },
+		{ "weapon.selectionorder",		(apf)WeaponSelectionOrder,	RUNTIME_CLASS(AWeapon) },
+		{ "weapon.sisterweapon",		(apf)WeaponSisterWeapon,	RUNTIME_CLASS(AWeapon) },
+		{ "weapon.upsound",				(apf)WeaponUpSound,			RUNTIME_CLASS(AWeapon) },
+		{ "weapon.yadjust",				(apf)WeaponYAdjust,			RUNTIME_CLASS(AWeapon) },
+		{ "weaponpiece.number",			(apf)WPieceValue,			RUNTIME_CLASS(AWeaponPiece) },
+		{ "weaponpiece.weapon",			(apf)WPieceWeapon,			RUNTIME_CLASS(AWeaponPiece) },
+		{ "wound",						ActorWoundState,			RUNTIME_CLASS(AActor) },
+		{ "woundhealth",				ActorWoundHealth,			RUNTIME_CLASS(AActor) },
+		{ "xdeath",						ActorXDeathState,			RUNTIME_CLASS(AActor) },
+		{ "xscale",						ActorXScale,				RUNTIME_CLASS(AActor) },
+		{ "yes",						ActorYesState,				RUNTIME_CLASS(AActor) },
+		{ "yscale",						ActorYScale,				RUNTIME_CLASS(AActor) },
+		// AWeapon:MinAmmo1 and 2 are never used so there is no point in adding them here!
 	};
 	return APropSearch (str, props, sizeof(props)/sizeof(ActorProps));
 }
 
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
+//==========================================================================
+//
+// Do some postprocessing after everything has been defined
+//
+//==========================================================================
 void FinishThingdef()
 {
+	int i;
+
+	for (i = 0;i < TypeInfo::m_RuntimeActors.Size(); i++)
+	{
+		TypeInfo * ti = TypeInfo::m_RuntimeActors[i];
+
+		// Friendlies never count as kills!
+		if (GetDefaultByType(ti)->flags & MF_FRIENDLY)
+		{
+			GetDefaultByType(ti)->flags &=~MF_COUNTKILL;
+		}
+
+		// the typeinfo properties of weapons have to be fixed here after all actors have been declared
+		if (ti->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
+		{
+			AWeapon * defaults=(AWeapon *)ti->ActorInfo->Defaults;
+			int v;
+
+			v=(int) defaults->AmmoType1;
+			if (v>0 && v<=StringTypes.Size())
+			{
+				defaults->AmmoType1=TypeInfo::FindType(StringTypes[v-1]);
+				if (!defaults->AmmoType1)
+				{
+					SC_ScriptError("Unknown ammo type '%s' in '%s'\n", StringTypes[v], ti->Name+1);
+				}
+				else if (defaults->AmmoType1->ParentType!=RUNTIME_CLASS(AAmmo))
+				{
+					SC_ScriptError("Invalid ammo type '%s' in '%s'\n", StringTypes[v], ti->Name+1);
+				}
+			}
+
+			v=(int) defaults->AmmoType2;
+			if (v>0 && v<=StringTypes.Size())
+			{
+				defaults->AmmoType2=TypeInfo::FindType(StringTypes[v-1]);
+				if (!defaults->AmmoType2)
+				{
+					SC_ScriptError("Unknown ammo type '%s' in '%s'\n", StringTypes[v], ti->Name+1);
+				}
+				else if (defaults->AmmoType2->ParentType!=RUNTIME_CLASS(AAmmo))
+				{
+					SC_ScriptError("Invalid ammo type '%s' in '%s'\n", StringTypes[v], ti->Name+1);
+				}
+			}
+
+			v=(int) defaults->SisterWeaponType;
+			if (v>0 && v<=StringTypes.Size())
+			{
+				defaults->SisterWeaponType=TypeInfo::FindType(StringTypes[v-1]);
+				if (!defaults->SisterWeaponType)
+				{
+					SC_ScriptError("Unknown sister weapon type '%s' in '%s'\n", StringTypes[v], ti->Name+1);
+				}
+				else if (!defaults->SisterWeaponType->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
+				{
+					SC_ScriptError("Invalid sister weapon type '%s' in '%s'\n", StringTypes[v], ti->Name+1);
+				}
+			}
+
+			// Do some consistency checks. If these states are undefined the weapon cannot work!
+			if (!defaults->ReadyState) SC_ScriptError("Weapon %s doesn't define a ready state.\n", ti->Name+1);
+			if (!defaults->UpState) SC_ScriptError("Weapon %s doesn't define a select state.\n", ti->Name+1);
+			if (!defaults->DownState) SC_ScriptError("Weapon %s doesn't define a deselect state.\n", ti->Name+1);
+			if (!defaults->AtkState) SC_ScriptError("Weapon %s doesn't define an attack state.\n", ti->Name+1);
+
+			// If the weapon doesn't define a hold state use the attack state instead.
+			if (!defaults->HoldAtkState) defaults->HoldAtkState=defaults->AtkState;
+			if (!defaults->AltHoldAtkState) defaults->AltHoldAtkState=defaults->AltAtkState;
+
+		}
+		// same for the weapon type of weapon pieces.
+		else if (ti->IsDescendantOf(RUNTIME_CLASS(AWeaponPiece)))
+		{
+			AWeaponPiece * defaults=(AWeaponPiece *)ti->ActorInfo->Defaults;
+			int v;
+
+			v=(int) defaults->WeaponClass;
+			if (v>0 && v<=StringTypes.Size())
+			{
+				defaults->WeaponClass = TypeInfo::FindType(StringTypes[v-1]);
+				if (!defaults->WeaponClass)
+				{
+					SC_ScriptError("Unknown weapon type '%s' in '%s'\n", StringTypes[v], ti->Name+1);
+				}
+				else if (defaults->WeaponClass->ParentType!=RUNTIME_CLASS(AWeapon))
+				{
+					SC_ScriptError("Invalid weapon type '%s' in '%s'\n", StringTypes[v], ti->Name+1);
+				}
+			}
+		}
+	}
+	for(i=0;i<StringTypes.Size();i++)
+	{
+		delete StringTypes[i];
+	}
+	StringTypes.Clear();
+	StringTypes.ShrinkToFit();
 }
 
 
diff -durN 96/src/thingdef_codeptr.cpp 96x/src/thingdef_codeptr.cpp
--- 96/src/thingdef_codeptr.cpp	1970-01-01 01:00:00.000000000 +0100
+++ 96x/src/thingdef_codeptr.cpp	2005-07-28 15:11:52.000000000 +0200
@@ -0,0 +1,1467 @@
+/*
+** thingdef.cpp
+**
+** Code pointers for Actor definitions
+**
+**---------------------------------------------------------------------------
+** Copyright 2002-2005 Christoph Oelckers
+** Copyright 2004-2005 Randy Heit
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+**
+** 1. Redistributions of source code must retain the above copyright
+**    notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+**    notice, this list of conditions and the following disclaimer in the
+**    documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+**    derived from this software without specific prior written permission.
+** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be
+**    covered by the terms of the GNU General Public License as published by
+**    the Free Software Foundation; either version 2 of the License, or (at
+**    your option) any later version.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**---------------------------------------------------------------------------
+**
+*/
+
+#include "gi.h"
+#include "actor.h"
+#include "info.h"
+#include "sc_man.h"
+#include "tarray.h"
+#include "w_wad.h"
+#include "templates.h"
+#include "r_defs.h"
+#include "r_draw.h"
+#include "a_pickups.h"
+#include "s_sound.h"
+#include "cmdlib.h"
+#include "p_lnspec.h"
+#include "p_enemy.h"
+#include "a_action.h"
+#include "decallib.h"
+#include "m_random.h"
+#include "autosegs.h"
+#include "i_system.h"
+#include "p_local.h"
+#include "c_console.h"
+#include "doomerrors.h"
+#include "vectors.h"
+#include "a_sharedglobal.h"
+#include "a_doomglobal.h"
+
+
+static FRandom pr_camissile ("CustomActorfire");
+static FRandom pr_camelee ("CustomMelee");
+static FRandom pr_cabullet ("CustomBullet");
+static FRandom pr_cajump ("CustomJump");
+static FRandom pr_cwbullet ("CustomWpBullet");
+static FRandom pr_cwjump ("CustomWpJump");
+static FRandom pr_cwpunch ("CustomWpPunch");
+static FRandom pr_grenade ("ThrowGrenade");
+static FRandom pr_crailgun ("CustomRailgun");
+static FRandom pr_spawndebris ("SpawnDebris");
+static FRandom pr_jiggle ("Jiggle");
+
+
+int EvalExpressionI (int id, AActor *self);
+float EvalExpressionF (int id, AActor *self);
+
+
+// A truly awful hack to get to the state that called an action function
+// without knowing whether it has been called from a weapon or actor.
+FState * CallingState;
+
+struct StateCallData
+{
+	FState * State;
+	bool Result;
+};
+
+StateCallData StateCall;
+
+//==========================================================================
+//
+// Executes the code pointers in a chain of states
+// until there is no next state
+//
+//==========================================================================
+
+bool CallStateChain(AActor * actor, FState * State)
+{
+	bool result = false;
+	int counter=0;
+
+	StateCall.State = State;
+	while (StateCall.State !=NULL)
+	{
+		// Assume success. The code pointer will set this to false if necessary
+		StateCall.Result = true;	
+		CallingState = StateCall.State;
+		StateCall.State->GetAction() (actor);
+
+		// collect all the results. Even one successful call signifies overall success.
+		result |= StateCall.Result;
+
+		// Since there are no delays it is a good idea to check for infinite loops here!
+		counter++;
+		if (counter>=10000)	break;
+
+		if (StateCall.State == CallingState) 
+		{
+			// Abort immediately if the state jumps to itself!
+			if (StateCall.State == StateCall.State->GetNextState()) return false;
+			
+			// If both variables are still the same there was no jump
+			// so we must advance to the next state.
+			StateCall.State = StateCall.State->GetNextState();
+		}
+	}
+	return result;
+}
+
+//==========================================================================
+//
+// Let's isolate all handling of CallingState in this one place 
+// so that removing it later becomes easier
+//
+//==========================================================================
+int CheckIndex(int paramsize, FState ** pcallstate)
+{
+	if (!(CallingState->Frame&SF_STATEPARAM)) return -1;
+
+	size_t index = CallingState->GetMisc1_2();
+	if (index > StateParameters.Size()-paramsize-2) return -1;
+	if (pcallstate) *pcallstate=CallingState;
+	return index+2;	// skip the misc parameters
+}
+
+
+//==========================================================================
+//
+// Simple flag changers
+//
+//==========================================================================
+void A_SetSolid(AActor * self)
+{
+	self->flags |= MF_SOLID;
+}
+
+void A_UnsetSolid(AActor * self)
+{
+	self->flags &= ~MF_SOLID;
+}
+
+void A_SetFloat(AActor * self)
+{
+	self->flags |= MF_FLOAT;
+}
+
+void A_UnsetFloat(AActor * self)
+{
+	self->flags &= ~(MF_FLOAT|MF_INFLOAT);
+}
+
+//==========================================================================
+//
+// Customizable attack functions which use actor parameters.
+// I think this is among the most requested stuff ever ;-)
+//
+//==========================================================================
+static void DoAttack (AActor *self, bool domelee, bool domissile)
+{
+	int index=CheckIndex(4);
+
+	if (index<0) return;
+	if (self->target == NULL) return;
+
+	int MeleeDamage=StateParameters[index];
+	int MeleeSound=StateParameters[index+1];
+	const char *MissileName=(const char *)StateParameters[index+2];
+	fixed_t MissileHeight=StateParameters[index+3];
+
+	A_FaceTarget (self);
+	if (domelee && MeleeDamage>0 && self->CheckMeleeRange ())
+	{
+		int damage = pr_camelee.HitDice(MeleeDamage);
+		if (MeleeSound) S_SoundID (self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM);
+		P_DamageMobj (self->target, self, self, damage, MOD_HIT);
+		P_TraceBleed (damage, self->target, self);
+	}
+	else if (domissile && MissileName)
+	{
+		const TypeInfo * ti=TypeInfo::FindType(MissileName);
+		if (ti) 
+		{
+			// Although there is a P_SpawnMissileZ function its
+			// aiming is much too bad to be of any use
+			self->z+=MissileHeight-32*FRACUNIT;
+			AActor * missile = P_SpawnMissile (self, self->target, ti);
+			self->z-=MissileHeight-32*FRACUNIT;
+
+			if (missile)
+			{
+				// automatic handling of seeker missiles
+				if (missile->flags2&MF2_SEEKERMISSILE)
+				{
+					missile->tracer=self->target;
+				}
+				// set the health value so that the missile works properly
+				if (missile->flags4&MF4_SPECTRAL)
+				{
+					missile->health=-2;
+				}
+			}
+		}
+	}
+}
+
+void A_MeleeAttack(AActor * self)
+{
+	DoAttack(self, true, false);
+}
+
+void A_MissileAttack(AActor * self)
+{
+	DoAttack(self, false, true);
+}
+
+void A_ComboAttack(AActor * self)
+{
+	DoAttack(self, true, true);
+}
+
+//==========================================================================
+//
+// Custom sound functions. These use misc1 and misc2 in the state structure
+// This has been changed to use the parameter array instead of using the
+// misc field directly so they can be used in weapon states
+//
+//==========================================================================
+static void DoPlaySound(AActor * self, int channel)
+{
+	int index=CheckIndex(1);
+	if (index<0) return;
+
+	int soundid = StateParameters[index];
+	S_SoundID (self, channel, soundid, 1, ATTN_NORM);
+}
+
+void A_PlaySound(AActor * self)
+{
+	DoPlaySound(self, CHAN_BODY);
+}
+
+void A_PlayWeaponSound(AActor * self)
+{
+	DoPlaySound(self, CHAN_WEAPON);
+}
+
+void A_StopSound(AActor * self)
+{
+	S_StopSound(self, CHAN_VOICE);
+}
+
+//==========================================================================
+//
+// Generic seeker missile function
+//
+//==========================================================================
+void A_SeekerMissile(AActor * self)
+{
+	int index=CheckIndex(2);
+	if (index<0) return;
+
+	P_SeekerMissile(self,
+		clamp<int>(EvalExpressionI (StateParameters[index], self), 0, 90) * ANGLE_1,
+		clamp<int>(EvalExpressionI (StateParameters[index+1], self), 0, 90) * ANGLE_1);
+}
+
+//==========================================================================
+//
+// Hitscan attack with a customizable amount of bullets (specified in damage)
+//
+//==========================================================================
+void A_BulletAttack (AActor *self)
+{
+	int i;
+	int bangle;
+	int slope;
+		
+	if (!self->target) return;
+
+	A_FaceTarget (self);
+	bangle = self->angle;
+
+	slope = P_AimLineAttack (self, bangle, MISSILERANGE);
+
+	S_SoundID (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM);
+	for (i=0 ; i<self->damage ; i++)
+    {
+		int angle = bangle + (pr_cabullet.Random2() << 20);
+		int damage = ((pr_cabullet()%5)+1)*3;
+		P_LineAttack(self, angle, MISSILERANGE, slope, damage,
+			GetDefaultByType(RUNTIME_CLASS(ABulletPuff))->DamageType, RUNTIME_CLASS(ABulletPuff));
+    }
+}
+
+//==========================================================================
+//
+// Do the state jump
+//
+//==========================================================================
+static void DoJump(AActor * self, FState * CallingState, int offset)
+{
+	if (!self->player) 
+	{
+		self->SetState (CallingState + offset);
+	}
+	else if (CallingState == self->player->psprites[ps_weapon].state)
+	{
+		P_SetPsprite(self->player, ps_weapon, CallingState + offset);
+	}
+	else if (CallingState == self->player->psprites[ps_flash].state)
+	{
+		P_SetPsprite(self->player, ps_flash, CallingState + offset);
+	}
+	else if (CallingState == StateCall.State)
+	{
+		StateCall.State += offset;
+	}
+	StateCall.Result=false;	// Jumps should never set the result for inventory state chains!
+}
+//==========================================================================
+//
+// State jump function
+//
+//==========================================================================
+void A_Jump(AActor * self)
+{
+	FState * CallingState;
+	int index=CheckIndex(2, &CallingState);
+
+	if (index>=0 && pr_cajump() < clamp<int>(EvalExpressionI (StateParameters[index], self), 0, 255))
+		DoJump(self, CallingState, StateParameters[index+1]);
+}
+
+//==========================================================================
+//
+// State jump function
+//
+//==========================================================================
+void A_JumpIfHealthLower(AActor * self)
+{
+	FState * CallingState;
+	int index=CheckIndex(2, &CallingState);
+
+	if (index>=0 && self->health < EvalExpressionI (StateParameters[index], self))
+		DoJump(self, CallingState, StateParameters[index+1]);
+}
+
+//==========================================================================
+//
+// State jump function
+//
+//==========================================================================
+void A_JumpIfCloser(AActor * self)
+{
+	FState * CallingState;
+	int index=CheckIndex(2, &CallingState);
+	AActor * target;
+
+	if (!self->player)
+	{
+		target=self->target;
+	}
+	else
+	{				
+		// Does the player aim at something that can be shot?
+		P_BulletSlope(self);
+		target = linetarget;
+	}
+
+	// No target - no jump
+	if (target==NULL) return;
+
+	fixed_t dist = EvalExpressionF (StateParameters[index], self) * FRACUNIT;
+	if (index>0 && P_AproxDistance(self->x-self->target->x, self->y-self->target->y) < dist)
+		DoJump(self, CallingState, StateParameters[index+1]);
+}
+
+//==========================================================================
+//
+// State jump function
+//
+//==========================================================================
+void A_JumpIfInventory(AActor * self)
+{
+	FState * CallingState;
+	int index=CheckIndex(3, &CallingState);
+	if (index<0) return;
+
+	const char * ItemType=(const char *)StateParameters[index];
+	int ItemAmount = EvalExpressionI (StateParameters[index+1], self);
+	int JumpOffset = StateParameters[index+2];
+	const TypeInfo * Type=TypeInfo::FindType(ItemType);
+
+	if (!Type) return;
+
+	AInventory * Item=self->FindInventory(Type);
+
+	if (Item)
+	{
+		if (ItemAmount>0 && Item->Amount>=ItemAmount) DoJump(self, CallingState, JumpOffset);
+		else if (Item->Amount>=Item->MaxAmount) DoJump(self, CallingState, JumpOffset);
+	}
+}
+
+
+//==========================================================================
+//
+// Parameterized version of A_Explode
+//
+//==========================================================================
+
+void A_ExplodeParms (AActor *self)
+{
+	int damage = 128;
+	int distance = 128;
+	bool hurtSource = true;
+
+	int index=CheckIndex(3);
+	if (index>=0) 
+	{
+		if (StateParameters[index] != 0)
+		{
+			damage = StateParameters[index];
+		}
+		if (StateParameters[index+1] != 0)
+		{
+			distance = StateParameters[index+1];
+		}
+		hurtSource = !!StateParameters[index+2];
+	}
+
+	P_RadiusAttack (self, self->target, damage, distance, self->DamageType, hurtSource);
+	if (self->z <= self->floorz + (distance<<FRACBITS))
+	{
+		P_HitFloor (self);
+	}
+}
+
+
+//==========================================================================
+//
+// Execute a line special / script
+//
+//==========================================================================
+void A_CallSpecial(AActor * self)
+{
+	int index=CheckIndex(6);
+	if (index<0) return;
+
+	StateCall.Result = !!LineSpecials[StateParameters[index]](NULL, self, false,
+		EvalExpressionI (StateParameters[index+1], self),
+		EvalExpressionI (StateParameters[index+2], self),
+		EvalExpressionI (StateParameters[index+3], self),
+		EvalExpressionI (StateParameters[index+4], self),
+		EvalExpressionI (StateParameters[index+5], self));
+}
+
+//==========================================================================
+//
+// The ultimate code pointer: Fully customizable missiles!
+//
+//==========================================================================
+void A_CustomMissile(AActor * self)
+{
+	int index=CheckIndex(6);
+	if (index<0) return;
+
+	const char * MissileName=(const char*)StateParameters[index];
+	fixed_t SpawnHeight=EvalExpressionF (StateParameters[index+1], self) * FRACUNIT;
+	int Spawnofs_XY=EvalExpressionI (StateParameters[index+2], self);
+	angle_t Angle=EvalExpressionF (StateParameters[index+3], self) * ANGLE_1;
+	int aimmode=EvalExpressionI (StateParameters[index+4], self);
+	angle_t pitch=EvalExpressionF (StateParameters[index+5], self) * ANGLE_1;
+
+	AActor * targ;
+	AActor * missile;
+
+	if (self->target != NULL || aimmode==2)
+	{
+		const TypeInfo * ti=TypeInfo::FindType(MissileName);
+		if (ti) 
+		{
+			angle_t ang = (self->angle - ANGLE_90) >> ANGLETOFINESHIFT;
+			fixed_t x = Spawnofs_XY * finecosine[ang];
+			fixed_t y = Spawnofs_XY * finesine[ang];
+			fixed_t z = SpawnHeight-32*FRACUNIT;
+
+			switch (aimmode)
+			{
+			case 0:
+				// same adjustment as above (in all 3 directions this time) - for better aiming!
+				self->x+=x;
+				self->y+=y;
+				self->z+=z;
+				missile = P_SpawnMissile(self, self->target, ti);
+				self->x-=x;
+				self->y-=y;
+				self->z-=z;
+				break;
+
+			case 1:
+				missile = P_SpawnMissileXYZ(self->x+x, self->y+y, self->z+SpawnHeight, self, self->target, ti);
+				break;
+
+			case 2:
+				missile = P_SpawnMissileAngleZ(self, self->z+SpawnHeight, ti, self->angle, 0);
+
+				// It is not necessary to use the correct angle here.
+				// The only important thing is that the horizontal momentum is correct.
+				// Therefore use 0 as the missile's angle and simplify the calculations accordingly.
+				// The actual momentum vector is set below.
+				if (missile)
+				{
+					fixed_t vx = finecosine[pitch>>ANGLETOFINESHIFT];
+					fixed_t vz = finesine[pitch>>ANGLETOFINESHIFT];
+
+					missile->momx = FixedMul (vx, missile->Speed);
+					missile->momy = 0;
+					missile->momz = FixedMul (vz, missile->Speed);
+				}
+
+				break;
+			}
+
+			if (missile)
+			{
+				// Use the actual momentum instead of the missile's Speed property
+				// so that this can handle missiles with a high vertical velocity 
+				// component properly.
+				vec3_t velocity = { missile->momx, missile->momy, 0 };
+
+				fixed_t missilespeed=(fixed_t)VectorLength(velocity);
+
+				missile->angle += Angle;
+				ang = missile->angle >> ANGLETOFINESHIFT;
+				missile->momx = FixedMul (missilespeed, finecosine[ang]);
+				missile->momy = FixedMul (missilespeed, finesine[ang]);
+	
+				// handle projectile shooting projectiles - track the
+				// links back to a real owner
+                if (self->flags&MF_MISSILE)
+                {
+                	AActor * owner=self ;//->target;
+                	while (owner->flags&MF_MISSILE && owner->target) owner=owner->target;
+                	targ=owner;
+                	missile->target=owner;
+					// automatic handling of seeker missiles
+					if (self->flags & missile->flags2 & MF2_SEEKERMISSILE)
+					{
+						missile->tracer=self->tracer;
+					}
+                }
+				else if (missile->flags2&MF2_SEEKERMISSILE)
+				{
+					// automatic handling of seeker missiles
+					missile->tracer=self->target;
+				}
+				// set the health value so that the missile works properly
+				if (missile->flags4&MF4_SPECTRAL)
+				{
+					missile->health=-2;
+				}
+			}
+		}
+	}
+}
+
+//==========================================================================
+//
+// An even more customizable hitscan attack
+//
+//==========================================================================
+void A_CustomBulletAttack (AActor *self)
+{
+	int index=CheckIndex(6);
+	if (index<0) return;
+
+	angle_t Spread_XY=EvalExpressionF (StateParameters[index], self) * ANGLE_1;
+	angle_t Spread_Z=EvalExpressionF (StateParameters[index+1], self) * ANGLE_1;
+	int NumBullets=EvalExpressionI (StateParameters[index+2], self);
+	int DamagePerBullet=EvalExpressionI (StateParameters[index+3], self);
+	const char * PuffType=(const char *)StateParameters[index+4];
+	fixed_t Range = EvalExpressionF (StateParameters[index+5], self) * FRACUNIT;
+
+	if(Range==0) Range=MISSILERANGE;
+
+
+	int i;
+	int bangle;
+	int bslope;
+	const TypeInfo *pufftype;
+
+	if (self->target)
+	{
+		A_FaceTarget (self);
+		bangle = self->angle;
+
+		pufftype = TypeInfo::FindType(PuffType);
+		if (!pufftype) pufftype=RUNTIME_CLASS(ABulletPuff);
+
+		bslope = P_AimLineAttack (self, bangle, MISSILERANGE);
+
+		S_SoundID (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM);
+		for (i=0 ; i<NumBullets ; i++)
+		{
+			int angle = bangle + pr_cabullet.Random2() * (Spread_XY / 255);
+			int slope = bslope + pr_cabullet.Random2() * (Spread_Z / 255);
+			int damage = ((pr_cabullet()%3)+1) * DamagePerBullet;
+			P_LineAttack(self, angle, Range, slope, damage, GetDefaultByType(pufftype)->DamageType, pufftype);
+		}
+    }
+}
+
+//==========================================================================
+//
+// State jump function
+//
+//==========================================================================
+void A_JumpIfNoAmmo(AActor * self)
+{
+	FState * CallingState;
+	int index=CheckIndex(1, &CallingState);
+	if (index<0 || !self->player || !self->player->ReadyWeapon) return;	// only for weapons!
+
+	if (!self->player->ReadyWeapon->CheckAmmo(self->player->ReadyWeapon->bAltFire, false, true))
+		DoJump(self, CallingState, StateParameters[index]);
+}
+
+
+//==========================================================================
+//
+// An even more customizable hitscan attack
+//
+//==========================================================================
+void A_FireBullets (AActor *self)
+{
+	int index=CheckIndex(7);
+	if (index<0 || !self->player) return;
+
+	angle_t Spread_XY=EvalExpressionF (StateParameters[index], self) * ANGLE_1;
+	angle_t Spread_Z=EvalExpressionF (StateParameters[index+1], self) * ANGLE_1;
+	int NumberOfBullets=EvalExpressionI (StateParameters[index+2], self);
+	int DamagePerBullet=EvalExpressionI (StateParameters[index+3], self);
+	const char * PuffTypeName=(const char *)StateParameters[index+4];
+	bool UseNoAmmo=!EvalExpressionI (StateParameters[index+5], self);
+	fixed_t Range=EvalExpressionF (StateParameters[index+6], self) * FRACUNIT; 
+	
+	const TypeInfo * PuffType;
+
+	player_t * player=self->player;
+	AWeapon * weapon=player->ReadyWeapon;
+
+	int i;
+	int bangle;
+	int bslope;
+
+	if (!UseNoAmmo && weapon)
+	{
+		if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return;	// out of ammo
+	}
+	
+	if (Range == 0) Range = PLAYERMISSILERANGE;
+
+	static_cast<APlayerPawn *>(self)->PlayAttacking2 ();
+
+	P_BulletSlope(self);
+	bangle = self->angle;
+	bslope = bulletpitch;
+
+	PuffType = TypeInfo::FindType(PuffTypeName);
+	if (!PuffType) PuffType=RUNTIME_CLASS(ABulletPuff);
+
+	S_SoundID (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM);
+
+	if ((NumberOfBullets==1 && !player->refire) || NumberOfBullets==0)
+	{
+		int damage = ((pr_cwbullet()%3)+1)*DamagePerBullet;
+		P_LineAttack(self, bangle, Range, bslope, damage, GetDefaultByType(PuffType)->DamageType, PuffType);
+	}
+	else 
+	{
+		if (NumberOfBullets == -1) NumberOfBullets = 1;
+		for (i=0 ; i<NumberOfBullets ; i++)
+		{
+			int angle = bangle + pr_cwbullet.Random2() * (Spread_XY / 255);
+			int slope = bslope + pr_cwbullet.Random2() * (Spread_Z / 255);
+			int damage = ((pr_cwbullet()%3)+1) * DamagePerBullet;
+			P_LineAttack(self, angle, Range, slope, damage, GetDefaultByType(PuffType)->DamageType, PuffType);
+		}
+	}
+}
+
+
+//==========================================================================
+//
+// A_FireProjectile
+//
+//==========================================================================
+void A_FireCustomMissile (AActor * self)
+{
+	int index=CheckIndex(5);
+	if (index<0 || !self->player) return;
+
+	const char * MissileName=(const char *)StateParameters[index];
+	angle_t Angle=EvalExpressionF (StateParameters[index+1], self) * ANGLE_1;
+	bool UseNoAmmo=!EvalExpressionI (StateParameters[index+2], self);
+	int SpawnOfs_XY=EvalExpressionI (StateParameters[index+3], self);
+	fixed_t SpawnHeight=EvalExpressionF (StateParameters[index+4], self) * FRACUNIT;
+
+	player_t *player=self->player;
+	AWeapon * weapon=player->ReadyWeapon;
+
+	if (!UseNoAmmo && weapon)
+	{
+		if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return;	// out of ammo
+	}
+
+	const TypeInfo * ti=TypeInfo::FindType(MissileName);
+	if (ti) 
+	{
+		angle_t ang = (self->angle - ANGLE_90) >> ANGLETOFINESHIFT;
+		fixed_t x = SpawnOfs_XY * finecosine[ang];
+		fixed_t y = SpawnOfs_XY * finesine[ang];
+		fixed_t z = SpawnHeight;
+
+		AActor * misl=P_SpawnPlayerMissile (self, self->x+x, self->y+y, self->z+z, ti, self->angle);
+		// automatic handling of seeker missiles
+		if (misl)
+		{
+			vec3_t velocity = { misl->momx, misl->momy, 0 };
+			fixed_t missilespeed=(fixed_t)VectorLength(velocity);
+
+			if (linetarget && misl->flags2&MF2_SEEKERMISSILE) misl->tracer=linetarget;
+			misl->angle += Angle;
+			angle_t an = misl->angle >> ANGLETOFINESHIFT;
+			misl->momx = FixedMul (missilespeed, finecosine[an]);
+			misl->momy = FixedMul (missilespeed, finesine[an]);
+			if (misl->flags4&MF4_SPECTRAL) misl->health=-1;
+		}
+	}
+}
+
+
+//==========================================================================
+//
+// A_CustomPunch
+//
+// Berserk is not handled here. That can be done with A_CheckIfInventory
+//
+//==========================================================================
+void A_CustomPunch (AActor *self)
+{
+	int index=CheckIndex(5);
+	if (index<0 || !self->player) return;
+
+	int Damage=EvalExpressionI (StateParameters[index], self);
+	bool norandom=!!EvalExpressionI (StateParameters[index+1], self);
+	bool UseNoAmmo=!EvalExpressionI (StateParameters[index+2], self);
+	const char * PuffTypeName=(const char *)StateParameters[index+3];
+	fixed_t Range=EvalExpressionF (StateParameters[index+4], self) * FRACUNIT; 
+
+	const TypeInfo * PuffType;
+
+
+	player_t *player=self->player;
+	AWeapon * weapon=player->ReadyWeapon;
+
+
+	angle_t 	angle;
+	int 		pitch;
+
+	if (!norandom) Damage *= (pr_cwpunch()%8+1);
+
+	angle = self->angle + (pr_cwpunch.Random2() << 18);
+	pitch = P_AimLineAttack (self, angle, MELEERANGE);
+
+	// only use ammo when actually hitting something!
+	if (!UseNoAmmo && linetarget && weapon)
+	{
+		if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return;	// out of ammo
+	}
+
+	PuffType = TypeInfo::FindType(PuffTypeName);
+	if (!PuffType) PuffType=RUNTIME_CLASS(ABulletPuff);
+
+	if (Range == 0) Range = MELEERANGE;
+	P_LineAttack (self, angle, Range, pitch, Damage, GetDefaultByType(PuffType)->DamageType, PuffType);
+
+	// turn to face target
+	if (linetarget)
+	{
+		S_SoundID (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM);
+
+		self->angle = R_PointToAngle2 (self->x,
+										self->y,
+										linetarget->x,
+										linetarget->y);
+	}
+}
+
+
+//==========================================================================
+//
+// customizable railgun attack function
+//
+//==========================================================================
+void A_RailAttack (AActor * self)
+{
+	int index=CheckIndex(7);
+	if (index<0 || !self->player) return;
+
+	int Damage=EvalExpressionI (StateParameters[index], self);
+	int Spawnofs_XY=EvalExpressionI (StateParameters[index+1], self);
+	bool UseNoAmmo=!EvalExpressionI (StateParameters[index+2], self);
+	int Color1=StateParameters[index+3];
+	int Color2=StateParameters[index+4];
+	bool Silent=!EvalExpressionI (StateParameters[index+5], self);
+	float MaxDiff=EvalExpressionF (StateParameters[index+6], self);
+
+	AWeapon * weapon=self->player->ReadyWeapon;
+
+	// only use ammo when actually hitting something!
+	if (!UseNoAmmo)
+	{
+		if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return;	// out of ammo
+	}
+
+	// Need to keep the default colors
+	if (Color1==0) Color1 = ColorMatcher.Pick (0, 0, 255);
+	else if (Color1>0) Color1--;
+
+	if (Color2==0) Color2 = ColorMatcher.Pick (255, 255, 255);
+	else if (Color2>0) Color2--;
+
+	P_RailAttack (self, Damage, Spawnofs_XY, Color1, Color2, MaxDiff, Silent);
+}
+
+//==========================================================================
+//
+// also for monsters
+//
+//==========================================================================
+
+void A_CustomRailgun (AActor *actor)
+{
+	if (!actor->target)
+		return;
+
+	int index=CheckIndex(7);
+	if (index<0) return;
+
+	int Damage=EvalExpressionI (StateParameters[index], actor);
+	int Spawnofs_XY=EvalExpressionI (StateParameters[index+1], actor);
+	int Color1=StateParameters[index+2];
+	int Color2=StateParameters[index+3];
+	bool Silent=!EvalExpressionI (StateParameters[index+4], actor);
+	bool aim=!EvalExpressionI (StateParameters[index+5], actor);
+	float MaxDiff=EvalExpressionF (StateParameters[index+6], actor);
+
+	// [RH] Andy Baker's stealth monsters
+	if (actor->flags & MF_STEALTH)
+	{
+		actor->visdir = 1;
+	}
+
+	actor->flags &= ~MF_AMBUSH;
+		
+	if (aim)
+	{
+		actor->angle = R_PointToAngle2 (actor->x,
+										actor->y,
+										actor->target->x,
+										actor->target->y);
+	}
+
+	actor->pitch = P_AimLineAttack (actor, actor->angle, MISSILERANGE);
+
+	// Let the aim trail behind the player
+	if (aim)
+	{
+		actor->angle = R_PointToAngle2 (actor->x,
+										actor->y,
+										actor->target->x - actor->target->momx * 3,
+										actor->target->y - actor->target->momy * 3);
+
+		if (actor->target->flags & MF_SHADOW)
+		{
+			actor->angle += pr_crailgun.Random2() << 21;
+		}
+	}
+
+	// Need to keep the default colors
+	if (Color1==0) Color1 = ColorMatcher.Pick (0, 0, 255);
+	else if (Color1>0) Color1--;
+
+	if (Color2==0) Color2 = ColorMatcher.Pick (255, 255, 255);
+	else if (Color2>0) Color2--;
+
+	P_RailAttack (actor, Damage, Spawnofs_XY, Color1, Color2, MaxDiff, Silent);
+}
+
+//===========================================================================
+//
+// A_GiveInventory
+//
+//===========================================================================
+
+void A_GiveInventory(AActor * self)
+{
+	int index=CheckIndex(2);
+	if (index<0) return;
+
+	const char * item =(const char*)StateParameters[index];
+	int amount=EvalExpressionI (StateParameters[index+1], self);
+
+	if (amount==0) amount=1;
+	const TypeInfo * mi=TypeInfo::FindType(item);
+	if (mi) 
+	{
+		AInventory *item = static_cast<AInventory *>(Spawn (mi, 0, 0, 0));
+		item->Amount = amount;
+		item->flags |= MF_DROPPED;
+		if (item->flags & MF_COUNTITEM)
+		{
+			item->flags&=~MF_COUNTITEM;
+			level.total_items--;
+		}
+		if (!item->DoTryPickup (self))
+		{
+			item->Destroy ();
+			StateCall.Result = false;
+		}
+		else StateCall.Result = true;
+	}
+	else StateCall.Result = false;
+}	
+
+//===========================================================================
+//
+// A_GiveInventory
+//
+//===========================================================================
+
+void A_TakeInventory(AActor * self)
+{
+	int index=CheckIndex(2);
+	if (index<0) return;
+
+	const char * item =(const char*)StateParameters[index];
+	int amount=EvalExpressionI (StateParameters[index+1], self);
+
+	const TypeInfo * mi=TypeInfo::FindType(item);
+
+	StateCall.Result=false;
+	if (mi) 
+	{
+		AInventory * inv = self->FindInventory(mi);
+
+		if (inv && !inv->IsKindOf(RUNTIME_CLASS(AHexenArmor)))
+		{
+			if (inv->Amount > 0) StateCall.Result=true;
+			if (!amount || amount>=inv->Amount) 
+			{
+				if (inv->IsKindOf(RUNTIME_CLASS(AAmmo))) inv->Amount=0;
+				else inv->Destroy();
+			}
+			else inv->Amount-=amount;
+		}
+	}
+}	
+
+
+//===========================================================================
+//
+// A_SpawnItem
+//
+// Spawns an item in front of the caller like Heretic's time bomb
+//
+//===========================================================================
+void A_SpawnItem(AActor * self)
+{
+	FState * CallingState;
+	int index=CheckIndex(4, &CallingState);
+	if (index<0) return;
+
+	const TypeInfo * missile= TypeInfo::FindType((const char *)StateParameters[index]);
+	int distance = EvalExpressionI (StateParameters[index+1], self);
+	fixed_t zheight = EvalExpressionF (StateParameters[index+2], self) * FRACUNIT;
+	bool useammo = !EvalExpressionI (StateParameters[index+3], self);
+
+	if (!missile) 
+	{
+		StateCall.Result=false;
+		return;
+	}
+
+	if (distance==0) 
+	{
+		// use the minimum distance that does not result in an overlap
+		distance=(self->radius+GetDefaultByType(missile)->radius)>>FRACBITS;
+	}
+
+	if (self->player && CallingState != self->state && CallingState != StateCall.State)
+	{
+		// Used from a weapon so use some ammo
+		AWeapon * weapon=self->player->ReadyWeapon;
+
+		if (!weapon) return;
+		if (useammo && !weapon->DepleteAmmo(weapon->bAltFire)) return;
+	}
+
+	AActor * mo = Spawn( missile, 
+					self->x + distance*finecosine[self->angle>>ANGLETOFINESHIFT], 
+					self->y + distance*finesine[self->angle>>ANGLETOFINESHIFT], 
+					self->z - self->floorclip + zheight);
+
+	mo->angle=self->angle;
+	if (mo) 
+	{
+		AActor * originator = self;
+
+		while (originator && originator->flags&MF_MISSILE) originator = originator->target;
+
+		if (mo->flags3&MF3_ISMONSTER)
+		{
+			if (!P_TestMobjLocation(mo))
+			{
+				// The monster is blocked so don't spawn it at all!
+				if (mo->flags&MF_COUNTKILL) level.total_monsters--;
+				mo->Destroy();
+				StateCall.Result=false;	// for an inventory iten's use state
+				return;
+			}
+			else if (originator)
+			{
+				if (originator->flags3&MF3_ISMONSTER)
+				{
+					// If this is a monster transfer all friendliness information
+					mo->target = originator->target;
+					mo->TIDtoHate = originator->TIDtoHate;
+					mo->LastLook = originator->LastLook;
+					mo->flags3 |= originator->flags3 & (MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS);
+					mo->flags4 |= originator->flags4 & MF4_NOHATEPLAYERS;
+					mo->flags = (mo->flags & ~MF_FRIENDLY) | (originator->flags & MF_FRIENDLY);
+					// Note to Randy: This line is missing from all other friendliness transfers!
+					mo->FriendPlayer=originator->FriendPlayer;
+
+					if (useammo) mo->master = originator;	// don't let it attack you (optional)!
+				}
+				else if (originator->player)
+				{
+					// A player always spawns a monster friendly to him
+					mo->flags|=MF_FRIENDLY;
+					mo->FriendPlayer = originator->player-players+1;
+
+					AActor * attacker=originator->player->attacker;
+					if (attacker)
+					{
+						if (!(attacker->flags&MF_FRIENDLY) || 
+							(deathmatch && attacker->FriendPlayer!=0 && attacker->FriendPlayer!=mo->FriendPlayer))
+						{
+							// Target the monster which last attacked the player
+							mo->target = attacker;
+						}
+					}
+				}
+			}
+		}
+		else 
+		{
+			// If this is a missile or something else set the target to the originator
+			mo->target=originator? originator : self;
+		}
+	}
+	StateCall.Result=true;
+}
+
+//===========================================================================
+//
+// A_ThrowGrenade
+//
+// Throws a grenade (like Hexen's fighter flechette)
+//
+//===========================================================================
+void A_ThrowGrenade(AActor * self)
+{
+	FState * CallingState;
+	int index=CheckIndex(5, &CallingState);
+	if (index<0) return;
+
+	const TypeInfo * missile= TypeInfo::FindType((const char *)StateParameters[index]);
+	fixed_t zheight = EvalExpressionF (StateParameters[index+1], self) * FRACUNIT;
+	fixed_t xymom = EvalExpressionF (StateParameters[index+2], self) * FRACUNIT;
+	fixed_t zmom = EvalExpressionF (StateParameters[index+3], self) * FRACUNIT;
+	bool useammo = !EvalExpressionI (StateParameters[index+4], self);
+
+	if (self->player && CallingState != self->state && CallingState != StateCall.State)
+	{
+		// Used from a weapon so use some ammo
+		AWeapon * weapon=self->player->ReadyWeapon;
+
+		if (!weapon) return;
+		if (useammo && !weapon->DepleteAmmo(weapon->bAltFire)) return;
+	}
+
+
+	AActor * bo;
+
+	bo = Spawn(missile, self->x, self->y, self->z - self->floorclip + zheight + 35*FRACUNIT);
+	if (bo)
+	{
+		int pitch = self->pitch;
+
+		if (xymom) bo->Speed=xymom;
+		bo->angle = self->angle+(((pr_grenade()&7)-4)<<24);
+		bo->momz = zmom + 2*finesine[pitch>>ANGLETOFINESHIFT];
+		bo->z += 2 * finesine[pitch>>ANGLETOFINESHIFT];
+		P_ThrustMobj(bo, bo->angle, bo->Speed);
+		bo->momx += self->momx>>1;
+		bo->momy += self->momy>>1;
+		bo->target= self;
+		bo->tics -= pr_grenade()&3;
+		P_CheckMissileSpawn (bo);
+		StateCall.Result=true;
+	} 
+	else StateCall.Result=false;
+}
+
+
+//===========================================================================
+//
+// A_Recoil
+//
+//===========================================================================
+void A_Recoil(AActor * actor)
+{
+	int index=CheckIndex(1, NULL);
+	if (index<0) return;
+	fixed_t xymom = EvalExpressionF (StateParameters[index], actor) * FRACUNIT;
+
+	angle_t angle = actor->angle + ANG180;
+	angle >>= ANGLETOFINESHIFT;
+	actor->momx += FixedMul (xymom, finecosine[angle]);
+	actor->momy += FixedMul (xymom, finesine[angle]);
+}
+
+
+//===========================================================================
+//
+// A_SelectWeapon
+//
+//===========================================================================
+void A_SelectWeapon(AActor * actor)
+{
+	int index=CheckIndex(1, NULL);
+	if (index<0 || actor->player == NULL) return;
+
+	const TypeInfo * weapon= TypeInfo::FindType((const char *)StateParameters[index]);
+	AWeapon * weaponitem = static_cast<AWeapon*>(actor->FindInventory(weapon));
+
+	if (weaponitem != NULL && weaponitem->IsKindOf(RUNTIME_CLASS(AWeapon)))
+	{
+		if (actor->player->ReadyWeapon != weaponitem)
+		{
+			actor->player->PendingWeapon = weaponitem;
+		}
+		StateCall.Result=true;
+	}
+	else StateCall.Result=false;
+}
+
+//===========================================================================
+//
+// A_Print
+//
+//===========================================================================
+void A_Print(AActor * actor)
+{
+	int index=CheckIndex(1, NULL);
+	if (index<0) return;
+
+	if (actor->CheckLocalView (consoleplayer) ||
+		(actor->target!=NULL && actor->target->CheckLocalView (consoleplayer)))
+	{
+		C_MidPrint((const char *)StateParameters[index]);
+	}
+}
+
+
+//===========================================================================
+//
+// A_SetTranslucent
+//
+//===========================================================================
+void A_SetTranslucent(AActor * self)
+{
+	int index=CheckIndex(2, NULL);
+	if (index<0) return;
+
+	fixed_t alpha = EvalExpressionF (StateParameters[index], self) * FRACUNIT;
+	int mode = EvalExpressionI (StateParameters[index+1], self);
+	mode = mode == 0 ? STYLE_Translucent : mode == 2 ? STYLE_Fuzzy : STYLE_Add;
+
+	self->alpha=clamp<fixed_t>(alpha, 0, FRACUNIT);
+
+	if (mode != STYLE_Fuzzy)
+	{
+		if (self->alpha == 0) mode = STYLE_None;
+		else if (mode == STYLE_Translucent && self->alpha == FRACUNIT) mode = STYLE_Normal;
+	}
+
+	self->RenderStyle=mode;
+}
+
+//===========================================================================
+//
+// A_FadeOut
+//
+// fades the actor out and destroys it when done
+//
+//===========================================================================
+void A_FadeOut(AActor * self)
+{
+	int index=CheckIndex(1, NULL);
+	if (index<0) return;
+
+	fixed_t reduce = EvalExpressionF (StateParameters[index], self) * FRACUNIT;
+	if (reduce == 0) reduce = FRACUNIT/10;
+
+	if (self->RenderStyle==STYLE_Normal) self->RenderStyle=STYLE_Translucent;
+	self->alpha -= reduce;
+	if (self->alpha<=0) self->Destroy();
+}
+
+//===========================================================================
+//
+// A_SpawnDebris
+//
+//===========================================================================
+void A_SpawnDebris(AActor * self)
+{
+	int i;
+	AActor * mo;
+	const TypeInfo * debris;
+
+	int index=CheckIndex(1, NULL);
+	if (index<0) return;
+
+	debris = TypeInfo::FindType((const char *)StateParameters[index]);
+	if (debris == NULL) return;
+
+	for (i = 0; i < GetDefaultByType(debris)->health; i++)
+	{
+		mo = Spawn(debris, self->x+((pr_spawndebris()-128)<<12),
+			self->y+((pr_spawndebris()-128)<<12), 
+			self->z+(pr_spawndebris()*self->height/256));
+		if (mo && i < mo->GetClass()->ActorInfo->NumOwnedStates)
+		{
+			mo->SetState (mo->GetClass()->ActorInfo->OwnedStates + i);
+			mo->momz = ((pr_spawndebris()&7)+5)*FRACUNIT;
+			mo->momx = pr_spawndebris.Random2()<<(FRACBITS-6);
+			mo->momy = pr_spawndebris.Random2()<<(FRACBITS-6);
+		}
+	}
+}
+
+
+//===========================================================================
+//
+// A_CheckSight
+// jumps if no player can see this actor
+//
+//===========================================================================
+void A_CheckSight(AActor * self)
+{
+	for (int i=0;i<MAXPLAYERS;i++) 
+	{
+		if (playeringame[i] && P_CheckSight(players[i].camera,self,true)) return;
+	}
+
+	FState * CallingState;
+	int index=CheckIndex(1, &CallingState);
+
+	if (index>=0) DoJump(self, CallingState, StateParameters[index]);
+}
+
+
+//===========================================================================
+//
+// A_ExtChase
+// A_Chase with optional parameters
+//
+//===========================================================================
+void A_DoChase(AActor * actor, bool fastchase, FState * meleestate, FState * missilestate, bool playactive, bool nightmarefast);
+
+void A_ExtChase(AActor * self)
+{
+	int index=CheckIndex(4, &CallingState);
+
+	A_DoChase(self, false,
+		EvalExpressionI (StateParameters[index], self) ? self->MeleeState:NULL,
+		EvalExpressionI (StateParameters[index+1], self) ? self->MissileState:NULL,
+		!EvalExpressionI (StateParameters[index+2], self),
+		!!EvalExpressionI (StateParameters[index+3], self));
+}
+
+
+//===========================================================================
+//
+// Weapon jiggling
+//
+//===========================================================================
+void A_Jiggle(AActor * self)
+{
+	int index=CheckIndex(2, &CallingState);
+	int xmax = EvalExpressionI (StateParameters[index], self);
+	int ymax = EvalExpressionI (StateParameters[index+1], self);
+
+	if (self->player)
+	{
+		int rand_x = (pr_jiggle()%(xmax*2))-xmax;
+		int rand_y = (pr_jiggle()%(ymax*2))-ymax;
+		self->player->psprites[0].sx += rand_x;
+		self->player->psprites[0].sy += rand_y;
+		self->player->psprites[1].sx += rand_x;
+		self->player->psprites[1].sy += rand_y;
+	}
+}
+
+//===========================================================================
+//
+// Inventory drop
+//
+//===========================================================================
+void A_DropInventory(AActor * self)
+{
+	int index=CheckIndex(1, &CallingState);
+	const TypeInfo * ti = TypeInfo::FindType((const char*)StateParameters[index]);
+	if (ti)
+	{
+		AInventory * inv = self->FindInventory(ti);
+		if (inv)
+		{
+			self->DropInventory(inv);
+		}
+	}
+}
+
+
+//===========================================================================
+//
+// A_SetBlend
+//
+//===========================================================================
+void A_SetBlend(AActor * self)
+{
+	int index=CheckIndex(3);
+	int colorn = StateParameters[index];
+
+	PalEntry color;
+	if (!colorn)
+		return;
+	else if (colorn == -1)
+		color.r = color.g = color.b = 255;
+	else
+		color = GPalette.BaseColors[colorn];
+
+	fixed_t alpha = EvalExpressionF (StateParameters[index+1], self) * FRACUNIT;
+	int tics = EvalExpressionI (StateParameters[index+2], self);
+
+
+	new DFlashFader(color.r/255.0f, color.g/255.0f, color.b/255.0f, alpha/(float)FRACUNIT,
+					color.r/255.0f, color.g/255.0f, color.b/255.0f, 0, tics, self);
+}
+
+
+//===========================================================================
+//
+// A_JUmpIf
+//
+//===========================================================================
+void A_JumpIf(AActor * self)
+{
+	FState * CallingState;
+	int index=CheckIndex(2, &CallingState);
+	int expression = EvalExpressionI (StateParameters[index], self);
+
+	if (index>=0 && expression) DoJump(self, CallingState, StateParameters[index+1]);
+}
+
+//===========================================================================
+//
+// A_SetUserVar
+//
+//===========================================================================
+void A_SetUserVar(AActor * self)
+{
+	int index=CheckIndex(2);
+	int var = StateParameters[index];
+	int expression = EvalExpressionI (StateParameters[index+1], self);
+
+	if (var >= 0 && var < 8)
+	{
+		self->user[var] = expression;
+	}
+}
+
+//===========================================================================
+//
+// A_KillMaster
+//
+//===========================================================================
+void A_KillMaster(AActor * self)
+{
+	if (self->master)
+	{
+		P_DamageMobj(self->master, self, self, self->master->health, MOD_UNKNOWN, DMG_NO_ARMOR);
+	}
+}
+
+//===========================================================================
+//
+// A_KillChildren
+//
+//===========================================================================
+void A_KillChildren(AActor * self)
+{
+	TThinkerIterator<AActor> it;
+	AActor * mo;
+
+	while (mo=it.Next())
+	{
+		if (mo->master == self)
+		{
+			P_DamageMobj(mo, self, self, mo->health, MOD_UNKNOWN, DMG_NO_ARMOR);
+		}
+	}
+}
+
+
diff -durN 96/src/thingdef_exp.cpp 96x/src/thingdef_exp.cpp
--- 96/src/thingdef_exp.cpp	1970-01-01 01:00:00.000000000 +0100
+++ 96x/src/thingdef_exp.cpp	2005-07-29 12:09:09.000000000 +0200
@@ -0,0 +1,1408 @@
+/*
+** thingdef_exp.cpp
+**
+** Expression parsing / runtime evaluating support
+**
+**---------------------------------------------------------------------------
+** Copyright 2005 Jan Cholasta
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+**
+** 1. Redistributions of source code must retain the above copyright
+**    notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+**    notice, this list of conditions and the following disclaimer in the
+**    documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+**    derived from this software without specific prior written permission.
+** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be
+**    covered by the terms of the GNU General Public License as published by
+**    the Free Software Foundation; either version 2 of the License, or (at
+**    your option) any later version.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**---------------------------------------------------------------------------
+**
+*/
+
+#include "actor.h"
+#include "sc_man.h"
+#include "tarray.h"
+#include "templates.h"
+#include "vectors.h"
+#include "cmdlib.h"
+#include "i_system.h"
+#include "m_random.h"
+
+static FRandom pr_exrandom ("EX_Random");
+
+BOOL SC_CheckNumber (void);
+BOOL SC_CheckFloat (void);
+
+enum ExpOp
+{
+	EX_NOP,
+
+	EX_Const,
+	EX_Var,
+
+	EX_Compl,		// ~exp
+	EX_Not,			// !exp
+//	EX_Plus,		// +exp
+	EX_Minus,		// -exp
+	EX_Mul,			// exp * exp
+	EX_Div,			// exp / exp
+	EX_Mod,			// exp % exp
+	EX_Add,			// exp + exp
+	EX_Sub,			// exp - exp
+	EX_LShift,		// exp << exp
+	EX_RShift,		// exp >> exp
+	EX_LT,			// exp < exp
+	EX_GT,			// exp > exp
+	EX_LE,			// exp <= exp
+	EX_GE,			// exp >= exp
+	EX_Eq,			// exp == exp
+	EX_NE,			// exp != exp
+	EX_And,			// exp & exp
+	EX_Xor,			// exp ^ exp
+	EX_Or,			// exp | exp
+	EX_LogAnd,		// exp && exp
+	EX_LogOr,		// exp || exp
+	EX_Cond,		// exp ? exp : exp
+
+	EX_Random,		// random (min, max)
+	EX_Sin,			// sin (angle)
+	EX_Cos,			// cos (angle)
+};
+
+enum ExpValType
+{
+	VAL_Int,
+	VAL_Float,
+};
+
+struct ExpVal
+{
+	ExpValType Type;
+	union
+	{
+		int Int;
+		float Float;
+	};
+};
+
+typedef ExpVal (*ExpVarGet) (AActor *, int);
+
+ExpVal GetAlpha (AActor *actor, int id)
+{
+	ExpVal val;
+	val.Type = VAL_Float;
+	val.Float = FIXED2FLOAT (actor->alpha);
+	return val;
+}
+
+ExpVal GetAngle (AActor *actor, int id)
+{
+	ExpVal val;
+	val.Type = VAL_Float;
+	val.Float = (float)actor->angle / ANGLE_1;
+	return val;
+}
+
+ExpVal GetArgs (AActor *actor, int id)
+{
+	ExpVal val;
+	val.Type = VAL_Int;
+	val.Int = actor->args[id];
+	return val;
+}
+
+ExpVal GetCeilingZ (AActor *actor, int id)
+{
+	ExpVal val;
+	val.Type = VAL_Float;
+	val.Float = FIXED2FLOAT (actor->ceilingz);
+	return val;
+}
+
+ExpVal GetFloorZ (AActor *actor, int id)
+{
+	ExpVal val;
+	val.Type = VAL_Float;
+	val.Float = FIXED2FLOAT (actor->floorz);
+	return val;
+}
+
+ExpVal GetHealth (AActor *actor, int id)
+{
+	ExpVal val;
+	val.Type = VAL_Int;
+	val.Int = actor->health;
+	return val;
+}
+
+ExpVal GetPitch (AActor *actor, int id)
+{
+	ExpVal val;
+	val.Type = VAL_Float;
+	val.Float = (float)actor->pitch / ANGLE_1;
+	return val;
+}
+
+ExpVal GetSpecial (AActor *actor, int id)
+{
+	ExpVal val;
+	val.Type = VAL_Int;
+	val.Int = actor->special;
+	return val;
+}
+
+ExpVal GetTID (AActor *actor, int id)
+{
+	ExpVal val;
+	val.Type = VAL_Int;
+	val.Int = actor->tid;
+	return val;
+}
+
+ExpVal GetTIDToHate (AActor *actor, int id)
+{
+	ExpVal val;
+	val.Type = VAL_Int;
+	val.Int = actor->TIDtoHate;
+	return val;
+}
+
+ExpVal GetUser (AActor *actor, int id)
+{
+	ExpVal val;
+	val.Type = VAL_Int;
+	val.Int = actor->user[id];
+	return val;
+}
+
+ExpVal GetWaterLevel (AActor *actor, int id)
+{
+	ExpVal val;
+	val.Type = VAL_Int;
+	val.Int = actor->waterlevel;
+	return val;
+}
+
+ExpVal GetX (AActor *actor, int id)
+{
+	ExpVal val;
+	val.Type = VAL_Float;
+	val.Float = FIXED2FLOAT (actor->x);
+	return val;
+}
+
+ExpVal GetY (AActor *actor, int id)
+{
+	ExpVal val;
+	val.Type = VAL_Float;
+	val.Float = FIXED2FLOAT (actor->y);
+	return val;
+}
+
+ExpVal GetZ (AActor *actor, int id)
+{
+	ExpVal val;
+	val.Type = VAL_Float;
+	val.Float = FIXED2FLOAT (actor->z);
+	return val;
+}
+
+static struct
+{
+	const char *name;	// identifier
+	int array;			// array size (0 if not an array)
+	ExpVarGet get;
+} ExpVars[] = {
+	{ "alpha",		0, GetAlpha },
+	{ "angle",		0, GetAngle },
+	{ "args",		5, GetArgs },
+	{ "ceilingz",	0, GetCeilingZ },
+	{ "floorz",		0, GetFloorZ },
+	{ "health",		0, GetHealth },
+	{ "pitch",		0, GetPitch },
+	{ "special",	0, GetSpecial },
+	{ "tid",		0, GetTID },
+	{ "tidtohate",	0, GetTIDToHate },
+	{ "user",		8, GetUser },
+	{ "waterlevel",	0, GetWaterLevel },
+	{ "x",			0, GetX },
+	{ "y",			0, GetY },
+	{ "z",			0, GetZ },
+};
+
+struct ExpData;
+static ExpVal EvalExpression (ExpData *data, AActor *self);
+
+struct ExpData
+{
+	ExpData ()
+	{
+		Type = EX_NOP;
+		Value.Type = VAL_Int;
+		Value.Int = 0;
+		for (int i = 0; i < 3; i++)
+			Children[i] = NULL;
+	}
+	~ExpData ()
+	{
+		for (int i = 0; i < 3; i++)
+			if (Children[i])
+				delete Children[i];
+	}
+	// Try to evaluate constant expression
+	void EvalConst ()
+	{
+		if (Type == EX_NOP || Type == EX_Const || Type == EX_Var)
+		{
+			return;
+		}
+		else if (Type == EX_Compl || Type == EX_Not || /*Type == EX_Plus || */Type == EX_Minus)
+		{
+			if (Children[0]->Type == EX_Const)
+			{
+				Type = EX_Const;
+				Value = EvalExpression (this, NULL);
+			}
+		}
+		else if (Type == EX_Cond)
+		{
+			if (Children[0]->Type == EX_Const)
+			{
+				bool cond = (Children[0]->Value.Type == VAL_Int) ? (Children[0]->Value.Int != 0) : (Children[0]->Value.Float != 0);
+				ExpData *data = Children[cond ? 1 : 2];
+				delete Children[cond ? 2 : 1];
+
+				Type = data->Type;
+				Value = data->Value;
+				for (int i = 0; i < 3; i++)
+				{
+					Children[i] = data->Children[i];
+					data->Children[i] = NULL;
+				}
+
+				delete data;
+			}
+		}
+		else if (Type != EX_Random)
+		{
+			if ((Type == EX_Div || Type == EX_Mod) &&
+				Children[1]->Type == EX_Const && Children[1]->Value.Int == 0)
+			{
+				I_FatalError ("Division by zero");
+			}
+			else if (Children[0]->Type == EX_Const && Children[1]->Type == EX_Const)
+			{
+				Type = EX_Const;
+				Value = EvalExpression (this, NULL);
+				delete Children[0];
+				delete Children[1];
+			}
+		}
+	}
+	bool Compare (ExpData *other)
+	{
+		if (Type != other->Type ||
+			Value.Type != other->Value.Type ||
+			Value.Int != other->Value.Int)
+		{
+			return false;
+		}
+
+		for (int i = 0; i < 3; i++)
+		{
+			if (Children[i] && !Children[i]->Compare (other->Children[i]))
+				return false;
+		}
+
+		return true;
+	}
+
+	ExpOp Type;
+	ExpVal Value;
+	ExpData *Children[3];
+};
+
+TArray<ExpData *> StateExpressions;
+
+//
+// ParseExpression
+// [GRB] Parses an expression and stores it into Expression array
+//
+
+static ExpData *ParseExpressionM ();
+static ExpData *ParseExpressionL ();
+static ExpData *ParseExpressionK ();
+static ExpData *ParseExpressionJ ();
+static ExpData *ParseExpressionI ();
+static ExpData *ParseExpressionH ();
+static ExpData *ParseExpressionG ();
+static ExpData *ParseExpressionF ();
+static ExpData *ParseExpressionE ();
+static ExpData *ParseExpressionD ();
+static ExpData *ParseExpressionC ();
+static ExpData *ParseExpressionB ();
+static ExpData *ParseExpressionA ();
+
+int ParseExpression ()
+{
+	ExpData *data = ParseExpressionM ();
+
+	for (int i = 0; i < StateExpressions.Size (); i++)
+	{
+		if (StateExpressions[i]->Compare (data))
+		{
+			delete data;
+			return i;
+		}
+	}
+
+	return StateExpressions.Push (data);
+}
+
+static ExpData *ParseExpressionM ()
+{
+	ExpData *tmp = ParseExpressionL ();
+
+	if (SC_CheckString ("?"))
+	{
+		ExpData *data = new ExpData;
+		data->Type = EX_Cond;
+		data->Children[0] = tmp;
+		data->Children[1] = ParseExpressionM ();
+		if (!SC_CheckString (":"))
+			SC_ScriptError ("':' expected");
+		data->Children[2] = ParseExpressionM ();
+		data->EvalConst ();
+		return data;
+	}
+	else
+	{
+		return tmp;
+	}
+}
+
+static ExpData *ParseExpressionL ()
+{
+	ExpData *tmp = ParseExpressionK ();
+
+	if (SC_CheckString ("||"))
+	{
+		ExpData *data = new ExpData;
+		data->Type = EX_LogOr;
+		data->Children[0] = tmp;
+		data->Children[1] = ParseExpressionL ();
+		data->EvalConst ();
+		return data;
+	}
+	else
+	{
+		return tmp;
+	}
+}
+
+static ExpData *ParseExpressionK ()
+{
+	ExpData *tmp = ParseExpressionJ ();
+
+	if (SC_CheckString ("&&"))
+	{
+		ExpData *data = new ExpData;
+		data->Type = EX_LogAnd;
+		data->Children[0] = tmp;
+		data->Children[1] = ParseExpressionK ();
+		data->EvalConst ();
+		return data;
+	}
+	else
+	{
+		return tmp;
+	}
+}
+
+static ExpData *ParseExpressionJ ()
+{
+	ExpData *tmp = ParseExpressionI ();
+
+	if (SC_CheckString ("|"))
+	{
+		ExpData *data = new ExpData;
+		data->Type = EX_Or;
+		data->Children[0] = tmp;
+		data->Children[1] = ParseExpressionJ ();
+		data->EvalConst ();
+		return data;
+	}
+	else
+	{
+		return tmp;
+	}
+}
+
+static ExpData *ParseExpressionI ()
+{
+	ExpData *tmp = ParseExpressionH ();
+
+	if (SC_CheckString ("^"))
+	{
+		ExpData *data = new ExpData;
+		data->Type = EX_Xor;
+		data->Children[0] = tmp;
+		data->Children[1] = ParseExpressionI ();
+		data->EvalConst ();
+		return data;
+	}
+	else
+	{
+		return tmp;
+	}
+}
+
+static ExpData *ParseExpressionH ()
+{
+	ExpData *tmp = ParseExpressionG ();
+
+	if (SC_CheckString ("&"))
+	{
+		ExpData *data = new ExpData;
+		data->Type = EX_And;
+		data->Children[0] = tmp;
+		data->Children[1] = ParseExpressionH ();
+		data->EvalConst ();
+		return data;
+	}
+	else
+	{
+		return tmp;
+	}
+}
+
+static ExpData *ParseExpressionG ()
+{
+	ExpData *tmp = ParseExpressionF ();
+	ExpData *data = new ExpData;
+
+	if (SC_CheckString ("=="))
+	{
+		data->Type = EX_Eq;
+	}
+	else if (SC_CheckString ("=="))
+	{
+		data->Type = EX_NE;
+	}
+	else
+	{
+		delete data;
+		return tmp;
+	}
+
+	data->Children[0] = tmp;
+	data->Children[1] = ParseExpressionG ();
+	data->EvalConst ();
+
+	return data;
+}
+
+static ExpData *ParseExpressionF ()
+{
+	ExpData *tmp = ParseExpressionE ();
+	ExpData *data = new ExpData;
+
+	if (SC_CheckString ("<"))
+	{
+		data->Type = EX_LT;
+	}
+	else if (SC_CheckString (">"))
+	{
+		data->Type = EX_GT;
+	}
+	else if (SC_CheckString ("<="))
+	{
+		data->Type = EX_LE;
+	}
+	else if (SC_CheckString (">="))
+	{
+		data->Type = EX_GE;
+	}
+	else
+	{
+		delete data;
+		return tmp;
+	}
+
+	data->Children[0] = tmp;
+	data->Children[1] = ParseExpressionF ();
+	data->EvalConst ();
+
+	return data;
+}
+
+static ExpData *ParseExpressionE ()
+{
+	ExpData *tmp = ParseExpressionD ();
+	ExpData *data = new ExpData;
+
+	if (SC_CheckString ("<<"))
+	{
+		data->Type = EX_LShift;
+	}
+	else if (SC_CheckString (">>"))
+	{
+		data->Type = EX_RShift;
+	}
+	else
+	{
+		delete data;
+		return tmp;
+	}
+
+	data->Children[0] = tmp;
+	data->Children[1] = ParseExpressionE ();
+	data->EvalConst ();
+
+	return data;
+}
+
+static ExpData *ParseExpressionD ()
+{
+	ExpData *tmp = ParseExpressionC ();
+	ExpData *data = new ExpData;
+
+	if (SC_CheckString ("+"))
+	{
+		data->Type = EX_Add;
+	}
+	else if (SC_CheckString ("-"))
+	{
+		data->Type = EX_Sub;
+	}
+	else
+	{
+		delete data;
+		return tmp;
+	}
+
+	data->Children[0] = tmp;
+	data->Children[1] = ParseExpressionD ();
+	data->EvalConst ();
+
+	return data;
+}
+
+static ExpData *ParseExpressionC ()
+{
+	ExpData *tmp = ParseExpressionB ();
+	ExpData *data = new ExpData;
+
+	if (SC_CheckString ("*"))
+	{
+		data->Type = EX_Mul;
+	}
+	else if (SC_CheckString ("/"))
+	{
+		data->Type = EX_Div;
+	}
+	else if (SC_CheckString ("%"))
+	{
+		data->Type = EX_Mod;
+	}
+	else
+	{
+		delete data;
+		return tmp;
+	}
+
+	data->Children[0] = tmp;
+	data->Children[1] = ParseExpressionC ();
+	data->EvalConst ();
+
+	return data;
+}
+
+static ExpData *ParseExpressionB ()
+{
+	ExpData *data = new ExpData;
+
+	if (SC_CheckString ("~"))
+	{
+		data->Type = EX_Compl;
+	}
+	else if (SC_CheckString ("!"))
+	{
+		data->Type = EX_Not;
+	}
+	else if (SC_CheckString ("-"))
+	{
+		data->Type = EX_Minus;
+	}
+	else
+	{
+		SC_CheckString ("+");
+		delete data;
+		return ParseExpressionA ();
+	}
+
+	data->Children[0] = ParseExpressionA ();
+	data->EvalConst ();
+
+	return data;
+}
+
+static ExpData *ParseExpressionA ()
+{
+	if (SC_CheckString ("("))
+	{
+		ExpData *data = ParseExpressionM ();
+
+		if (!SC_CheckString (")"))
+			SC_ScriptError ("')' expected");
+
+		return data;
+	}
+	else if (SC_CheckString ("random"))
+	{
+		if (!SC_CheckString ("("))
+			SC_ScriptError ("'(' expected");
+
+		ExpData *data = new ExpData;
+		data->Type = EX_Random;
+
+		data->Children[0] = ParseExpressionM ();
+
+		if (!SC_CheckString (","))
+			SC_ScriptError ("',' expected");
+
+		data->Children[1] = ParseExpressionM ();
+
+		if (!SC_CheckString (")"))
+			SC_ScriptError ("')' expected");
+
+		return data;
+	}
+	else if (SC_CheckString ("sin"))
+	{
+		if (!SC_CheckString ("("))
+			SC_ScriptError ("'(' expected");
+
+		ExpData *data = new ExpData;
+		data->Type = EX_Sin;
+
+		data->Children[0] = ParseExpressionM ();
+
+		if (!SC_CheckString (")"))
+			SC_ScriptError ("')' expected");
+
+		return data;
+	}
+	else if (SC_CheckString ("cos"))
+	{
+		if (!SC_CheckString ("("))
+			SC_ScriptError ("'(' expected");
+
+		ExpData *data = new ExpData;
+		data->Type = EX_Cos;
+
+		data->Children[0] = ParseExpressionM ();
+
+		if (!SC_CheckString (")"))
+			SC_ScriptError ("')' expected");
+
+		return data;
+	}
+	else if (SC_CheckNumber ())
+	{
+		ExpData *data = new ExpData;
+		data->Type = EX_Const;
+		data->Value.Type = VAL_Int;
+		data->Value.Int = sc_Number;
+
+		return data;
+	}
+	else if (SC_CheckFloat ())
+	{
+		ExpData *data = new ExpData;
+		data->Type = EX_Const;
+		data->Value.Type = VAL_Float;
+		data->Value.Float = sc_Float;
+
+		return data;
+	}
+	else
+	{
+		SC_MustGetString ();
+
+		int varid = -1;
+		for (int i = 0; i < sizeof(ExpVars)/sizeof(ExpVars[0]); i++)
+		{
+			if (!stricmp (sc_String, ExpVars[i].name))
+			{
+				varid = i;
+				break;
+			}
+		}
+
+		if (varid == -1)
+			SC_ScriptError ("Value expected");
+
+		ExpData *data = new ExpData;
+		data->Type = EX_Var;
+		data->Value.Type = VAL_Int;
+		data->Value.Int = varid;
+
+		if (ExpVars[varid].array)
+		{
+			if (!SC_CheckString ("["))
+				SC_ScriptError ("'[' expected");
+
+			data->Children[0] = ParseExpressionM ();
+
+			if (!SC_CheckString ("]"))
+				SC_ScriptError ("']' expected");
+		}
+
+		return data;
+	}
+}
+
+//
+// EvalExpression
+// [GRB] Evaluates previously stored expression
+//
+
+int EvalExpressionI (int id, AActor *self)
+{
+	ExpVal val = EvalExpression (StateExpressions[id], self);
+
+	switch (val.Type)
+	{
+	default:
+	case VAL_Int:
+		return val.Int;
+	case VAL_Float:
+		return (int)val.Float;
+	}
+}
+
+float EvalExpressionF (int id, AActor *self)
+{
+	ExpVal val = EvalExpression (StateExpressions[id], self);
+
+	switch (val.Type)
+	{
+	default:
+	case VAL_Int:
+		return (float)val.Int;
+	case VAL_Float:
+		return val.Float;
+	}
+}
+
+static ExpVal EvalExpression (ExpData *data, AActor *self)
+{
+	ExpVal val;
+
+	switch (data->Type)
+	{
+	case EX_Const:
+		val = data->Value;
+		break;
+	case EX_Var:
+		if (!self)
+		{
+			// Should not happen
+			I_FatalError ("Missing actor data");
+		}
+		else
+		{
+			int id = 0;
+			if (ExpVars[data->Value.Int].array)
+			{
+				ExpVal idval = EvalExpression (data->Children[0], self);
+				id = ((idval.Type == VAL_Int) ? idval.Int : (int)idval.Float) % ExpVars[data->Value.Int].array;
+			}
+
+			val = ExpVars[data->Value.Int].get (self, id);
+		}
+		break;
+	case EX_Compl:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+
+			val.Type = VAL_Int;
+			val.Int = ~((a.Type == VAL_Int) ? a.Int : (int)a.Float);
+		}
+		break;
+	case EX_Not:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+
+			val.Type = VAL_Int;
+			val.Int = !((a.Type == VAL_Int) ? a.Int : (int)a.Float);
+		}
+		break;
+	case EX_Minus:
+		{
+			val = EvalExpression (data->Children[0], self);
+
+			if (val.Type == VAL_Int)
+				val.Int = -val.Int;
+			else
+				val.Float = -val.Float;
+		}
+		break;
+	case EX_Mul:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+			ExpVal b = EvalExpression (data->Children[1], self);
+
+			if (a.Type == VAL_Int)
+			{
+				if (b.Type == VAL_Int)
+				{
+					val.Type = VAL_Int;
+					val.Int = a.Int * b.Int;
+				}
+				else
+				{
+					val.Type = VAL_Float;
+					val.Float = a.Int * b.Float;
+				}
+			}
+			else
+			{
+				val.Type = VAL_Float;
+				if (b.Type == VAL_Int)
+					val.Float = a.Float * b.Int;
+				else
+					val.Float = a.Float * b.Float;
+			}
+		}
+		break;
+	case EX_Div:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+			ExpVal b = EvalExpression (data->Children[1], self);
+
+			if (a.Type == VAL_Int)
+			{
+				if (b.Type == VAL_Int)
+				{
+					val.Type = VAL_Int;
+					val.Int = a.Int / b.Int;
+				}
+				else
+				{
+					val.Type = VAL_Float;
+					val.Float = a.Int / b.Float;
+				}
+			}
+			else
+			{
+				val.Type = VAL_Float;
+				if (b.Type == VAL_Int)
+					val.Float = a.Float / b.Int;
+				else
+					val.Float = a.Float / b.Float;
+			}
+		}
+		break;
+	case EX_Mod:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+			ExpVal b = EvalExpression (data->Children[1], self);
+
+			if (a.Type == VAL_Int)
+			{
+				if (b.Type == VAL_Int)
+				{
+					val.Type = VAL_Int;
+					val.Int = a.Int % b.Int;
+				}
+				else
+				{
+					val.Type = VAL_Float;
+					val.Float = fmodf (a.Int, b.Float);
+				}
+			}
+			else
+			{
+				val.Type = VAL_Float;
+				if (b.Type == VAL_Int)
+					val.Float = fmodf (a.Float, b.Int);
+				else
+					val.Float = fmodf (a.Float, b.Float);
+			}
+		}
+		break;
+	case EX_Add:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+			ExpVal b = EvalExpression (data->Children[1], self);
+
+			if (a.Type == VAL_Int)
+			{
+				if (b.Type == VAL_Int)
+				{
+					val.Type = VAL_Int;
+					val.Int = a.Int + b.Int;
+				}
+				else
+				{
+					val.Type = VAL_Float;
+					val.Float = a.Int + b.Float;
+				}
+			}
+			else
+			{
+				val.Type = VAL_Float;
+				if (b.Type == VAL_Int)
+					val.Float = a.Float + b.Int;
+				else
+					val.Float = a.Float + b.Float;
+			}
+		}
+		break;
+	case EX_Sub:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+			ExpVal b = EvalExpression (data->Children[1], self);
+
+			if (a.Type == VAL_Int)
+			{
+				if (b.Type == VAL_Int)
+				{
+					val.Type = VAL_Int;
+					val.Int = a.Int - b.Int;
+				}
+				else
+				{
+					val.Type = VAL_Float;
+					val.Float = a.Int - b.Float;
+				}
+			}
+			else
+			{
+				val.Type = VAL_Float;
+				if (b.Type == VAL_Int)
+					val.Float = a.Float - b.Int;
+				else
+					val.Float = a.Float - b.Float;
+			}
+		}
+		break;
+	case EX_LShift:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+			ExpVal b = EvalExpression (data->Children[1], self);
+
+			val.Type = VAL_Int;
+
+			if (a.Type == VAL_Int)
+			{
+				if (b.Type == VAL_Int)
+					val.Int = a.Int << b.Int;
+				else
+					val.Int = a.Int << (int)b.Float;
+			}
+			else
+			{
+				if (b.Type == VAL_Int)
+					val.Int = (int)a.Float << b.Int;
+				else
+					val.Int = (int)a.Float << (int)b.Float;
+			}
+		}
+		break;
+	case EX_RShift:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+			ExpVal b = EvalExpression (data->Children[1], self);
+
+			val.Type = VAL_Int;
+
+			if (a.Type == VAL_Int)
+			{
+				if (b.Type == VAL_Int)
+					val.Int = a.Int >> b.Int;
+				else
+					val.Int = a.Int >> (int)b.Float;
+			}
+			else
+			{
+				if (b.Type == VAL_Int)
+					val.Int = (int)a.Float >> b.Int;
+				else
+					val.Int = (int)a.Float >> (int)b.Float;
+			}
+		}
+		break;
+	case EX_LT:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+			ExpVal b = EvalExpression (data->Children[1], self);
+
+			val.Type = VAL_Int;
+
+			if (a.Type == VAL_Int)
+			{
+				if (b.Type == VAL_Int)
+					val.Int = a.Int < b.Int;
+				else
+					val.Int = a.Int < b.Float;
+			}
+			else
+			{
+				if (b.Type == VAL_Int)
+					val.Int = a.Float < b.Int;
+				else
+					val.Int = a.Float < b.Float;
+			}
+		}
+		break;
+	case EX_GT:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+			ExpVal b = EvalExpression (data->Children[1], self);
+
+			val.Type = VAL_Int;
+
+			if (a.Type == VAL_Int)
+			{
+				if (b.Type == VAL_Int)
+					val.Int = a.Int > b.Int;
+				else
+					val.Int = a.Int > b.Float;
+			}
+			else
+			{
+				if (b.Type == VAL_Int)
+					val.Int = a.Float > b.Int;
+				else
+					val.Int = a.Float > b.Float;
+			}
+		}
+		break;
+	case EX_LE:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+			ExpVal b = EvalExpression (data->Children[1], self);
+
+			val.Type = VAL_Int;
+
+			if (a.Type == VAL_Int)
+			{
+				if (b.Type == VAL_Int)
+					val.Int = a.Int <= b.Int;
+				else
+					val.Int = a.Int <= b.Float;
+			}
+			else
+			{
+				if (b.Type == VAL_Int)
+					val.Int = a.Float <= b.Int;
+				else
+					val.Int = a.Float <= b.Float;
+			}
+		}
+		break;
+	case EX_GE:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+			ExpVal b = EvalExpression (data->Children[1], self);
+
+			val.Type = VAL_Int;
+
+			if (a.Type == VAL_Int)
+			{
+				if (b.Type == VAL_Int)
+					val.Int = a.Int >= b.Int;
+				else
+					val.Int = a.Int >= b.Float;
+			}
+			else
+			{
+				if (b.Type == VAL_Int)
+					val.Int = a.Float >= b.Int;
+				else
+					val.Int = a.Float >= b.Float;
+			}
+		}
+		break;
+	case EX_Eq:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+			ExpVal b = EvalExpression (data->Children[1], self);
+
+			val.Type = VAL_Int;
+
+			if (a.Type == VAL_Int)
+			{
+				if (b.Type == VAL_Int)
+					val.Int = a.Int == b.Int;
+				else
+					val.Int = a.Int == b.Float;
+			}
+			else
+			{
+				if (b.Type == VAL_Int)
+					val.Int = a.Float == b.Int;
+				else
+					val.Int = a.Float == b.Float;
+			}
+		}
+		break;
+	case EX_NE:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+			ExpVal b = EvalExpression (data->Children[1], self);
+
+			val.Type = VAL_Int;
+
+			if (a.Type == VAL_Int)
+			{
+				if (b.Type == VAL_Int)
+					val.Int = a.Int != b.Int;
+				else
+					val.Int = a.Int != b.Float;
+			}
+			else
+			{
+				if (b.Type == VAL_Int)
+					val.Int = a.Float != b.Int;
+				else
+					val.Int = a.Float != b.Float;
+			}
+		}
+		break;
+	case EX_And:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+			ExpVal b = EvalExpression (data->Children[1], self);
+
+			val.Type = VAL_Int;
+
+			if (a.Type == VAL_Int)
+			{
+				if (b.Type == VAL_Int)
+					val.Int = a.Int & b.Int;
+				else
+					val.Int = a.Int & (int)b.Float;
+			}
+			else
+			{
+				if (b.Type == VAL_Int)
+					val.Int = (int)a.Float & b.Int;
+				else
+					val.Int = (int)a.Float & (int)b.Float;
+			}
+		}
+		break;
+	case EX_Xor:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+			ExpVal b = EvalExpression (data->Children[1], self);
+
+			val.Type = VAL_Int;
+
+			if (a.Type == VAL_Int)
+			{
+				if (b.Type == VAL_Int)
+					val.Int = a.Int ^ b.Int;
+				else
+					val.Int = a.Int ^ (int)b.Float;
+			}
+			else
+			{
+				if (b.Type == VAL_Int)
+					val.Int = (int)a.Float ^ b.Int;
+				else
+					val.Int = (int)a.Float ^ (int)b.Float;
+			}
+		}
+		break;
+	case EX_Or:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+			ExpVal b = EvalExpression (data->Children[1], self);
+
+			val.Type = VAL_Int;
+
+			if (a.Type == VAL_Int)
+			{
+				if (b.Type == VAL_Int)
+					val.Int = a.Int | b.Int;
+				else
+					val.Int = a.Int | (int)b.Float;
+			}
+			else
+			{
+				if (b.Type == VAL_Int)
+					val.Int = (int)a.Float | b.Int;
+				else
+					val.Int = (int)a.Float | (int)b.Float;
+			}
+		}
+		break;
+	case EX_LogAnd:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+			ExpVal b = EvalExpression (data->Children[1], self);
+
+			val.Type = VAL_Int;
+
+			if (a.Type == VAL_Int)
+			{
+				if (b.Type == VAL_Int)
+					val.Int = a.Int && b.Int;
+				else
+					val.Int = a.Int && b.Float;
+			}
+			else
+			{
+				if (b.Type == VAL_Int)
+					val.Int = a.Float && b.Int;
+				else
+					val.Int = a.Float && b.Float;
+			}
+		}
+		break;
+	case EX_LogOr:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+			ExpVal b = EvalExpression (data->Children[1], self);
+
+			val.Type = VAL_Int;
+
+			if (a.Type == VAL_Int)
+			{
+				if (b.Type == VAL_Int)
+					val.Int = a.Int || b.Int;
+				else
+					val.Int = a.Int || b.Float;
+			}
+			else
+			{
+				if (b.Type == VAL_Int)
+					val.Int = a.Float || b.Int;
+				else
+					val.Int = a.Float || b.Float;
+			}
+		}
+		break;
+	case EX_Cond:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+
+			if (a.Type == VAL_Int)
+				val = a.Int ? EvalExpression (data->Children[1], self) : EvalExpression (data->Children[2], self);
+			else
+				val = a.Float ? EvalExpression (data->Children[1], self) : EvalExpression (data->Children[2], self);
+		}
+		break;
+
+	case EX_Random:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+			ExpVal b = EvalExpression (data->Children[1], self);
+
+			int min = (a.Type == VAL_Int) ? a.Int : (int)a.Float;
+			int max = (b.Type == VAL_Int) ? b.Int : (int)b.Float;
+
+			val.Type = VAL_Int;
+
+			if (max < min)
+			{
+				swap (max, min);
+			}
+
+			if (max - min > 255)
+			{
+				int num1,num2,num3,num4;
+
+				num1 = pr_exrandom();
+				num2 = pr_exrandom();
+				num3 = pr_exrandom();
+				num4 = pr_exrandom();
+
+				val.Int = ((num1 << 24) | (num2 << 16) | (num3 << 8) | num4);
+			}
+			else
+			{
+				val.Int = pr_exrandom();
+			}
+			val.Int %= (max - min + 1);
+			val.Int += min;
+		}
+		break;
+
+	case EX_Sin:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+			angle_t angle = ((a.Type == VAL_Int) ? (float)a.Int : a.Float) * ANGLE_1;
+
+			val.Type = VAL_Float;
+			val.Float = FIXED2FLOAT (finesine[angle>>ANGLETOFINESHIFT]);
+		}
+		break;
+
+	case EX_Cos:
+		{
+			ExpVal a = EvalExpression (data->Children[0], self);
+			angle_t angle = ((a.Type == VAL_Int) ? (float)a.Int : a.Float) * ANGLE_1;
+
+			val.Type = VAL_Float;
+			val.Float = FIXED2FLOAT (finecosine[angle>>ANGLETOFINESHIFT]);
+		}
+		break;
+	}
+
+	return val;
+}
+
+
+//
+// InitExpressions
+// [GRB] Set up expression data
+//
+
+void InitExpressions ()
+{
+	// StateExpressions[0] always is const 0;
+	ExpData *data = new ExpData;
+	data->Type = EX_Const;
+	data->Value.Type = VAL_Int;
+	data->Value.Int = 0;
+
+	StateExpressions.Push (data);
+}
+
+
+//
+// ClearExpressions
+// [GRB] Free all expression data
+//
+
+void STACK_ARGS ClearExpressions ()
+{
+	ExpData *data;
+
+	while (StateExpressions.Pop (data))
+		delete data;
+}
diff -durN 96/src/thingdef_specials.gperf 96x/src/thingdef_specials.gperf
--- 96/src/thingdef_specials.gperf	2004-03-08 16:43:38.000000000 +0100
+++ 96x/src/thingdef_specials.gperf	2005-06-23 11:44:18.000000000 +0200
@@ -77,7 +77,7 @@
 teleport_nofog,71,1,2
 teleport_newmap,74,2
 teleport_endgame,75,0
-thrustthing,72,2,3
+thrustthing,72,2,4
 damagething,73,1
 thing_activate,130,1
 thing_deactivate,131,1
@@ -168,4 +168,10 @@
 ceiling_raisetonearest,252,2
 ceiling_lowertolowest,253,2
 ceiling_lowertofloor,254,2
-ceiling_crushraiseandstaysila,255,4
\ Chybí znak konce øádku na konci souboru
+ceiling_crushraiseandstaysila,255,4
+door_animated,4,3
+clearforcefield,34,1
+teleport_zombiechanger,39,2
+acs_executewithresult,84,1,4
+plat_upnearestwaitdownstay,172,3
+noisealert,173,2
diff -durN 96/src/thingdef_specials.h 96x/src/thingdef_specials.h
--- 96/src/thingdef_specials.h	2004-10-27 10:22:29.000000000 +0200
+++ 96x/src/thingdef_specials.h	2005-06-23 11:50:32.000000000 +0200
@@ -1,10 +1,37 @@
 /* ANSI-C code produced by gperf version 3.0.1 */
-/* Command-line: gperf -tE -LANSI-C -Hspecialhash -Nis_special -C thingdef_specials.gperf  */
+/* Command-line: gperf -tE -LANSI-C -Hspecialhash -Nis_special -C --null-strings thingdef_specials.gperf  */
 /* Computed positions: -k'1,7,9,14,17' */
 
+#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
+      && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
+      && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
+      && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
+      && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
+      && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
+      && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
+      && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
+      && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
+      && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
+      && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
+      && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
+      && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
+      && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
+      && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
+      && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
+      && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
+      && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
+      && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
+      && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
+      && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
+      && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
+      && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
+/* The character set is not based on ISO-646.  */
+#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
+#endif
+
 #line 10 "thingdef_specials.gperf"
 struct ACSspecials { const char *name; unsigned char Special; unsigned char MinArgs; unsigned char MaxArgs; };
-/* maximum key range = 542, duplicates = 0 */
+/* maximum key range = 567, duplicates = 0 */
 
 #ifdef __GNUC__
 __inline
@@ -18,32 +45,32 @@
 {
   static const unsigned short asso_values[] =
     {
-      555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555,  60, 555,   0,  65,  20,
-      245,  10,  25,   0, 120, 155, 115,   0,   5, 185,
-       75,  70,  65, 555,   0,   5,   5, 110,  55,  50,
-        0,   5, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
-      555, 555, 555, 555, 555, 555
+      580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580,  60, 580,   0, 105,  20,
+      200,  10, 140,   0,  30, 155,  75,   0,   5, 165,
+       85,  70,  65, 580,   0,   5,   5,  65,  30,  10,
+        5,  30, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
+      580, 580, 580, 580, 580, 580
     };
   register int hval = len;
 
@@ -88,88 +115,88 @@
 {
   enum
     {
-      TOTAL_KEYWORDS = 160,
+      TOTAL_KEYWORDS = 166,
       MIN_WORD_LENGTH = 8,
       MAX_WORD_LENGTH = 29,
       MIN_HASH_VALUE = 13,
-      MAX_HASH_VALUE = 554
+      MAX_HASH_VALUE = 579
     };
 
   static const struct ACSspecials wordlist[] =
     {
-      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-      {""}, {""}, {""}, {""},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0},
 #line 76 "thingdef_specials.gperf"
       {"teleport",70,1},
-      {""}, {""}, {""}, {""},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
 #line 93 "thingdef_specials.gperf"
       {"teleportgroup",77,5},
-      {""}, {""},
+      {(char*)0}, {(char*)0},
 #line 88 "thingdef_specials.gperf"
       {"thing_spawn",135,3,4},
 #line 49 "thingdef_specials.gperf"
       {"light_strobe",116,5},
-      {""}, {""}, {""},
+      {(char*)0}, {(char*)0}, {(char*)0},
 #line 13 "thingdef_specials.gperf"
       {"acs_suspend",81,2},
-      {""},
+      {(char*)0},
 #line 145 "thingdef_specials.gperf"
       {"thing_setgoal",229,3},
-      {""}, {""},
+      {(char*)0}, {(char*)0},
 #line 160 "thingdef_specials.gperf"
       {"exit_secret",244,1},
 #line 107 "thingdef_specials.gperf"
       {"line_alignceiling",183,2},
-      {""},
+      {(char*)0},
 #line 82 "thingdef_specials.gperf"
       {"thing_activate",130,1},
-      {""}, {""},
+      {(char*)0}, {(char*)0},
 #line 126 "thingdef_specials.gperf"
       {"generic_lift",203,5},
-      {""}, {""}, {""}, {""},
-#line 114 "thingdef_specials.gperf"
-      {"setplayerproperty",191,3},
-      {""},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0},
 #line 127 "thingdef_specials.gperf"
       {"generic_stairs",204,5},
 #line 105 "thingdef_specials.gperf"
       {"thing_settranslation",180,2},
-      {""},
+      {(char*)0},
 #line 99 "thingdef_specials.gperf"
       {"thing_spawnfacing",139,2,4},
-      {""}, {""}, {""},
-#line 89 "thingdef_specials.gperf"
-      {"thing_spawnnofog",137,3,4},
-      {""}, {""}, {""},
+      {(char*)0}, {(char*)0},
+#line 102 "thingdef_specials.gperf"
+      {"thing_hate",177,2,3},
+      {(char*)0}, {(char*)0}, {(char*)0},
+#line 91 "thingdef_specials.gperf"
+      {"ceiling_waggle",38,5},
 #line 170 "thingdef_specials.gperf"
       {"ceiling_lowertofloor",254,2},
-      {""},
+      {(char*)0},
 #line 168 "thingdef_specials.gperf"
       {"ceiling_raisetonearest",252,2},
-#line 123 "thingdef_specials.gperf"
-      {"generic_floor",200,5},
-      {""}, {""},
+      {(char*)0}, {(char*)0}, {(char*)0},
 #line 16 "thingdef_specials.gperf"
       {"ceiling_crushandraise",42,3},
 #line 119 "thingdef_specials.gperf"
       {"ceiling_crushandraisea",196,4},
-      {""}, {""},
+      {(char*)0}, {(char*)0},
 #line 128 "thingdef_specials.gperf"
       {"generic_crusher",205,5},
 #line 18 "thingdef_specials.gperf"
       {"ceiling_lowerandcrush",43,3},
-      {""},
+      {(char*)0},
 #line 120 "thingdef_specials.gperf"
       {"ceiling_crushandraisesilenta",197,4},
-      {""},
+      {(char*)0},
 #line 21 "thingdef_specials.gperf"
       {"ceiling_crushraiseandstay",45,3},
 #line 118 "thingdef_specials.gperf"
       {"ceiling_crushraiseandstaya",195,4},
-      {""}, {""},
+      {(char*)0}, {(char*)0},
 #line 171 "thingdef_specials.gperf"
       {"ceiling_crushraiseandstaysila",255,4},
-      {""}, {""},
+      {(char*)0}, {(char*)0},
 #line 15 "thingdef_specials.gperf"
       {"acs_lockedexecute",83,5},
 #line 135 "thingdef_specials.gperf"
@@ -182,12 +209,12 @@
       {"teleport_endgame",75,0},
 #line 141 "thingdef_specials.gperf"
       {"scroll_floor",223,4},
-      {""}, {""},
+      {(char*)0}, {(char*)0},
 #line 47 "thingdef_specials.gperf"
       {"light_glow",114,4},
-      {""},
-#line 90 "thingdef_specials.gperf"
-      {"floor_waggle",138,5},
+#line 12 "thingdef_specials.gperf"
+      {"acs_execute",80,1,5},
+      {(char*)0},
 #line 92 "thingdef_specials.gperf"
       {"teleportother",76,3},
 #line 142 "thingdef_specials.gperf"
@@ -196,337 +223,389 @@
       {"light_stop",117,1},
 #line 134 "thingdef_specials.gperf"
       {"sector_setdamage",214,3},
-      {""}, {""},
-#line 91 "thingdef_specials.gperf"
-      {"ceiling_waggle",38,5},
+#line 114 "thingdef_specials.gperf"
+      {"setplayerproperty",191,3},
+      {(char*)0}, {(char*)0},
 #line 108 "thingdef_specials.gperf"
       {"line_alignfloor",184,2},
 #line 148 "thingdef_specials.gperf"
       {"light_strobedoom",232,3},
 #line 139 "thingdef_specials.gperf"
       {"sector_setcurrent",220,4},
-      {""},
+      {(char*)0},
 #line 133 "thingdef_specials.gperf"
       {"sector_setfade",213,4},
-#line 144 "thingdef_specials.gperf"
-      {"plat_raiseandstaytx0",228,2},
-      {""},
+#line 177 "thingdef_specials.gperf"
+      {"noisealert",173,2},
+      {(char*)0},
 #line 112 "thingdef_specials.gperf"
       {"sector_setceilingscale",188,5},
-      {""},
+      {(char*)0},
 #line 110 "thingdef_specials.gperf"
       {"sector_setceilingpanning",186,5},
-#line 20 "thingdef_specials.gperf"
-      {"ceiling_raisebyvalue",41,3},
-      {""}, {""}, {""}, {""},
-#line 19 "thingdef_specials.gperf"
-      {"ceiling_lowerbyvalue",40,3},
-#line 121 "thingdef_specials.gperf"
-      {"ceiling_raisebyvaluetimes8",198,3},
-      {""}, {""},
-#line 31 "thingdef_specials.gperf"
-      {"floor_lowertolowest",21,2},
-      {""},
-#line 122 "thingdef_specials.gperf"
-      {"ceiling_lowerbyvaluetimes8",199,3},
-      {""},
-#line 157 "thingdef_specials.gperf"
-      {"floor_lowertolowesttxty",241,2},
+      {(char*)0}, {(char*)0},
+#line 143 "thingdef_specials.gperf"
+      {"acs_executealways",226,1,5},
+      {(char*)0}, {(char*)0}, {(char*)0},
+#line 175 "thingdef_specials.gperf"
+      {"acs_executewithresult",84,1,4},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0},
 #line 45 "thingdef_specials.gperf"
       {"light_changetovalue",112,2},
-      {""},
+      {(char*)0},
 #line 169 "thingdef_specials.gperf"
       {"ceiling_lowertolowest",253,2},
-      {""}, {""}, {""},
-#line 42 "thingdef_specials.gperf"
-      {"light_forcelightning",109,1},
-      {""},
+      {(char*)0}, {(char*)0}, {(char*)0},
+#line 144 "thingdef_specials.gperf"
+      {"plat_raiseandstaytx0",228,2},
+      {(char*)0},
 #line 17 "thingdef_specials.gperf"
       {"ceiling_crushstop",44,1},
-      {""}, {""},
+      {(char*)0}, {(char*)0},
+#line 173 "thingdef_specials.gperf"
+      {"clearforcefield",34,1},
+      {(char*)0}, {(char*)0},
+#line 44 "thingdef_specials.gperf"
+      {"light_lowerbyvalue",111,2},
+      {(char*)0}, {(char*)0}, {(char*)0},
+#line 70 "thingdef_specials.gperf"
+      {"radius_quake",120,5},
+      {(char*)0}, {(char*)0},
 #line 124 "thingdef_specials.gperf"
       {"generic_ceiling",201,5},
-#line 12 "thingdef_specials.gperf"
-      {"acs_execute",80,1,5},
-      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 102 "thingdef_specials.gperf"
-      {"thing_hate",177,2,3},
-      {""}, {""}, {""},
+      {(char*)0}, {(char*)0}, {(char*)0},
 #line 59 "thingdef_specials.gperf"
       {"plat_stop",61,1},
-      {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 20 "thingdef_specials.gperf"
+      {"ceiling_raisebyvalue",41,3},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+#line 19 "thingdef_specials.gperf"
+      {"ceiling_lowerbyvalue",40,3},
+#line 121 "thingdef_specials.gperf"
+      {"ceiling_raisebyvaluetimes8",198,3},
 #line 136 "thingdef_specials.gperf"
       {"sector_setgravity",216,3},
-      {""}, {""}, {""}, {""}, {""}, {""},
-#line 57 "thingdef_specials.gperf"
-      {"plat_upbyvalue",65,4},
+      {(char*)0}, {(char*)0}, {(char*)0},
+#line 122 "thingdef_specials.gperf"
+      {"ceiling_lowerbyvaluetimes8",199,3},
+      {(char*)0}, {(char*)0}, {(char*)0},
 #line 132 "thingdef_specials.gperf"
       {"sector_setcolor",212,4,5},
-      {""}, {""},
+      {(char*)0},
+#line 90 "thingdef_specials.gperf"
+      {"floor_waggle",138,5},
 #line 109 "thingdef_specials.gperf"
       {"sector_setrotation",185,3},
-      {""},
-#line 146 "thingdef_specials.gperf"
-      {"plat_upbyvaluestaytx",230,3},
-      {""},
+#line 56 "thingdef_specials.gperf"
+      {"plat_upwaitdownstay",64,3},
+      {(char*)0},
+#line 89 "thingdef_specials.gperf"
+      {"thing_spawnnofog",137,3,4},
 #line 111 "thingdef_specials.gperf"
       {"sector_setfloorpanning",187,5},
 #line 14 "thingdef_specials.gperf"
       {"acs_terminate",82,2},
-      {""},
+#line 63 "thingdef_specials.gperf"
+      {"polyobj_rotateright",3,3},
 #line 162 "thingdef_specials.gperf"
       {"elevator_movetofloor",246,2},
 #line 106 "thingdef_specials.gperf"
       {"line_mirror",182,0},
-      {""}, {""},
+      {(char*)0},
+#line 123 "thingdef_specials.gperf"
+      {"generic_floor",200,5},
 #line 140 "thingdef_specials.gperf"
       {"scroll_texture_both",221,5},
-#line 32 "thingdef_specials.gperf"
-      {"floor_lowertonearest",22,2},
+      {(char*)0},
 #line 80 "thingdef_specials.gperf"
-      {"thrustthing",72,2,3},
+      {"thrustthing",72,2,4},
 #line 98 "thingdef_specials.gperf"
       {"thrustthingz",128,4},
-#line 29 "thingdef_specials.gperf"
-      {"floor_lowerinstant",66,3},
-      {""}, {""},
+      {(char*)0}, {(char*)0},
+#line 131 "thingdef_specials.gperf"
+      {"translucentline",208,2,3},
 #line 94 "thingdef_specials.gperf"
       {"teleportinsector",78,4,5},
-#line 70 "thingdef_specials.gperf"
-      {"radius_quake",120,5},
-      {""}, {""},
+#line 87 "thingdef_specials.gperf"
+      {"thing_remove",132,1},
+      {(char*)0},
+#line 57 "thingdef_specials.gperf"
+      {"plat_upbyvalue",65,4},
 #line 113 "thingdef_specials.gperf"
       {"sector_setfloorscale",189,5},
 #line 97 "thingdef_specials.gperf"
       {"thing_setspecial",127,5},
-      {""}, {""}, {""}, {""},
+      {(char*)0}, {(char*)0},
+#line 31 "thingdef_specials.gperf"
+      {"floor_lowertolowest",21,2},
+#line 146 "thingdef_specials.gperf"
+      {"plat_upbyvaluestaytx",230,3},
 #line 104 "thingdef_specials.gperf"
       {"changeskill",179,1},
-#line 143 "thingdef_specials.gperf"
-      {"acs_executealways",226,1,5},
-      {""}, {""},
+      {(char*)0},
+#line 157 "thingdef_specials.gperf"
+      {"floor_lowertolowesttxty",241,2},
+#line 164 "thingdef_specials.gperf"
+      {"healthing",248,1,2},
 #line 101 "thingdef_specials.gperf"
       {"thing_changetid",176,2},
-      {""}, {""},
-#line 48 "thingdef_specials.gperf"
-      {"light_flicker",115,3},
-      {""},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
 #line 117 "thingdef_specials.gperf"
       {"ceiling_raiseinstant",194,3},
 #line 53 "thingdef_specials.gperf"
       {"pillar_open",30,4},
-#line 87 "thingdef_specials.gperf"
-      {"thing_remove",132,1},
-      {""},
-#line 56 "thingdef_specials.gperf"
-      {"plat_upwaitdownstay",64,3},
-#line 116 "thingdef_specials.gperf"
-      {"ceiling_lowerinstant",193,3},
-#line 151 "thingdef_specials.gperf"
-      {"floor_transfertrigger",235,1},
-      {""}, {""},
-#line 63 "thingdef_specials.gperf"
-      {"polyobj_rotateright",3,3},
-#line 156 "thingdef_specials.gperf"
-      {"floor_raisebytexture",240,2},
-      {""},
-#line 115 "thingdef_specials.gperf"
-      {"ceiling_lowertohighestfloor",192,2},
-      {""}, {""},
-#line 131 "thingdef_specials.gperf"
-      {"translucentline",208,2,3},
-      {""}, {""}, {""}, {""}, {""},
-#line 154 "thingdef_specials.gperf"
-      {"floor_raisetolowestceiling",238,2},
-      {""},
-#line 40 "thingdef_specials.gperf"
-      {"floorandceiling_raisebyvalue",96,3},
-      {""}, {""},
-#line 167 "thingdef_specials.gperf"
-      {"floorandceiling_lowerraise",251,3},
-      {""},
-#line 39 "thingdef_specials.gperf"
-      {"floorandceiling_lowerbyvalue",95,3},
-      {""}, {""},
-#line 55 "thingdef_specials.gperf"
-      {"plat_downbyvalue",63,4},
-      {""},
-#line 62 "thingdef_specials.gperf"
-      {"polyobj_rotateleft",2,3},
-      {""}, {""},
-#line 152 "thingdef_specials.gperf"
-      {"floor_transfernumeric",236,1},
-#line 153 "thingdef_specials.gperf"
-      {"changecamera",237,3},
-#line 163 "thingdef_specials.gperf"
-      {"elevator_lowertonearest",247,2},
-      {""},
-#line 41 "thingdef_specials.gperf"
-      {"floor_crushstop",46,1},
-      {""}, {""},
-#line 44 "thingdef_specials.gperf"
-      {"light_lowerbyvalue",111,2},
-      {""}, {""}, {""},
 #line 51 "thingdef_specials.gperf"
       {"pillar_build",29,3},
-#line 161 "thingdef_specials.gperf"
-      {"elevator_raisetonearest",245,2},
-      {""}, {""}, {""}, {""}, {""},
+#line 71 "thingdef_specials.gperf"
+      {"sector_changesound",140,2},
+      {(char*)0},
+#line 116 "thingdef_specials.gperf"
+      {"ceiling_lowerinstant",193,3},
+      {(char*)0}, {(char*)0}, {(char*)0},
 #line 73 "thingdef_specials.gperf"
       {"stairs_buildup",27,5},
 #line 96 "thingdef_specials.gperf"
       {"thing_move",125,2},
-      {""}, {""}, {""}, {""},
+      {(char*)0},
+#line 115 "thingdef_specials.gperf"
+      {"ceiling_lowertohighestfloor",192,2},
+      {(char*)0}, {(char*)0},
 #line 25 "thingdef_specials.gperf"
       {"door_raise",12,3},
 #line 72 "thingdef_specials.gperf"
       {"stairs_builddown",26,5},
-      {""},
-#line 27 "thingdef_specials.gperf"
-      {"floor_lowerbyvalue",20,3},
-      {""},
+#line 153 "thingdef_specials.gperf"
+      {"changecamera",237,3},
+      {(char*)0}, {(char*)0},
 #line 23 "thingdef_specials.gperf"
       {"door_close",10,2},
 #line 83 "thingdef_specials.gperf"
       {"thing_deactivate",131,1},
-      {""},
+#line 150 "thingdef_specials.gperf"
+      {"light_maxneighbor",234,1},
 #line 84 "thingdef_specials.gperf"
       {"thing_destroy",133,1,2},
-#line 28 "thingdef_specials.gperf"
-      {"floor_lowerbyvaluetimes8",36,3},
+      {(char*)0},
 #line 74 "thingdef_specials.gperf"
       {"stairs_builddownsync",31,4},
-      {""}, {""}, {""}, {""},
-#line 38 "thingdef_specials.gperf"
-      {"floor_raisetonearest",25,2},
-#line 159 "thingdef_specials.gperf"
-      {"exit_normal",243,1},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0},
 #line 125 "thingdef_specials.gperf"
       {"generic_door",202,5},
-#line 36 "thingdef_specials.gperf"
-      {"floor_raiseinstant",67,3},
-      {""}, {""}, {""}, {""},
-#line 71 "thingdef_specials.gperf"
-      {"sector_changesound",140,2},
-#line 164 "thingdef_specials.gperf"
-      {"healthing",248,1,2},
-#line 46 "thingdef_specials.gperf"
-      {"light_fade",113,3},
+      {(char*)0}, {(char*)0},
+#line 158 "thingdef_specials.gperf"
+      {"floor_lowertohighest",242,3},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0},
+#line 55 "thingdef_specials.gperf"
+      {"plat_downbyvalue",63,4},
+      {(char*)0},
+#line 165 "thingdef_specials.gperf"
+      {"door_closewaitopen",249,3},
+      {(char*)0}, {(char*)0},
 #line 68 "thingdef_specials.gperf"
       {"polyobj_or_rotateleft",90,3},
 #line 69 "thingdef_specials.gperf"
       {"polyobj_or_rotateright",91,3},
-#line 165 "thingdef_specials.gperf"
-      {"door_closewaitopen",249,3},
-      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 30 "thingdef_specials.gperf"
-      {"floor_movetovaluetimes8",68,4},
-      {""}, {""}, {""}, {""}, {""}, {""},
-#line 22 "thingdef_specials.gperf"
-      {"ceiling_movetovaluetimes8",69,4},
-      {""}, {""}, {""}, {""}, {""},
-#line 85 "thingdef_specials.gperf"
-      {"thing_projectile",134,5},
-      {""}, {""}, {""}, {""},
-#line 103 "thingdef_specials.gperf"
-      {"thing_projectileaimed",178,4,5},
-      {""},
-#line 86 "thingdef_specials.gperf"
-      {"thing_projectilegravity",136,5},
-#line 58 "thingdef_specials.gperf"
-      {"plat_perpetualraise",60,3},
+#line 163 "thingdef_specials.gperf"
+      {"elevator_lowertonearest",247,2},
+      {(char*)0},
+#line 42 "thingdef_specials.gperf"
+      {"light_forcelightning",109,1},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
 #line 67 "thingdef_specials.gperf"
       {"polyobj_or_move",92,4},
-      {""},
-#line 130 "thingdef_specials.gperf"
-      {"plat_perpetualraiselip",207,4},
-      {""}, {""}, {""}, {""},
-#line 150 "thingdef_specials.gperf"
-      {"light_maxneighbor",234,1},
+#line 159 "thingdef_specials.gperf"
+      {"exit_normal",243,1},
+      {(char*)0},
+#line 161 "thingdef_specials.gperf"
+      {"elevator_raisetonearest",245,2},
+      {(char*)0},
+#line 32 "thingdef_specials.gperf"
+      {"floor_lowertonearest",22,2},
+      {(char*)0}, {(char*)0},
+#line 29 "thingdef_specials.gperf"
+      {"floor_lowerinstant",66,3},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+#line 27 "thingdef_specials.gperf"
+      {"floor_lowerbyvalue",20,3},
+      {(char*)0}, {(char*)0}, {(char*)0},
+#line 174 "thingdef_specials.gperf"
+      {"teleport_zombiechanger",39,2},
+#line 43 "thingdef_specials.gperf"
+      {"light_raisebyvalue",110,2},
+#line 28 "thingdef_specials.gperf"
+      {"floor_lowerbyvaluetimes8",36,3},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
 #line 137 "thingdef_specials.gperf"
       {"stairs_buildupdoom",217,5},
-      {""},
+      {(char*)0},
+#line 22 "thingdef_specials.gperf"
+      {"ceiling_movetovaluetimes8",69,4},
+      {(char*)0}, {(char*)0}, {(char*)0},
+#line 138 "thingdef_specials.gperf"
+      {"sector_setwind",218,4},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0},
 #line 52 "thingdef_specials.gperf"
       {"pillar_buildandcrush",94,4},
-      {""}, {""},
+      {(char*)0}, {(char*)0},
 #line 75 "thingdef_specials.gperf"
       {"stairs_buildupsync",32,4},
-#line 138 "thingdef_specials.gperf"
-      {"sector_setwind",218,4},
-      {""}, {""}, {""}, {""}, {""},
-#line 158 "thingdef_specials.gperf"
-      {"floor_lowertohighest",242,3},
-      {""}, {""},
-#line 43 "thingdef_specials.gperf"
-      {"light_raisebyvalue",110,2},
+      {(char*)0}, {(char*)0}, {(char*)0},
+#line 149 "thingdef_specials.gperf"
+      {"light_minneighbor",233,1},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0},
+#line 62 "thingdef_specials.gperf"
+      {"polyobj_rotateleft",2,3},
 #line 54 "thingdef_specials.gperf"
       {"plat_downwaitupstay",62,3},
-      {""}, {""},
+#line 41 "thingdef_specials.gperf"
+      {"floor_crushstop",46,1},
+#line 85 "thingdef_specials.gperf"
+      {"thing_projectile",134,5},
 #line 129 "thingdef_specials.gperf"
       {"plat_downwaitupstaylip",206,4},
-      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 166 "thingdef_specials.gperf"
-      {"floor_donut",250,3},
-      {""}, {""}, {""}, {""}, {""}, {""},
-#line 34 "thingdef_specials.gperf"
-      {"floor_raisebyvalue",23,3},
-      {""}, {""}, {""},
-#line 155 "thingdef_specials.gperf"
-      {"floor_raisebyvaluetxty",239,3},
-      {""},
-#line 35 "thingdef_specials.gperf"
-      {"floor_raisebyvaluetimes8",35,3},
-      {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 48 "thingdef_specials.gperf"
+      {"light_flicker",115,3},
+      {(char*)0}, {(char*)0},
+#line 103 "thingdef_specials.gperf"
+      {"thing_projectileaimed",178,4,5},
 #line 61 "thingdef_specials.gperf"
       {"polyobj_move",4,4},
-      {""}, {""}, {""}, {""}, {""},
-#line 147 "thingdef_specials.gperf"
-      {"plat_toggleceiling",231,1},
-      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-      {""},
+#line 86 "thingdef_specials.gperf"
+      {"thing_projectilegravity",136,5},
+#line 58 "thingdef_specials.gperf"
+      {"plat_perpetualraise",60,3},
+      {(char*)0},
+#line 151 "thingdef_specials.gperf"
+      {"floor_transfertrigger",235,1},
+#line 130 "thingdef_specials.gperf"
+      {"plat_perpetualraiselip",207,4},
+      {(char*)0}, {(char*)0},
+#line 156 "thingdef_specials.gperf"
+      {"floor_raisebytexture",240,2},
+#line 176 "thingdef_specials.gperf"
+      {"plat_upnearestwaitdownstay",172,3},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+#line 152 "thingdef_specials.gperf"
+      {"floor_transfernumeric",236,1},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+#line 154 "thingdef_specials.gperf"
+      {"floor_raisetolowestceiling",238,2},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0},
+#line 46 "thingdef_specials.gperf"
+      {"light_fade",113,3},
+      {(char*)0}, {(char*)0},
+#line 40 "thingdef_specials.gperf"
+      {"floorandceiling_raisebyvalue",96,3},
 #line 24 "thingdef_specials.gperf"
       {"door_open",11,2},
-      {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 149 "thingdef_specials.gperf"
-      {"light_minneighbor",233,1},
-      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-      {""}, {""}, {""}, {""},
+      {(char*)0},
+#line 167 "thingdef_specials.gperf"
+      {"floorandceiling_lowerraise",251,3},
+      {(char*)0},
+#line 39 "thingdef_specials.gperf"
+      {"floorandceiling_lowerbyvalue",95,3},
+      {(char*)0}, {(char*)0}, {(char*)0},
+#line 64 "thingdef_specials.gperf"
+      {"polyobj_doorswing",7,4},
+#line 30 "thingdef_specials.gperf"
+      {"floor_movetovaluetimes8",68,4},
+      {(char*)0}, {(char*)0},
 #line 81 "thingdef_specials.gperf"
       {"damagething",73,1},
-      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-      {""}, {""}, {""}, {""}, {""},
+#line 65 "thingdef_specials.gperf"
+      {"polyobj_doorslide",8,5},
+      {(char*)0}, {(char*)0},
 #line 37 "thingdef_specials.gperf"
       {"floor_raisetohighest",24,2},
-      {""}, {""}, {""}, {""}, {""}, {""},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0},
 #line 95 "thingdef_specials.gperf"
       {"thing_damage",119,2,3},
-      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 65 "thingdef_specials.gperf"
-      {"polyobj_doorslide",8,5},
-      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 100 "thingdef_specials.gperf"
-      {"thing_projectileintercept",175,5},
-      {""}, {""}, {""}, {""}, {""},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0},
+#line 147 "thingdef_specials.gperf"
+      {"plat_toggleceiling",231,1},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0},
+#line 38 "thingdef_specials.gperf"
+      {"floor_raisetonearest",25,2},
+      {(char*)0}, {(char*)0},
+#line 36 "thingdef_specials.gperf"
+      {"floor_raiseinstant",67,3},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+#line 34 "thingdef_specials.gperf"
+      {"floor_raisebyvalue",23,3},
+      {(char*)0}, {(char*)0}, {(char*)0},
+#line 155 "thingdef_specials.gperf"
+      {"floor_raisebyvaluetxty",239,3},
+      {(char*)0},
+#line 35 "thingdef_specials.gperf"
+      {"floor_raisebyvaluetimes8",35,3},
+      {(char*)0},
 #line 66 "thingdef_specials.gperf"
       {"polyobj_or_movetimes8",93,4},
-      {""}, {""}, {""}, {""},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0},
+#line 166 "thingdef_specials.gperf"
+      {"floor_donut",250,3},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
 #line 26 "thingdef_specials.gperf"
       {"door_lockedraise",13,4},
-      {""}, {""}, {""}, {""}, {""},
-#line 64 "thingdef_specials.gperf"
-      {"polyobj_doorswing",7,4},
-      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-      {""}, {""}, {""}, {""}, {""},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0},
+#line 172 "thingdef_specials.gperf"
+      {"door_animated",4,3},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0},
+#line 100 "thingdef_specials.gperf"
+      {"thing_projectileintercept",175,5},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0},
 #line 60 "thingdef_specials.gperf"
       {"polyobj_movetimes8",6,4},
-      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-      {""},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0},
+      {(char*)0}, {(char*)0}, {(char*)0},
 #line 33 "thingdef_specials.gperf"
       {"floor_raiseandcrush",28,3}
     };
@@ -539,7 +618,7 @@
         {
           register const char *s = wordlist[key].name;
 
-          if (*str == *s && !strcmp (str + 1, s + 1))
+          if (s && *str == *s && !strcmp (str + 1, s + 1))
             return &wordlist[key];
         }
     }
diff -durN 96/src/v_draw.cpp 96x/src/v_draw.cpp
--- 96/src/v_draw.cpp	2004-12-24 17:31:42.000000000 +0100
+++ 96x/src/v_draw.cpp	2005-06-22 15:18:33.000000000 +0200
@@ -90,6 +90,7 @@
 	int shadowColor = 0;
 	int virtWidth = this->GetWidth();
 	int virtHeight = this->GetHeight();
+	BOOL keepratio = false;
 
 	x0 <<= FRACBITS;
 	y0 <<= FRACBITS;
@@ -310,6 +311,10 @@
 				spanptr = NULL;
 			}
 			break;
+
+		case DTA_KeepRatio:
+			keepratio = va_arg (tags, BOOL);
+			break;
 		}
 		tag = va_arg (tags, DWORD);
 	}
@@ -318,22 +323,22 @@
 	if (virtWidth != Width || virtHeight != Height)
 	{
 		int myratio = CheckRatio (Width, Height);
-		if (myratio != 0)
+		if (myratio != 0 && !keepratio)
 		{ // The target surface is not 4:3, so expand the specified
 		  // virtual size to avoid undesired stretching of the image.
 		  // Does not handle non-4:3 virtual sizes. I'll worry about
 		  // those if somebody expresses a desire to use them.
 			x0 = Scale (Width*960, x0-virtWidth*FRACUNIT/2, virtWidth*BaseRatioSizes[myratio][0]) + Width*FRACUNIT/2;
 			y0 = Scale (Height, y0, virtHeight);
-			destwidth = FixedDiv (Width*960 * img->GetWidth(), virtWidth*BaseRatioSizes[myratio][0]);
-			destheight = FixedDiv (Height * img->GetHeight(), virtHeight);
+			destwidth = FixedDiv (Width*960 * (destwidth>>FRACBITS), virtWidth*BaseRatioSizes[myratio][0]);
+			destheight = FixedDiv (Height * (destheight>>FRACBITS), virtHeight);
 		}
 		else
 		{
 			x0 = Scale (Width, x0, virtWidth);
 			y0 = Scale (Height, y0, virtHeight);
-			destwidth = FixedDiv (Width * img->GetWidth(), virtWidth);
-			destheight = FixedDiv (Height * img->GetHeight(), virtHeight);
+			destwidth = FixedDiv (Width * (destwidth>>FRACBITS), virtWidth);
+			destheight = FixedDiv (Height * (destheight>>FRACBITS), virtHeight);
 		}
 	}
 
diff -durN 96/src/v_font.cpp 96x/src/v_font.cpp
--- 96/src/v_font.cpp	2004-10-28 12:58:09.000000000 +0200
+++ 96x/src/v_font.cpp	2005-06-22 15:18:33.000000000 +0200
@@ -48,6 +48,7 @@
 #include "i_system.h"
 #include "gi.h"
 #include "cmdlib.h"
+#include "sc_man.h"
 
 // This is a font character that loads a patch and recolors it.
 class FFontChar1 : public FPatchTexture
@@ -233,10 +234,15 @@
 		}
 	}
 
-	SpaceWidth = (Chars['N' - first].Pic->GetWidth() + 1) / 2;
+	if ('N'-first>=0 && 'N'-first<count && Chars['N' - first].Pic)
+	{
+		SpaceWidth = (Chars['N' - first].Pic->GetWidth() + 1) / 2;
+	}
+	else SpaceWidth=4;
 	BuildTranslations (luminosity, identity);
 
 	delete[] luminosity;
+	delete[] charlumps;
 }
 
 FFont::~FFont ()
@@ -1032,3 +1038,288 @@
 
 	Spans = CreateSpans (Pixels);
 }
+
+
+
+
+//===========================================================================
+// 
+// Essentially a normal multilump font but 
+// with an explicit list of character patches
+//
+//===========================================================================
+class FSpecialFont : public FFont
+{
+public:
+	FSpecialFont (const char *name, int first, int count, int * lumplist, const bool * notranslate);
+};
+
+
+FSpecialFont::FSpecialFont (const char *name, int first, int count, int * lumplist, const bool * notranslate)
+{
+	int i, j, lump;
+	char buffer[12];
+	int *charlumps;
+	byte usedcolors[256], identity[256];
+	double *luminosity;
+	int maxyoffs;
+	int TotalColors;
+
+	Name=copystring(name);
+	Chars = new CharData[count];
+	charlumps = new int[count];
+	PatchRemap = new BYTE[256];
+	Ranges = NULL;
+	FirstChar = first;
+	LastChar = first + count - 1;
+	FontHeight = 0;
+	GlobalKerning = false;
+	memset (usedcolors, 0, 256);
+	Name = copystring (name);
+	Next = FirstFont;
+	FirstFont = this;
+
+	maxyoffs = 0;
+
+	for (i = 0; i < count; i++)
+	{
+		lump = charlumps[i] = lumplist[i];
+		if (lump >= 0)
+		{
+			Wads.GetLumpName(buffer,lump);
+			FTexture *pic = TexMan[TexMan.AddPatch (buffer)];
+			int height = pic->GetHeight();
+			int yoffs = pic->TopOffset;
+
+			if (yoffs > maxyoffs)
+			{
+				maxyoffs = yoffs;
+			}
+			height += abs (yoffs);
+			if (height > FontHeight)
+			{
+				FontHeight = height;
+			}
+
+			/* experimental True Color OpenGL font support ;)
+			if (TexMan.GLSupport())
+			{
+				RecordGLTextureColors(lump, usedcolors);
+			}
+			else
+			*/
+			{
+				RecordTextureColors (pic, usedcolors);
+			}
+		}
+	}
+
+	// exclude the non-translated colors from the translation calculation
+	if (notranslate!=NULL)
+	{
+		for(i=0;i<256;i++) if (notranslate[i]) usedcolors[i]=false;
+	}
+
+	TotalColors = ActiveColors = SimpleTranslation (usedcolors, PatchRemap, identity, &luminosity);
+
+	// Map all untranslated colors into the table of used colors
+	if (notranslate!=NULL)
+	{
+		for(i=0;i<256;i++) 
+		{
+			if (notranslate[i]) 
+			{
+				PatchRemap[i]=TotalColors;
+				identity[TotalColors]=i;
+				TotalColors++;
+			}
+		}
+	}
+
+	for (i = 0; i < count; i++)
+	{
+		if (charlumps[i] >= 0)
+		{
+			/* experimental True Color OpenGL font support ;)
+			if (TexMan.GLSupport()) 
+			{
+				Chars[i].Pic = new FGLFontChar1(charlumps[i], PatchRemap, TotalColors, &Ranges);
+			}
+			else 
+			*/
+			{
+				Chars[i].Pic = new FFontChar1 (charlumps[i], PatchRemap);
+			}
+		}
+		else
+		{
+			Chars[i].Pic = NULL;
+		}
+	}
+
+	// Special fonts normally don't have all characters so be careful here!
+	if ('N'-first>=0 && 'N'-first<count && Chars['N' - first].Pic) 
+	{
+		SpaceWidth = (Chars['N' - first].Pic->GetWidth() + 1) / 2;
+	}
+	else SpaceWidth=4;
+
+	BuildTranslations (luminosity, identity/*, TexMan.GLSupport()*/);
+
+
+	// add the untranslated colors to the Ranges table
+	if (ActiveColors<TotalColors)
+	{
+		int factor = 1;//3 * TexMan.GLSupport();
+		byte * oldranges=Ranges;
+		Ranges = new byte[NUM_TEXT_COLORS * TotalColors * factor ];
+
+		for (i = 0; i < CR_UNTRANSLATED; i++)
+		{
+			memcpy(&Ranges[i * TotalColors * factor], &oldranges[i * ActiveColors * factor], ActiveColors * factor);
+
+			for(j=ActiveColors;j<TotalColors;j++)
+			{
+				/* experimental True Color OpenGL font support ;)
+				if (TexMan.GLSupport())
+				{
+					Ranges[(TotalColors*i + j)*3 + 0]=GPalette.BaseColors[identity[j]].r;
+					Ranges[(TotalColors*i + j)*3 + 1]=GPalette.BaseColors[identity[j]].g;
+					Ranges[(TotalColors*i + j)*3 + 2]=GPalette.BaseColors[identity[j]].b;
+				}
+				else
+				*/
+				{
+					Ranges[TotalColors*i + j]=identity[j];
+				}
+			}
+		}
+		delete [] oldranges;
+	}
+	ActiveColors=TotalColors;
+
+	delete[] luminosity;
+	delete[] charlumps;
+}
+
+
+//===========================================================================
+// 
+// Initialize a list of custom multipatch fonts
+//
+//===========================================================================
+BOOL SC_CheckNumber (void);
+
+void V_InitCustomFonts()
+{
+	int lumplist[256];
+	bool notranslate[256];
+	char namebuffer[16],templatebuf[16];
+	int adder=0;
+	int i;
+	int llump,lastlump=-1;
+	int format=0;
+	int start=33;
+	int first=33;
+	int count=223;
+
+
+	while ((llump = Wads.FindLump ("FONTDEFS", &lastlump)) != -1)
+	{
+		SC_OpenLumpNum(llump,"FONTDEFS");
+		while (SC_GetString())
+		{
+			memset(lumplist,-1,sizeof(lumplist));
+			memset(notranslate,0,sizeof(notranslate));
+			strncpy(namebuffer,sc_String,16);
+			namebuffer[15]=0;
+			format=0;
+			start=33;
+			first=33;
+			count=223;
+
+			SC_MustGetStringName("{");
+			while (!SC_CheckString("}"))
+			{
+				SC_MustGetString();
+				if (SC_Compare("TEMPLATE"))
+				{
+					if (format==2) goto wrong;
+					SC_MustGetString();
+					strncpy(templatebuf,sc_String,16);
+					templatebuf[15]=0;
+					format=1;
+				}
+				else if (SC_Compare("BASE"))
+				{
+					if (format==2) goto wrong;
+					SC_MustGetNumber();
+					start=sc_Number;
+					format=1;
+				}
+				else if (SC_Compare("FIRST"))
+				{
+					if (format==2) goto wrong;
+					SC_MustGetNumber();
+					first=sc_Number;
+					format=1;
+				}
+				else if (SC_Compare("COUNT"))
+				{
+					if (format==2) goto wrong;
+					SC_MustGetNumber();
+					count=sc_Number;
+					format=1;
+				}
+				else if (SC_Compare("NOTRANSLATION"))
+				{
+					if (format==1) goto wrong;
+					while (SC_CheckNumber() && !sc_Crossed)
+					{
+						if (sc_Number>=0 && sc_Number<256) notranslate[sc_Number]=true;
+					}
+					format=2;
+				}
+				else
+				{
+					if (format==1) goto wrong;
+					int * p=&lumplist[*(unsigned char*)sc_String];
+					SC_MustGetString();
+					*p=Wads.CheckNumForName(sc_String);
+					format=2;
+				}
+			}
+			if (format==1)
+			{
+				new FFont(namebuffer, templatebuf, first, count, start);
+			}
+			else if (format==2)
+			{
+				for(i=0;i<256;i++)
+				{
+					if (lumplist[i]!=-1)
+					{
+						first=i;
+						break;
+					}
+				}
+				for(i=255;i>=0;i--)
+				{
+					if (lumplist[i]!=-1)
+					{
+						count=i-first+1;
+						break;
+					}
+				}
+				if (count>0) new FSpecialFont(namebuffer, first, count, &lumplist[first], notranslate);
+			}
+			else goto wrong;
+		}
+		SC_Close();
+	}
+	return;
+
+wrong:
+	SC_ScriptError("Invalid combination of properties in font '%s'", namebuffer);
+
+}
diff -durN 96/src/v_font.h 96x/src/v_font.h
--- 96/src/v_font.h	2004-04-29 16:07:39.000000000 +0200
+++ 96x/src/v_font.h	2005-06-22 15:18:33.000000000 +0200
@@ -140,4 +140,6 @@
 
 extern FFont *SmallFont, *SmallFont2, *BigFont, *ConFont;
 
+void V_InitCustomFonts();
+
 #endif //__V_FONT_H__
diff -durN 96/src/v_video.cpp 96x/src/v_video.cpp
--- 96/src/v_video.cpp	2004-12-24 18:13:39.000000000 +0100
+++ 96x/src/v_video.cpp	2005-06-22 15:18:34.000000000 +0200
@@ -911,6 +911,7 @@
 
 	FBaseCVar::ResetColors ();
 	ConFont = new FSingleLumpFont ("ConsoleFont", Wads.GetNumForName ("CONFONT"));
+	V_InitCustomFonts();
 
 	BuildTransTable (GPalette.BaseColors);
 }
diff -durN 96/src/v_video.h 96x/src/v_video.h
--- 96/src/v_video.h	2004-12-23 11:46:17.000000000 +0100
+++ 96x/src/v_video.h	2005-06-22 15:18:34.000000000 +0200
@@ -100,6 +100,7 @@
 	DTA_ClipRight,		// don't draw anything at or to the right of this column (on dest, not source)
 	DTA_Masked,			// true(default)=use masks from texture, false=ignore masks
 	DTA_HUDRules,		// use fullscreen HUD rules to position and size textures
+	DTA_KeepRatio,		// Doesn't adjust screen size for DTA_Virtual* if the aspect ratio is not 4:3
 };
 
 enum
diff -durN 96/src/version.h 96x/src/version.h
--- 96/src/version.h	2004-12-24 20:48:18.000000000 +0100
+++ 96x/src/version.h	2005-07-25 13:04:02.000000000 +0200
@@ -37,14 +37,14 @@
 // Lots of different representations for the version number
 enum { GAMEVERSION = 205 };
 #define STRVERSION "205"
-#define DOTVERSIONSTR "2.0.96 (Santa's Little Helper?)"
+#define DOTVERSIONSTR "2.0.96x"
 #define GAMEVER (2*256+2)
 
 // SAVEVER is the version of the information stored in level snapshots.
 // Note that SAVEVER is not directly comparable to VERSION.
 // SAVESIG should match SAVEVER.
-#define SAVEVER 227
-#define SAVESIG "ZDOOMSAVE227"
+#define SAVEVER 229
+#define SAVESIG "ZDOOMSAVE229"
 
 // MINSAVEVER is the minimum level snapshot version that can be loaded.
 #define MINSAVEVER 221	// Used by 2.0.90
diff -durN 96/src/wi_stuff.cpp 96x/src/wi_stuff.cpp
--- 96/src/wi_stuff.cpp	2004-12-24 20:44:48.000000000 +0100
+++ 96x/src/wi_stuff.cpp	2005-07-11 18:31:45.000000000 +0200
@@ -21,6 +21,7 @@
 //
 //-----------------------------------------------------------------------------
 
+// Enhancements by Graf Zahl
 
 #include <ctype.h>
 #include <stdio.h>
@@ -41,6 +42,8 @@
 #include "hu_stuff.h"
 #include "v_palette.h"
 #include "s_sndseq.h"
+#include "sc_man.h"
+#include "v_text.h"
 #include "gi.h"
 
 // States for the intermission
@@ -53,20 +56,15 @@
 } stateenum_t;
 
 CVAR (Bool, wi_percents, true, CVAR_ARCHIVE)
+CVAR (Bool, wi_sucks, true, CVAR_ARCHIVE)		// this is for those of us who absolutely can't stand the 'sucks'!
+CVAR (Bool, wi_totaltime, false, CVAR_ARCHIVE)	// Something that can be added later.
+
 
 void WI_loadData ();
 void WI_unloadData ();
 
 #define NEXTSTAGE		(gameinfo.gametype == GAME_Doom ? "weapons/rocklx" : "doors/dr1_clos")
 #define PASTSTATS		(gameinfo.gametype == GAME_Doom ? "weapons/shotgr" : "plats/pt1_stop")
-//
-// Data needed to add patches to full screen intermission pics.
-// Patches are statistics messages, and animations.
-// Loads of by-pixel layout and placement, offsets etc.
-//
-
-#define NUMEPISODES 	3
-#define NUMMAPS 		9
 
 // GLOBAL LOCATIONS
 #define WI_TITLEY				2
@@ -100,13 +98,27 @@
 #define DM_VICTIMSX 			5
 #define DM_VICTIMSY 			50
 
-
-
-
+// These animation variables, structures, etc. are used for the
+// DOOM/Ultimate DOOM intermission screen animations.  This is
+// totally different from any sprite or texture/flat animations
 typedef enum
 {
-	ANIM_ALWAYS,
-	ANIM_LEVEL
+	ANIM_ALWAYS,	// determined by patch entry
+	ANIM_PIC,		// continuous
+
+	// condition bitflags
+	ANIM_IFVISITED=8,
+	ANIM_IFNOTVISITED=16,
+	ANIM_IFENTERING=32,
+	ANIM_IFNOTENTERING=64,
+	ANIM_IFLEAVING=128,
+	ANIM_IFNOTLEAVING=256,
+	ANIM_IFTRAVELLING=512,
+	ANIM_IFNOTTRAVELLING=1024,
+
+	ANIM_TYPE=7,
+	ANIM_CONDITION=~7,
+		
 } animenum_t;
 
 typedef struct
@@ -114,166 +126,45 @@
 	int x, y;
 } yahpt_t;
 
+struct lnode_t
+{
+	int   x;       // x/y coordinate pair structure
+	int   y;
+	char level[9];
+} ;
+
+
+#define FACEBACKOFS 4
+
+
+//
+// Animation.
+// There is another anim_t used in p_spec.
+// (which is why I have renamed this one!)
+//
+
+#define MAX_ANIMATION_FRAMES 20
 typedef struct
 {
-	animenum_t	type;
+	int			type;	// Made an int so I can use '|'
 	int 		period;	// period in tics between animations
 	int 		nanims;	// number of animation frames
 	yahpt_t 	loc;	// location of animation
 	int 		data;	// ALWAYS: n/a, RANDOM: period deviation (<256)
-	FTexture*	p[3];	// actual graphics for frames of animations
+	FTexture *	p[MAX_ANIMATION_FRAMES];	// actual graphics for frames of animations
 
 	// following must be initialized to zero before use!
 	int 		nexttic;	// next value of bcnt (used in conjunction with period)
 	int 		ctr;		// next frame number to animate
 	int 		state;		// used by RANDOM and LEVEL when animating
+	
+	char		levelname[9];
+	char		levelname2[9];
 } in_anim_t;
 
-static yahpt_t lnodes[NUMEPISODES*2][NUMMAPS] =
-{
-//
-// Doom 1 Episodes
-//
-	// Episode 1 World Map
-	{
-		{ 185, 164 },	// location of level 1 (CJ)
-		{ 148, 143 },	// location of level 2 (CJ)
-		{ 69, 122 },	// location of level 3 (CJ)
-		{ 209, 102 },	// location of level 4 (CJ)
-		{ 116, 89 },	// location of level 5 (CJ)
-		{ 166, 55 },	// location of level 6 (CJ)
-		{ 71, 56 }, 	// location of level 7 (CJ)
-		{ 135, 29 },	// location of level 8 (CJ)
-		{ 71, 24 }		// location of level 9 (CJ)
-	},
-
-	// Episode 2 World Map
-	{
-		{ 254, 25 },	// location of level 1 (CJ)
-		{ 97, 50 }, 	// location of level 2 (CJ)
-		{ 188, 64 },	// location of level 3 (CJ)
-		{ 128, 78 },	// location of level 4 (CJ)
-		{ 214, 92 },	// location of level 5 (CJ)
-		{ 133, 130 },	// location of level 6 (CJ)
-		{ 208, 136 },	// location of level 7 (CJ)
-		{ 148, 140 },	// location of level 8 (CJ)
-		{ 235, 158 }	// location of level 9 (CJ)
-	},
-
-	// Episode 3 World Map
-	{
-		{ 156, 168 },	// location of level 1 (CJ)
-		{ 48, 154 },	// location of level 2 (CJ)
-		{ 174, 95 },	// location of level 3 (CJ)
-		{ 265, 75 },	// location of level 4 (CJ)
-		{ 130, 48 },	// location of level 5 (CJ)
-		{ 279, 23 },	// location of level 6 (CJ)
-		{ 198, 48 },	// location of level 7 (CJ)
-		{ 140, 25 },	// location of level 8 (CJ)
-		{ 281, 136 }	// location of level 9 (CJ)
-	},
-//
-// Heretic Episodes
-//
-	{
-		{ 172, 78 },
-		{ 86, 90 },
-		{ 73, 66 },
-		{ 159, 95 },
-		{ 148, 126 },
-		{ 132, 54 },
-		{ 131, 74 },
-		{ 208, 138 },
-		{ 52, 101 }
-	},
-	{
-		{ 218, 57 },
-		{ 137, 81 },
-		{ 155, 124 },
-		{ 171, 68 },
-		{ 250, 86 },
-		{ 136, 98 },
-		{ 203, 90 },
-		{ 220, 140 },
-		{ 279, 106 }
-	},
-	{
-		{ 86, 99 },
-		{ 124, 103 },
-		{ 154, 79 },
-		{ 202, 83 },
-		{ 178, 59 },
-		{ 142, 58 },
-		{ 219, 66 },
-		{ 247, 57 },
-		{ 107, 80 }
-	}
-};
-
-
-//
-// Animation locations for episode 0 (1).
-// Using patches saves a lot of space,
-//	as they replace 320x200 full screen frames.
-//
-static in_anim_t epsd0animinfo[] =
-{
-	{ ANIM_ALWAYS, TICRATE/3, 3, { 224, 104 } },
-	{ ANIM_ALWAYS, TICRATE/3, 3, { 184, 160 } },
-	{ ANIM_ALWAYS, TICRATE/3, 3, { 112, 136 } },
-	{ ANIM_ALWAYS, TICRATE/3, 3, { 72, 112 } },
-	{ ANIM_ALWAYS, TICRATE/3, 3, { 88, 96 } },
-	{ ANIM_ALWAYS, TICRATE/3, 3, { 64, 48 } },
-	{ ANIM_ALWAYS, TICRATE/3, 3, { 192, 40 } },
-	{ ANIM_ALWAYS, TICRATE/3, 3, { 136, 16 } },
-	{ ANIM_ALWAYS, TICRATE/3, 3, { 80, 16 } },
-	{ ANIM_ALWAYS, TICRATE/3, 3, { 64, 24 } }
-};
-
-static in_anim_t epsd1animinfo[] =
-{
-	{ ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 1 },
-	{ ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 2 },
-	{ ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 3 },
-	{ ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 4 },
-	{ ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 5 },
-	{ ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 6 },
-	{ ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 7 },
-	{ ANIM_LEVEL, TICRATE/3, 3, { 192, 144 }, 8 },
-	{ ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 8 }
-};
-
-static in_anim_t epsd2animinfo[] =
-{
-	{ ANIM_ALWAYS, TICRATE/3, 3, { 104, 168 } },
-	{ ANIM_ALWAYS, TICRATE/3, 3, { 40, 136 } },
-	{ ANIM_ALWAYS, TICRATE/3, 3, { 160, 96 } },
-	{ ANIM_ALWAYS, TICRATE/3, 3, { 104, 80 } },
-	{ ANIM_ALWAYS, TICRATE/3, 3, { 120, 32 } },
-	{ ANIM_ALWAYS, TICRATE/4, 3, { 40, 0 } }
-};
-
-static int NUMANIMS[NUMEPISODES] =
-{
-	sizeof(epsd0animinfo)/sizeof(in_anim_t),
-	sizeof(epsd1animinfo)/sizeof(in_anim_t),
-	sizeof(epsd2animinfo)/sizeof(in_anim_t)
-};
-
-static in_anim_t *anims[NUMEPISODES] =
-{
-	epsd0animinfo,
-	epsd1animinfo,
-	epsd2animinfo
-};
+static TArray<lnode_t> lnodes;
+static TArray<in_anim_t> anims;
 
-// [RH] Map name -> index mapping
-static char names[NUMEPISODES][NUMMAPS][8] =
-{
-	{ "E1M1", "E1M2", "E1M3", "E1M4", "E1M5", "E1M6", "E1M7", "E1M8", "E1M9" },
-	{ "E2M1", "E2M2", "E2M3", "E2M4", "E2M5", "E2M6", "E2M7", "E2M8", "E2M9" },
-	{ "E3M1", "E3M2", "E3M3", "E3M4", "E3M5", "E3M6", "E3M7", "E3M8", "E3M9" }
-};
 
 //
 // GENERAL DATA
@@ -303,12 +194,11 @@
 static wbplayerstruct_t*plrs;				// wbs->plyr[]
 static int				cnt;				// used for general timing
 static int				bcnt;				// used for timing of background animation
-static int				epsd;				// episode currently displayed
-
 static int				cnt_kills[MAXPLAYERS];
 static int				cnt_items[MAXPLAYERS];
 static int				cnt_secret[MAXPLAYERS];
 static int				cnt_time;
+static int				cnt_total_time;
 static int				cnt_par;
 static int				cnt_pause;
 
@@ -316,7 +206,7 @@
 //		GRAPHICS
 //
 
-static FTexture* 		yah[2];		// You Are Here graphic
+static TArray<FTexture *> yah; 		// You Are Here graphic
 static FTexture* 		splat;		// splat
 static FTexture* 		percent;	// %, : graphics
 static FTexture* 		colon;
@@ -342,354 +232,663 @@
 static FTexture*		lnames[2];	// Name graphics of each level (centered)
 
 // [RH] Info to dynamically generate the level name graphics
-static int				lnamewidths[2];
 static const char		*lnametexts[2];
 
 static FTexture			*background;
-static bool				DrawFoM;	// [RH] Make the Fortress of Mystery permanent after visiting it
 
 //
 // CODE
 //
 
-void WI_slamBackground ()
-{
-	if (background)
-	{
-		screen->DrawTexture (background, 0, 0, DTA_320x200, true, TAG_DONE);
-		screen->FillBorder (NULL);
-		if (DrawFoM)
-		{
-			screen->DrawTexture (anims[1][7].p[2], anims[1][7].loc.x, anims[1][7].loc.y, DTA_320x200, true, TAG_DONE);
-		}
-	}
-	else if (state != NoState)
-	{
-		int picnum = TexMan.CheckForTexture ("FLOOR16", FTexture::TEX_Flat, FTextureManager::TEXMAN_Overridable);
-		if (picnum >= 0)
-		{
-			screen->FlatFill (0, 0, SCREENWIDTH, SCREENHEIGHT, TexMan(picnum));
-		}
-		else
-		{
-			screen->Clear (0, 0, SCREENWIDTH, SCREENHEIGHT, 0);
-		}
-	}
-}
+// ====================================================================
+//
+// Background script commands
+//
+// ====================================================================
 
-static void WI_DrawCharPatch (FTexture *patch, int x, int y)
+static const char *WI_Cmd[]={
+	"Background",
+	"Splat",
+	"Pointer",
+	"Spots",
+
+	"IfEntering",
+	"IfNotEntering",
+	"IfVisited",
+	"IfNotVisited",
+	"IfLeaving",
+	"IfNotLeaving",
+	"IfTravelling",
+	"IfNotTravelling",
+
+	"Animation",
+	"Pic",
+	
+	NULL
+};
+
+//====================================================================
+// 
+//	Loads the background - either from a single texture
+//	or an intermission lump.
+//	Unfortunately the texture manager is incapable of recognizing text
+//	files so if you use a script you have to prefix its name by '$' in 
+//  MAPINFO.
+//
+//====================================================================
+static bool IsExMy(const char * name)
 {
-	screen->DrawTexture (patch, x, y,
-		DTA_Clean, true,
-		DTA_ShadowAlpha, (gameinfo.gametype == GAME_Doom) ? 0 : FRACUNIT/2,
-		TAG_DONE);
+	// Only check for the first 3 episodes. They are the only ones with default intermission scripts.
+	// Level names can be upper- and lower case so use tolower to check!
+	return (tolower(name[0])=='e' && name[1]>='1' && name[1]<='3' && tolower(name[2])=='m');
 }
 
-static int WI_DrawName (const char *str, int x, int y, bool nomove=false)
+void WI_LoadBackground(bool isenterpic)
 {
-	screen->SetFont (BigFont);
-	if (nomove)
+	const char * lumpname = NULL;
+	char buffer[10];
+	in_anim_t an;
+	lnode_t pt;
+	int texture;
+
+	bcnt=0;
+
+	if (isenterpic)
 	{
-		x = (SCREENWIDTH - x*CleanXfac) / 2;
-		screen->DrawText (CR_UNTRANSLATED, x, y, str,
-			DTA_CleanNoMove, true,
-			DTA_Shadow, gameinfo.gametype == GAME_Doom,
-			TAG_DONE);
+		level_info_t * li = FindLevelInfo(wbs->next);
+		if (li != NULL) lumpname = li->enterpic;
 	}
 	else
 	{
-		x = 160 - x/2;
-		screen->DrawText (CR_UNTRANSLATED, x, y, str,
-			DTA_Clean, true,
-			DTA_Shadow, gameinfo.gametype == GAME_Doom,
-			TAG_DONE);
+		lumpname = level.info->exitpic;
 	}
-	screen->SetFont (SmallFont);
-	return BigFont->GetHeight()*5/4;
-}
-
-static int WI_CalcWidth (const char *str)
-{
-	int w;
 
-	if (!str)
-		return 0;
+	// Try to get a default if nothing specified
+	if (lumpname == NULL || lumpname[0]==0) 
+	{
+		lumpname = NULL;
+		switch(gameinfo.gametype)
+		{
+		case GAME_Doom:
+			if (gamemode != commercial)
+			{
+				char * level = isenterpic? wbs->next : wbs->current;
+				if (IsExMy(level))
+				{
+					sprintf(buffer, "$IN_EPI%c", level[1]);
+					lumpname = buffer;
+				}
+			}
+			if (!lumpname) 
+			{
+				if (isenterpic) 
+				{
+					// One special case needs to be handled here!
+					// If going from E1-E3 to E4 the default should be used, not the exit pic.
 
-	w = BigFont->StringWidth (str);
+					// Not if the exit pic is user defined!
+					if (level.info->exitpic[0]!=0) return;
 
-	return w;
-}
+					// not if the last level is not from the first 3 episodes
+					if (!IsExMy(wbs->current)) return;
 
-// Draws "<Levelname> Finished!"
-void WI_drawLF ()
-{
-	if (gameinfo.gametype == GAME_Doom)
-	{
-		int y;
+					// not if the next level is one of the first 3 episodes
+					if (IsExMy(wbs->next)) return;
+				}
+				lumpname = "INTERPIC";
+			}
+			break;
 
-		if (!lnames[0] && !lnamewidths[0])
-			return;
+		case GAME_Heretic:
+			if (isenterpic)
+			{
+				if (IsExMy(wbs->next))
+				{
+					sprintf(buffer, "$IN_HTC%c", wbs->next[1]);
+					lumpname = buffer;
+				}
+			}
+			if (!lumpname) 
+			{
+				if (isenterpic) return;
+				lumpname = "FLOOR16";
+			}
+			break;
 
-		y = WI_TITLEY;
+		case GAME_Hexen:
+			if (isenterpic) return;
+			lumpname = "INTERPIC";
+			break;
 
-		if (lnames[0])
-		{ // draw <LevelName> 
-			screen->DrawTexture (lnames[0], (320 - lnames[0]->GetWidth()) / 2, y, DTA_Clean, true, TAG_DONE);
-			y += (5*lnames[0]->GetHeight())/4;
-		}
-		else
-		{ // [RH] draw a dynamic title string
-			y += WI_DrawName (lnametexts[0], lnamewidths[0], y);
+		case GAME_Strife:
+		default:
+			// Strife doesn't have an intermission pic so choose something neutral!
+			if (isenterpic) return;
+			lumpname = gameinfo.borderFlat;
+			break;
 		}
-
-		// draw "Finished!"
-		screen->DrawTexture (finished, (320 - finished->GetWidth()) / 2, y, DTA_Clean, true, TAG_DONE);
 	}
-	else
+	if (lumpname == NULL)
 	{
-		WI_DrawName (lnametexts[0], lnamewidths[0], 3);
-		screen->DrawText (CR_UNTRANSLATED,
-			160 - SmallFont->StringWidth ("FINISHED")/2, 25, "FINISHED",
-			DTA_Clean, true, TAG_DONE);
+		// shouldn't happen!
+		background = NULL;
+		return;
 	}
-}
 
-// Draws "Entering <LevelName>"
-void WI_drawEL ()
-{
-	if (gameinfo.gametype == GAME_Doom)
+	lnodes.Clear();
+	anims.Clear();
+	yah.Clear();
+	splat=NULL;
+
+	// a name with a starting '$' indicates an intermission script
+	if (*lumpname!='$')
 	{
-		int y = WI_TITLEY;
+		// The background picture can also be a flat so just using AddPatch doesn't work
+		texture = TexMan.CheckForTexture(lumpname, FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny);
+		if (texture == -1) texture = TexMan.AddPatch(lumpname);
+	}
+	else
+	{
+		int lumpnum=Wads.GetNumForName(lumpname+1);
+		if (lumpnum>=0)
+		{
+			SC_OpenLumpNum(lumpnum,lumpname+1);
+			while (SC_GetString())
+			{
+				memset(&an,0,sizeof(an));
+				int caseval=SC_MustMatchString(WI_Cmd);
+				switch(caseval)
+				{
+				case 0:		// Background
+					SC_MustGetString();
+					texture=TexMan.CheckForTexture(sc_String, FTexture::TEX_MiscPatch,FTextureManager::TEXMAN_TryAny);
+					if (texture == -1) texture = TexMan.AddPatch(sc_String);
+					break;
 
-		if (!lnames[1] && !lnamewidths[1])
-			return;
+				case 1:		// Splat
+					SC_MustGetString();
+					splat=TexMan[TexMan.AddPatch(sc_String)];
+					break;
 
-		y = WI_TITLEY;
+				case 2:		// Pointers
+					while (SC_GetString() && !sc_Crossed)
+					{
+						int v=TexMan.AddPatch(sc_String);
+						yah.Push(TexMan[v]);
+					}
+					if (sc_Crossed) SC_UnGet();
+					break;
 
-		// draw "Entering"
-		screen->DrawTexture (entering, (SCREENWIDTH - entering->GetWidth() * CleanXfac) / 2, y, DTA_CleanNoMove, true, TAG_DONE);
+				case 3:		// Spots
+					SC_MustGetStringName("{");
+					while (!SC_CheckString("}"))
+					{
+						SC_MustGetString();
+						strncpy(pt.level, sc_String,8);
+						pt.level[8]=0;
+						SC_MustGetNumber();
+						pt.x=sc_Number;
+						SC_MustGetNumber();
+						pt.y=sc_Number;
+						lnodes.Push(pt);
+					}
+					break;
 
-		// [RH] Changed to adjust by height of entering patch instead of title
-		y += (5*entering->GetHeight())/4*CleanYfac;
+				case 4:		// IfEntering
+					an.type=ANIM_IFENTERING;
+					goto readanimation;
 
-		if (lnames[1])
-		{ // draw level
-			screen->DrawTexture (lnames[1], (SCREENWIDTH - lnames[1]->GetWidth()*CleanXfac)/2, y, DTA_CleanNoMove, true, TAG_DONE);
+				case 5:		// IfEntering
+					an.type=ANIM_IFNOTENTERING;
+					goto readanimation;
+
+				case 6:		// IfVisited
+					an.type=ANIM_IFVISITED;
+					goto readanimation;
+
+				case 7:		// IfNotVisited
+					an.type=ANIM_IFNOTVISITED;
+					goto readanimation;
+
+				case 8:		// IfLeaving
+					an.type=ANIM_IFLEAVING;
+					goto readanimation;
+				
+				case 9:		// IfNotLeaving
+					an.type=ANIM_IFNOTLEAVING;
+					goto readanimation;
+
+				case 10:	// IfTravelling
+					an.type=ANIM_IFTRAVELLING;
+					SC_MustGetString();
+					strncpy(an.levelname2,sc_String,8);
+					an.levelname2[8]=0;
+					goto readanimation;
+
+				case 11:	// IfNotTravelling
+					an.type=ANIM_IFTRAVELLING;
+					SC_MustGetString();
+					strncpy(an.levelname2,sc_String,8);
+					an.levelname2[8]=0;
+					goto readanimation;
+
+				readanimation:
+					SC_MustGetString();
+					strncpy(an.levelname,sc_String,8);
+					an.levelname[8]=0;
+					SC_MustGetString();
+					caseval=SC_MustMatchString(WI_Cmd);
+
+				default:
+					switch (caseval)
+					{
+					case 12:	// Animation
+						an.type |= ANIM_ALWAYS;
+						SC_MustGetNumber();
+						an.loc.x=sc_Number;
+						SC_MustGetNumber();
+						an.loc.y=sc_Number;
+						SC_MustGetNumber();
+						an.period=sc_Number;
+						an.nexttic = 1 + (M_Random()%an.period);
+						if (SC_GetString())
+						{
+							if (SC_Compare("ONCE"))
+							{
+								an.data=1;
+							}
+							else
+							{
+								SC_UnGet();
+							}
+						}
+						if (!SC_CheckString("{"))
+						{
+							SC_MustGetString();
+							an.p[an.nanims++]=TexMan[TexMan.AddPatch(sc_String)];
+						}
+						else
+						{
+							while (!SC_CheckString("}"))
+							{
+								SC_MustGetString();
+								if (an.nanims<MAX_ANIMATION_FRAMES) an.p[an.nanims++]=TexMan[TexMan.AddPatch(sc_String)];
+							}
+						}
+						an.ctr=-1;
+						anims.Push(an);
+						break;
+
+					case 13:		// Pic
+						an.type |= ANIM_PIC;
+						SC_MustGetNumber();
+						an.loc.x=sc_Number;
+						SC_MustGetNumber();
+						an.loc.y=sc_Number;
+						SC_MustGetString();
+						an.p[0]=TexMan[TexMan.AddPatch(sc_String)];
+						anims.Push(an);
+						break;
+
+					default:
+						SC_ScriptError("Unknown token %s in intermission script", sc_String);
+					}
+				}
+			}
+			SC_Close();
 		}
-		else
-		{ // [RH] draw a dynamic title string
-			WI_DrawName (lnametexts[1], lnamewidths[1], y, true);
+		else 
+		{
+			Printf("Intermission script %s not found!\n", lumpname+1);
+			texture = TexMan.GetTexture("INTERPIC", FTexture::TEX_MiscPatch);
 		}
 	}
-	else
-	{
-		screen->DrawText (CR_UNTRANSLATED,
-			(SCREENWIDTH - SmallFont->StringWidth ("NOW ENTERING:") * CleanXfac)/2, 10, "NOW ENTERING:",
-			DTA_CleanNoMove, true, TAG_DONE);
-		WI_DrawName (lnametexts[1], lnamewidths[1], 10+10*CleanYfac, true);
-	}
+	background=TexMan[texture];
 }
 
-int WI_MapToIndex (char *map, int ep)
+//====================================================================
+// 
+//	made this more generic and configurable through a script
+//	Removed all the ugly special case handling for different game modes
+//
+//====================================================================
+
+void WI_updateAnimatedBack()
 {
 	int i;
 
-	for (i = 0; i < NUMMAPS; i++)
+	for(i=0;i<anims.Size();i++)
 	{
-		if (!strnicmp (names[ep][i], map, 8))
+		in_anim_t * a = &anims[i];
+		switch (a->type & ANIM_TYPE)
+		{
+		case ANIM_ALWAYS:
+			if (bcnt >= a->nexttic)
+			{
+				if (++a->ctr >= a->nanims) 
+				{
+					if (a->data==0) a->ctr = 0;
+					else a->ctr--;
+				}
+				a->nexttic = bcnt + a->period;
+			}
 			break;
+			
+		case ANIM_PIC:
+			a->ctr = 0;
+			break;
+			
+		}
 	}
-	return i;
 }
 
-void WI_drawOnLnode (int n, FTexture *c[], int episode)
-{
+//====================================================================
+// 
+//	Draws the background including all animations
+//
+//====================================================================
 
-	int 	i;
-	int 	left;
-	int 	top;
-	int 	right;
-	int 	bottom;
-	BOOL 	fits = false;
+void WI_drawBackground()
+{
+	int i;
+	int animwidth=320;		// For a flat fill or clear background scale animations to 320x200
+	int animheight=200;
 
-	if (gameinfo.gametype == GAME_Heretic)
+	if (background)
 	{
-		episode += 3;
+		// background
+		if (background->UseType == FTexture::TEX_MiscPatch)
+		{
+			// scale all animations below to fit the size of the base pic
+			// The base pic is always scaled to fit the screen so this allows
+			// placing the animations precisely where they belong on the base pic
+			animwidth = background->GetWidth();
+			animheight = background->GetHeight();
+			screen->DrawTexture(background, 0, 0, DTA_VirtualWidth, animwidth,
+				DTA_VirtualHeight, animheight, TAG_DONE);
+		}
+		else 
+		{
+			screen->FlatFill(0, 0, SCREENWIDTH, SCREENHEIGHT, background);
+		}
 	}
-	i = 0;
-	do
+	else 
 	{
-		right = c[i]->GetWidth();
-		bottom = c[i]->GetHeight();
-		left = lnodes[episode][n].x - c[i]->LeftOffset;
-		top = lnodes[episode][n].y - c[i]->TopOffset;
-		right += left;
-		bottom += top;
+		screen->Clear(0,0, SCREENWIDTH, SCREENHEIGHT, 0);
+	}
 
-		if (left >= 0 && right < 320 && top >= 0 && bottom < 200)
-		{
-			fits = true;
-		}
-		else
+	for(i=0;i<anims.Size();i++)
+	{
+		in_anim_t * a = &anims[i];
+		level_info_t * li;
+
+		switch (a->type & ANIM_CONDITION)
 		{
-			i++;
+		case ANIM_IFVISITED:
+			li = FindLevelInfo(a->levelname);
+			if (li == NULL || !(li->flags & LEVEL_VISITED)) continue;
+			break;
+
+		case ANIM_IFNOTVISITED:
+			li = FindLevelInfo(a->levelname);
+			if (li == NULL || (li->flags & LEVEL_VISITED)) continue;
+			break;
+
+			// StatCount means 'leaving' - everything else means 'entering'!
+		case ANIM_IFENTERING:
+			if (state == StatCount || strnicmp(a->levelname, wbs->next, 8)) continue;
+			break;
+
+		case ANIM_IFNOTENTERING:
+			if (state != StatCount && !strnicmp(a->levelname, wbs->next, 8)) continue;
+			break;
+
+		case ANIM_IFLEAVING:
+			if (state != StatCount || strnicmp(a->levelname, wbs->current, 8)) continue;
+			break;
+
+		case ANIM_IFNOTLEAVING:
+			if (state == StatCount && !strnicmp(a->levelname, wbs->current, 8)) continue;
+			break;
+
+		case ANIM_IFTRAVELLING:
+			if (strnicmp(a->levelname2, wbs->current, 8) || strnicmp(a->levelname, wbs->next, 8)) continue;
+			break;
+
+		case ANIM_IFNOTTRAVELLING:
+			if (!strnicmp(a->levelname2, wbs->current, 8) && !strnicmp(a->levelname, wbs->next, 8)) continue;
+			break;
 		}
-	} while (!fits && i != 2);
+		if (a->ctr >= 0)
+			screen->DrawTexture(a->p[a->ctr], a->loc.x, a->loc.y, 
+								DTA_VirtualWidth, animwidth, DTA_VirtualHeight, animheight, TAG_DONE);
+	}
+}
 
-	if (fits && i<2)
+
+//====================================================================
+//
+// Draws a single character with a shadow
+//
+//====================================================================
+
+static void WI_DrawCharPatch (FTexture *patch, int x, int y)
+{
+	if (patch->UseType!=FTexture::TEX_FontChar)
 	{
-		screen->DrawTexture (c[i], lnodes[episode][n].x, lnodes[episode][n].y, DTA_320x200, true, TAG_DONE);
+		screen->DrawTexture (patch, x, y,
+			DTA_Clean, true,
+			DTA_ShadowAlpha, (gameinfo.gametype == GAME_Doom) ? 0 : FRACUNIT/2,
+			TAG_DONE);
 	}
 	else
-	{ // DEBUG
-		DPrintf ("Could not place patch on level %d", n+1); 
+	{
+		screen->DrawTexture (patch, x, y,
+			DTA_Clean, true,
+			DTA_ShadowAlpha, (gameinfo.gametype == GAME_Doom) ? 0 : FRACUNIT/2,
+			DTA_Translation, BigFont->GetColorTranslation (CR_UNTRANSLATED),	// otherwise it doesn't look good in Strife!
+			TAG_DONE);
 	}
 }
 
-void WI_initAnimatedBack ()
+
+//====================================================================
+//
+// Draws a level name with the big font
+//
+// x is no longer passed as a parameter because the text is now broken into several lines
+// if it is too long
+//
+//====================================================================
+
+int WI_DrawName(int y,const char * levelname, bool nomove=false)
 {
-	int i;
-	in_anim_t *a;
+	int l,i,len=0;
+	const char * p;
+	int h=0;
+	int lastlinelen=0;
+	int lastindex=0;
+	int firstindex=0;
+	int lumph;
 
-	if (gamemode == commercial)
-	{
-		return;
-	}
+	lumph=BigFont->GetHeight();
 
-	if (gameinfo.gametype == GAME_Doom)
+	p=levelname;
+	l=strlen(p);
+	if (!l) return 0;
+
+	screen->SetFont(BigFont);
+	brokenlines_t * lines = V_BreakLines(320, p);
+
+	if (lines)
 	{
-		if (epsd > 2)
+		for (i=0; lines[i].width != -1; i++)
 		{
-			return;
+			if (!nomove)
+			{
+				screen->DrawText(CR_UNTRANSLATED, 160 - lines[i].width/2, y+h, lines[i].string, DTA_Clean, true, TAG_DONE);
+			}
+			else
+			{
+				screen->DrawText(CR_UNTRANSLATED, (SCREENWIDTH - lines[i].width * CleanXfac) / 2, (y+h) * CleanYfac, 
+					lines[i].string, DTA_CleanNoMove, true, TAG_DONE);
+			}
+			h+=lumph;
 		}
-		for (i = 0; i < NUMANIMS[epsd]; i++)
-		{
-			a = &anims[epsd][i];
+		V_FreeBrokenLines(lines);
+	}
+	screen->SetFont(SmallFont);
+	return h+lumph/4;
+}
 
-			// init variables
-			a->ctr = -1;
 
-			// specify the next time to draw it
-			if (a->type == ANIM_ALWAYS)
-				a->nexttic = bcnt + 1 + (M_Random()%a->period);
-			else if (a->type == ANIM_LEVEL)
-				a->nexttic = bcnt + 1;
-		}
+//====================================================================
+//
+// Draws "<Levelname> Finished!"
+//
+// Either uses the specified patch or the big font
+// A level name patch can be specified for all games now, not just Doom.
+//
+//====================================================================
+void WI_drawLF ()
+{
+	int y = WI_TITLEY;
+
+	FTexture * tex = wbs->lname0[0]? TexMan[TexMan.AddPatch(wbs->lname0)] : NULL;
+	
+	// draw <LevelName> 
+	if (tex)
+	{
+		screen->DrawTexture(tex, 160-tex->GetWidth()/2, y, DTA_Clean, true, TAG_DONE);
+		y += tex->GetHeight() + BigFont->GetHeight()/4;
 	}
-	else if (state == ShowNextLoc)
+	else 
 	{
-		if (epsd <= 2)
+		y+=WI_DrawName(y, lnametexts[0]);
+	}
+	
+	// draw "Finished!"
+	if (y < NG_STATSY - screen->Font->GetHeight()*3/4)
+	{
+		// don't draw 'finished' if the level name is too high!
+		if (gameinfo.gametype == GAME_Doom) 
 		{
-			char name[9];
-			sprintf (name, "MAPE%d", epsd + 1);
-			background = TexMan[name];
+			screen->DrawTexture(finished, 160 - finished->GetWidth()/2, y, DTA_Clean, true, TAG_DONE);
+		}
+		else 
+		{
+			screen->SetFont(gameinfo.gametype&GAME_Raven? SmallFont : BigFont);
+			screen->DrawText(CR_WHITE, 160 - screen->Font->StringWidth("finished")/2, y-4, "finished", 
+				DTA_Clean, true, TAG_DONE);
+			screen->SetFont(SmallFont);
 		}
 	}
 }
 
-void WI_DrawDoomBack ()
+
+//====================================================================
+//
+// Draws "Entering <LevelName>"
+//
+// Either uses the specified patch or the big font
+// A level name patch can be specified for all games now, not just Doom.
+//
+//====================================================================
+void WI_drawEL ()
 {
-	char name[9];
+	int y = WI_TITLEY;
 
-	if ((gamemode == commercial) ||
-		(gamemode == retail && epsd >= 3))
-		strcpy (name, "INTERPIC");
-	else 
-		sprintf (name, "WIMAP%d", epsd);
 
-	// background
-	background = TexMan (name);
-	if (FindLevelInfo ("E2M9")->flags & LEVEL_VISITED && epsd == 1)
-	{ // [RH] Add the Fortress of Mystery if it has been visited
-		DrawFoM = true;
-		// anims[1][7].data = 800;	// Don't animate it again if the user uses changemap E2M9
+	// draw "entering"
+	// be careful with the added height so that it works for oversized 'entering' patches!
+	if (gameinfo.gametype == GAME_Doom)
+	{
+		screen->DrawTexture(entering, (SCREENWIDTH - entering->GetWidth() * CleanXfac) / 2, y * CleanYfac, DTA_CleanNoMove, true, TAG_DONE);
+		y += entering->GetHeight() + screen->Font->GetHeight()/4;
 	}
 	else
 	{
-		DrawFoM = false;
-		anims[1][7].data = 8;
+		screen->SetFont(gameinfo.gametype&GAME_Raven? SmallFont : BigFont);
+		screen->DrawText(CR_WHITE, (SCREENWIDTH - screen->Font->StringWidth("now entering:") * CleanXfac) / 2, y * CleanYfac, 
+			"now entering:", DTA_CleanNoMove, true, TAG_DONE);
+		y += screen->Font->GetHeight()*5/4;
+		screen->SetFont(SmallFont);
 	}
-}
-
-void WI_updateAnimatedBack (void)
-{
-	int i;
-	in_anim_t *a;
 
-	if (gameinfo.gametype != GAME_Doom ||
-		gamemode == commercial ||
-		epsd > 2)
+	// draw <LevelName>
+	FTexture * tex = wbs->lname1[0]? TexMan[TexMan.AddPatch(wbs->lname1)] : NULL;
+	if (tex)
 	{
-		return;
+		screen->DrawTexture(tex, (SCREENWIDTH - tex->GetWidth() * CleanXfac) / 2, y * CleanYfac, DTA_CleanNoMove, true, TAG_DONE);
 	}
-
-	for (i = 0; i < NUMANIMS[epsd]; i++)
+	else
 	{
-		a = &anims[epsd][i];
-
-		if (bcnt == a->nexttic)
-		{
-			switch (a->type)
-			{
-			case ANIM_ALWAYS:
-				if (++a->ctr >= a->nanims)
-					a->ctr = 0;
-				a->nexttic = bcnt + a->period;
-				break;
+		WI_DrawName(y, lnametexts[1], true);
+	}
+}
 
-			case ANIM_LEVEL:
-				// gawd-awful hack for level anims
 
-				if (!(state == StatCount && i == 7)
-					&& WI_MapToIndex (wbs->next, wbs->next_ep) == a->data)
-				{
-					a->ctr++;
-					if (a->ctr == a->nanims)
-						a->ctr--;
-					a->nexttic = bcnt + a->period;
-				}
+//====================================================================
+//
+// Draws the splats and the 'You are here' arrows
+//
+//====================================================================
 
-				break;
-			}
-		}
+int WI_MapToIndex (char *map)
+{
+	int i;
 
+	for (i = 0; i < lnodes.Size(); i++)
+	{
+		if (!strnicmp (lnodes[i].level, map, 8))
+			break;
 	}
+	return i;
 }
 
-void WI_drawAnimatedBack (void)
-{
-	int i;
-	in_anim_t *a;
 
-	WI_slamBackground ();
+//====================================================================
+//
+// Draws the splats and the 'You are here' arrows
+//
+//====================================================================
 
-	if (gameinfo.gametype == GAME_Doom &&
-		gamemode != commercial &&
-		epsd <= 2)
+void WI_drawOnLnode( int   n, FTexture * c[] ,int numc)
+{
+	int   i;
+	for(i=0;i<numc;i++)
 	{
-		for (i = 0; i < NUMANIMS[epsd]; i++)
-		{
-			a = &anims[epsd][i];
+		int            left;
+		int            top;
+		int            right;
+		int            bottom;
 
-			if (a->ctr >= 0)
-				screen->DrawTexture (a->p[a->ctr], a->loc.x, a->loc.y, DTA_320x200, true, TAG_DONE);
+
+		right = c[i]->GetWidth();
+		bottom = c[i]->GetHeight();
+		left = lnodes[n].x - c[i]->LeftOffset;
+		top = lnodes[n].y - c[i]->TopOffset;
+		right += left;
+		bottom += top;
+		
+		if (left >= 0 && right < 320 && top >= 0 && bottom < 200) 
+		{
+			screen->DrawTexture (c[i], lnodes[n].x, lnodes[n].y, DTA_320x200, true, TAG_DONE);
+			break;
 		}
-	}
+	} 
 }
 
+// ====================================================================
 //
 // Draws a number.
 // If digits > 0, then use that many digits minimum,
 //	otherwise only use as many as necessary.
 // Returns new x position.
 //
-
+// ====================================================================
 int WI_drawNum (int x, int y, int n, int digits, bool leadingzeros = true)
 {
-	// if non-number, do not draw it
-	if (n == 1994)
-		return 0;
-
 	int fontwidth = num[3]->GetWidth();
 	int xofs;
 	char text[8];
@@ -739,6 +938,12 @@
 
 }
 
+// ====================================================================
+//
+//
+//
+// ====================================================================
+
 void WI_drawPercent (int x, int y, int p, int b)
 {
 	if (p < 0)
@@ -746,7 +951,8 @@
 
 	if (wi_percents)
 	{
-		screen->DrawTexture (percent, x, y, DTA_Clean, true, TAG_DONE);
+		WI_DrawCharPatch (percent, x, y);
+
 		if (b == 0)
 			WI_drawNum (x, y, 100, -1, false);
 		else
@@ -763,42 +969,42 @@
 	}
 }
 
-
-
+//====================================================================
 //
 // Display level completion time and par, or "sucks" message if overflow.
 //
-void WI_drawTime (int x, int y, int t)
+//====================================================================
+void WI_drawTime (int x, int y, int t, bool no_sucks=false)
 {
 	if (t<0)
 		return;
 
-	if (t <= 61*59 || gameinfo.gametype != GAME_Doom)
+	if (no_sucks || !wi_sucks || (t <= 61*59 || gameinfo.gametype != GAME_Doom))
 	{
 		int hours = t / 3600;
 		t -= hours * 3600;
 		int minutes = t / 60;
 		t -= minutes * 60;
 		int seconds = t;
-		/*
-		int numspacing = SHORT(num[0]->width)*2;
-		int spacing = numspacing + SHORT(colon->width);
-		*/
 
-		x -= 34*2 + 26;
+		// Why were these offsets hard coded? Half the WADs with custom patches
+		// I tested screwed up miserably in this function!
+		int num_spacing = num[3]->GetWidth();
+		int colon_spacing = colon->GetWidth();
+
+		x -= 2*num_spacing;
+		WI_drawNum (x, y, seconds, 2);
+		x -= colon_spacing;
+		WI_DrawCharPatch (colon, x , y);
+		x -= 2*num_spacing ;
+		WI_drawNum (x, y, minutes, 2, hours!=0);
 		if (hours)
 		{
+			x -= colon_spacing;
+			WI_DrawCharPatch (colon, x , y);
+			x -= 2*num_spacing ;
 			WI_drawNum (x, y, hours, 2);
-			WI_DrawCharPatch (colon, x + 26, y);
 		}
-		x += 34;
-		if (minutes | hours)
-		{
-			WI_drawNum (x, y, minutes, 2);
-		}
-		x += 34;
-		WI_DrawCharPatch (colon, x - 8, y);
-		WI_drawNum (x, y, seconds, 2);
 	}
 	else
 	{ // "sucks"
@@ -840,18 +1046,18 @@
 
 void WI_initShowNextLoc ()
 {
-	state = ShowNextLoc;
-	acceleratestage = 0;
-	cnt = SHOWNEXTLOCDELAY * TICRATE;
-
-	if (epsd != wbs->next_ep && gameinfo.gametype == GAME_Doom)
+	if (wbs->next_ep == -1) 
 	{
-		WI_unloadData ();
-		epsd = wbs->next_ep;
-		WI_loadData ();
+		// Last map in episode - there is no next location!
+		WI_End();
+		G_WorldDone();
+		return;
 	}
 
-	WI_initAnimatedBack();
+	state = ShowNextLoc;
+	acceleratestage = 0;
+	cnt = SHOWNEXTLOCDELAY * TICRATE;
+	WI_LoadBackground(true);
 }
 
 void WI_updateShowNextLoc ()
@@ -864,42 +1070,38 @@
 		snl_pointeron = (cnt & 31) < 20;
 }
 
-void WI_drawShowNextLoc ()
+void WI_drawShowNextLoc(void)
 {
-	int i;
-
-	// draw animated background
-	WI_drawAnimatedBack (); 
+	int   i;
+	
+	WI_drawBackground();
 
-	if (gamemode != commercial)
+	if (splat)
 	{
-		if (!(gameinfo.gametype & (GAME_Doom|GAME_Heretic)) || epsd > 2)
-		{
-			WI_drawEL();
-			return;
-		}
-
-		// draw a splat on taken cities.
-		for (i = 0; i < NUMMAPS; i++)
+		for (i=0 ; i<lnodes.Size() ; i++) 
 		{
-			if (FindLevelInfo (names[epsd][i])->flags & LEVEL_VISITED)
-				WI_drawOnLnode (i, &splat, epsd);
+			level_info_t * li = FindLevelInfo (lnodes[i].level);
+			if (li && li->flags & LEVEL_VISITED) WI_drawOnLnode(i, &splat,1);  // draw a splat on taken cities.
 		}
-
-		// draw flashing ptr
-		if (snl_pointeron)
-			WI_drawOnLnode (WI_MapToIndex (wbs->next, wbs->next_ep), yah, wbs->next_ep); 
+	}
+		
+	// draw flashing ptr
+	if (snl_pointeron && yah.Size())
+	{
+		unsigned int v = WI_MapToIndex (wbs->next);
+		// Draw only if it points to a valid level on the current screen!
+		if (v<lnodes.Size()) WI_drawOnLnode (v, &yah[0], yah.Size()); 
 	}
 
 	// draws which level you are entering..
-	WI_drawEL();  
+	WI_drawEL ();  
 
 }
 
 void WI_drawNoState ()
 {
 	snl_pointeron = true;
-	WI_drawShowNextLoc ();
+	WI_drawShowNextLoc();
 }
 
 int WI_fragSum (int playernum)
@@ -948,8 +1150,6 @@
 			dm_totals[i] = 0;
 		}
 	}
-	
-	WI_initAnimatedBack();
 }
 
 void WI_updateDeathmatchStats ()
@@ -965,7 +1165,7 @@
 	{
 		/*
 		acceleratestage = 0;
-
+		
 		for (i=0 ; i<MAXPLAYERS ; i++)
 		{
 			if (playeringame[i])
@@ -973,8 +1173,8 @@
 				for (j=0 ; j<MAXPLAYERS ; j++)
 					if (playeringame[j])
 						dm_frags[i][j] = plrs[i].frags[j];
-
-				dm_totals[i] = WI_fragSum(i);
+					
+					dm_totals[i] = WI_fragSum(i);
 			}
 		}
 		
@@ -982,7 +1182,8 @@
 		*/
 		dm_state = 4;
 	}
-
+	
+    
 	if (dm_state == 2)
 	{
 		/*
@@ -1060,12 +1261,12 @@
 {
 
 	// draw animated background
-	WI_drawAnimatedBack(); 
+	WI_drawBackground(); 
 	WI_drawLF();
 
 	// [RH] Draw heads-up scores display
 	HU_DrawScores (&players[me]);
-	
+
 /*
 	int 		i;
 	int 		j;
@@ -1146,8 +1347,8 @@
 }
 
 static int cnt_frags[MAXPLAYERS];
-static int dofrags;
-static int ng_state;
+static int    dofrags;
+static int    ng_state;
 
 void WI_initNetgameStats ()
 {
@@ -1171,12 +1372,8 @@
 	}
 
 	dofrags = !!dofrags;
-
-	WI_initAnimatedBack ();
 }
 
-
-
 void WI_updateNetgameStats ()
 {
 
@@ -1313,10 +1510,7 @@
 		if (acceleratestage)
 		{
 			S_Sound (CHAN_VOICE, PASTSTATS, 1, ATTN_NONE);
-			if ( gamemode == commercial )
-				WI_initNoState();
-			else
-				WI_initShowNextLoc();
+			WI_initShowNextLoc();
 		}
 	}
 	else if (ng_state & 1)
@@ -1335,7 +1529,7 @@
 	int pwidth = percent->GetWidth();
 
 	// draw animated background
-	WI_drawAnimatedBack(); 
+	WI_drawBackground(); 
 
 	WI_drawLF();
 
@@ -1384,25 +1578,46 @@
 			y += WI_SPACINGY;
 		}
 	}
-	else
+	else 
 	{
-		screen->SetFont (BigFont);
-		screen->DrawText (CR_UNTRANSLATED, 95, 35, "KILLS", DTA_Clean, true, DTA_Shadow, true, TAG_DONE);
-		screen->DrawText (CR_UNTRANSLATED, 155, 35, "BONUS", DTA_Clean, true, DTA_Shadow, true, TAG_DONE);
-		screen->DrawText (CR_UNTRANSLATED, 232, 35, "SECRET", DTA_Clean, true, DTA_Shadow, true, TAG_DONE);
-		WI_drawLF ();
+		if (gameinfo.gametype & GAME_Raven)
+		{
+			screen->SetFont (BigFont);
+			screen->DrawText (CR_UNTRANSLATED, 95, 35, "KILLS", DTA_Clean, true, DTA_Shadow, true, TAG_DONE);
+			screen->DrawText (CR_UNTRANSLATED, 155, 35, "BONUS", DTA_Clean, true, DTA_Shadow, true, TAG_DONE);
+			screen->DrawText (CR_UNTRANSLATED, 232, 35, "SECRET", DTA_Clean, true, DTA_Shadow, true, TAG_DONE);
+			y = 50;
+		}
+		else
+		{
+			screen->SetFont (SmallFont);
+			screen->DrawText (CR_UNTRANSLATED, 95, 50, "KILLS", DTA_Clean, true, DTA_Shadow, true, TAG_DONE);
+			screen->DrawText (CR_UNTRANSLATED, 155, 50, "BONUS", DTA_Clean, true, DTA_Shadow, true, TAG_DONE);
+			screen->DrawText (CR_UNTRANSLATED, 232, 50, "SECRET", DTA_Clean, true, DTA_Shadow, true, TAG_DONE);
+			y = 62;
+		}
+		WI_drawLF ();	
 
-		y = 50;
 		for (i = 0; i < MAXPLAYERS; i++)
 		{
 			if (y >= 200-WI_SPACINGY)
 				break;
 			if (!playeringame[i])
 				continue;
-			screen->DrawTexture (star, 25, y,
-				DTA_Translation, translationtables[TRANSLATION_Players] + i*256,
-				DTA_Clean, true,
-				TAG_DONE);
+			if (gameinfo.gametype == GAME_Heretic)
+			{
+				screen->DrawTexture (star, 25, y,
+					DTA_Translation, translationtables[TRANSLATION_Players] + i*256,
+					DTA_Clean, true,
+					TAG_DONE);
+			}
+			else	// Hexen and Strife don't have a face graphic for this.
+			{
+				char pstr[3]={'P', '1'+i};
+				screen->SetFont (BigFont);
+				screen->DrawText(CR_UNTRANSLATED, 25, y+10, pstr, DTA_Clean, true, TAG_DONE);
+			}
+
 			WI_drawPercent (127, y+10, cnt_kills[i], wbs->maxkills);
 			if (ng_state >= 4)
 			{
@@ -1414,10 +1629,11 @@
 			}
 			y += 37;
 		}
+		screen->SetFont (SmallFont);
 	}
 }
 
-static int sp_state;
+static int  sp_state;
 
 void WI_initStats ()
 {
@@ -1427,8 +1643,8 @@
 	cnt_kills[0] = cnt_items[0] = cnt_secret[0] = -1;
 	cnt_time = cnt_par = -1;
 	cnt_pause = TICRATE;
-
-	WI_initAnimatedBack();
+	
+	cnt_total_time = -1;
 }
 
 void WI_updateStats ()
@@ -1449,6 +1665,7 @@
 		cnt_secret[0] = plrs[me].ssecret;
 		cnt_time = plrs[me].stime / TICRATE;
 		cnt_par = wbs->partime / TICRATE;
+	    cnt_total_time = wbs->totaltime / TICRATE;
 	}
 
 	if (sp_state == 2)
@@ -1508,11 +1725,15 @@
 
 			cnt_time += 3;
 			cnt_par += 3;
+			cnt_total_time += 3;
 		}
 
 		if (cnt_time >= plrs[me].stime / TICRATE)
 			cnt_time = plrs[me].stime / TICRATE;
 
+		if (cnt_total_time >= wbs->totaltime / TICRATE)
+			cnt_total_time = wbs->totaltime / TICRATE;
+
 		if (cnt_par >= wbs->partime / TICRATE)
 		{
 			cnt_par = wbs->partime / TICRATE;
@@ -1529,11 +1750,7 @@
 		if (acceleratestage)
 		{
 			S_Sound (CHAN_VOICE, PASTSTATS, 1, ATTN_NONE);
-
-			if (gamemode == commercial)
-				WI_initNoState();
-			else
-				WI_initShowNextLoc();
+			WI_initShowNextLoc();
 		}
 	}
 	else if (sp_state & 1)
@@ -1554,10 +1771,10 @@
 	lh = (3*num[0]->GetHeight())/2;
 
 	// draw animated background
-	WI_drawAnimatedBack();
-
+	WI_drawBackground(); 
+	
 	WI_drawLF();
-
+	
 	if (gameinfo.gametype == GAME_Doom)
 	{
 		screen->DrawTexture (kills, SP_STATSX, SP_STATSY, DTA_Clean, true, TAG_DONE);
@@ -1571,12 +1788,17 @@
 
 		screen->DrawTexture (timepic, SP_TIMEX, SP_TIMEY, DTA_Clean, true, TAG_DONE);
 		WI_drawTime (160 - SP_TIMEX, SP_TIMEY, cnt_time);
+		if (wi_totaltime)
+		{
+			WI_drawTime (160 - SP_TIMEX, SP_TIMEY + lh, cnt_total_time, true);	// no 'sucks' for total time ever!
+		}
 
 		if (wbs->partime)
 		{
 			screen->DrawTexture (par, 160 + SP_TIMEX, SP_TIMEY, DTA_Clean, true, TAG_DONE);
 			WI_drawTime (320 - SP_TIMEX, SP_TIMEY, cnt_par);
 		}
+
 	}
 	else
 	{
@@ -1585,31 +1807,24 @@
 		screen->DrawText (CR_UNTRANSLATED, 50, 90, "ITEMS", DTA_Clean, true, DTA_Shadow, true, TAG_DONE);
 		screen->DrawText (CR_UNTRANSLATED, 50, 115, "SECRETS", DTA_Clean, true, DTA_Shadow, true, TAG_DONE);
 
+		int slashpos = gameinfo.gametype==GAME_Strife? 235:237;
+		int countpos = gameinfo.gametype==GAME_Strife? 185:200;
 		if (sp_state >= 2)
 		{
-			WI_drawNum (200, 65, cnt_kills[0], 3, false);
-			screen->DrawTexture (slash, 237, 65,
-				DTA_ShadowAlpha, FRACUNIT/2,
-				DTA_Clean, true,
-				TAG_DONE);
+			WI_drawNum (countpos, 65, cnt_kills[0], 3, false);
+			WI_DrawCharPatch (slash, slashpos, 65);
 			WI_drawNum (248, 65, wbs->maxkills, 3, false);
 		}
 		if (sp_state >= 4)
 		{
-			WI_drawNum (200, 90, cnt_items[0], 3, false);
-			screen->DrawTexture (slash, 237, 90,
-				DTA_ShadowAlpha, FRACUNIT/2,
-				DTA_Clean, true,
-				TAG_DONE);
+			WI_drawNum (countpos, 90, cnt_items[0], 3, false);
+			WI_DrawCharPatch (slash, slashpos, 90);
 			WI_drawNum (248, 90, wbs->maxitems, 3, false);
 		}
 		if (sp_state >= 6)
 		{
-			WI_drawNum (200, 115, cnt_secret[0], 3, false);
-			screen->DrawTexture (slash, 237, 115,
-				DTA_ShadowAlpha, FRACUNIT/2,
-				DTA_Clean, true,
-				TAG_DONE);
+			WI_drawNum (countpos, 115, cnt_secret[0], 3, false);
+			WI_DrawCharPatch (slash, slashpos, 115);
 			WI_drawNum (248, 115, wbs->maxsecret, 3, false);
 		}
 		if (sp_state >= 8)
@@ -1617,12 +1832,25 @@
 			screen->DrawText (CR_UNTRANSLATED, 85, 160, "TIME",
 				DTA_Clean, true, DTA_Shadow, true, TAG_DONE);
 			WI_drawTime (249, 160, cnt_time);
+			if (wi_totaltime)
+			{
+				WI_drawTime (249, 180, cnt_total_time);
+			}
 		}
 		screen->SetFont (SmallFont);
 	}
 }
 
-void WI_checkForAccelerate (void)
+// ====================================================================
+// WI_checkForAccelerate
+// Purpose: See if the player has hit either the attack or use key
+//          or mouse button.  If so we set acceleratestage to 1 and
+//          all those display routines above jump right to the end.
+// Args:    none
+// Returns: void
+//
+// ====================================================================
+void WI_checkForAccelerate(void)
 {
 	int i;
 	player_t *player;
@@ -1643,138 +1871,64 @@
 	}
 }
 
-// Updates stuff each tick
-void WI_Ticker ()
+// ====================================================================
+// WI_Ticker
+// Purpose: Do various updates every gametic, for stats, animation,
+//          checking that intermission music is running, etc.
+// Args:    none
+// Returns: void
+//
+// ====================================================================
+void WI_Ticker(void)
 {
 	// counter for general background animation
 	bcnt++;  
-
+	
 	if (bcnt == 1)
 	{
-		// intermission music
-		if (gameinfo.gametype == GAME_Heretic)
+		// intermission music - use the defaults if none specified
+		if (level.info->intermusic[0]) 
+			S_ChangeMusic(level.info->intermusic);
+		else if (gameinfo.gametype == GAME_Heretic)
 			S_ChangeMusic ("mus_intr");
 		else if (gameinfo.gametype == GAME_Hexen)
 			S_ChangeMusic ("hub");
+		else if (gameinfo.gametype == GAME_Strife)	// Strife also needs a default!
+			S_ChangeMusic ("d_slide");
 		else if (gamemode == commercial)
 			S_ChangeMusic ("d_dm2int");
 		else
 			S_ChangeMusic ("d_inter"); 
-	}
-
-	WI_checkForAccelerate ();
 
+	}
+	
+	WI_checkForAccelerate();
+	
 	switch (state)
 	{
-	case StatCount:
-		if (deathmatch)
-			WI_updateDeathmatchStats ();
-		else if (multiplayer)
-			WI_updateNetgameStats ();
-		else
-			WI_updateStats ();
+    case StatCount:
+		if (deathmatch) WI_updateDeathmatchStats();
+		else if (multiplayer) WI_updateNetgameStats();
+		else WI_updateStats();
 		break;
-	
-	case ShowNextLoc:
-		WI_updateShowNextLoc ();
+		
+    case ShowNextLoc:
+		WI_updateShowNextLoc();
 		break;
-	
-	case NoState:
-		WI_updateNoState ();
+		
+    case NoState:
+		WI_updateNoState();
 		break;
-
-	case LeavingIntermission:
-		break;	// Silence GCC
 	}
 }
 
-void WI_loadData ()
+void WI_loadData(void)
 {
-	int i, j;
+	int i;
 	char name[9];
-	in_anim_t *a;
-
-	if (gameinfo.gametype != GAME_Doom)
-	{
-		for (i = 0; i < 2; i++)
-		{
-			lnames[i] = NULL;
-			lnametexts[i] = G_MaybeLookupLevelName (FindLevelInfo (i == 0 ? wbs->current : wbs->next));
-			lnamewidths[i] = WI_CalcWidth (lnametexts[i]);
-		}
-		if (gameinfo.gametype == GAME_Hexen)
-		{
-			background = TexMan["INTERPIC"];
-			return;
-		}
-	}
-
-	if (gameinfo.gametype == GAME_Doom && gamemode != commercial && epsd < 3)
-	{
-		for (j = 0; j < NUMANIMS[epsd]; j++)
-		{
-			a = &anims[epsd][j];
-			for (i = 0; i < a->nanims; i++)
-			{
-				// MONDO HACK!
-				if (epsd != 1 || j != 8) 
-				{
-					// animations
-					sprintf (name, "WIA%d%.2d%.2d", epsd, j, i);  
-					a->p[i] = TexMan[name];
-				}
-				else
-				{
-					// HACK ALERT!
-					a->p[i] = anims[1][4].p[i];
-				}
-			}
-		}
-	}
-
-	if (gameinfo.gametype == GAME_Doom)
-	{
-		WI_DrawDoomBack ();
-
-		for (i = 0; i < 2; i++)
-		{
-			char *lname = (i == 0 ? wbs->lname0 : wbs->lname1);
-
-			j = lname && lname[0] ? TexMan.CheckForTexture (lname, FTexture::TEX_MiscPatch) : -1;
-
-			if (j > 0)
-			{
-				lnames[i] = TexMan[j];
-			}
-			else
-			{
-				lnames[i] = NULL;
-				if (i == 1 && strncmp (wbs->next, "enDSeQ", 6) == 0)
-				{
-					lnametexts[i] = NULL;
-					lnamewidths[i] = 0;
-				}
-				else
-				{
-					lnametexts[i] = G_MaybeLookupLevelName (FindLevelInfo (i == 0 ? wbs->current : wbs->next));
-					lnamewidths[i] = WI_CalcWidth (lnametexts[i]);
-				}
-			}
-		}
-	}
-	else
-	{
-		background = NULL;
-	}
 
 	if (gameinfo.gametype == GAME_Doom)
 	{
-		if (gamemode != commercial)
-		{
-			yah[0] = TexMan["WIURH0"];	// you are here
-			yah[1] = TexMan["WIURH1"];	// you are here (alt.]
-			splat = TexMan["WISPLAT"];	// splat
-		}
 		wiminus = TexMan["WIMINUS"];		// minus sign
 		percent = TexMan["WIPCNT"];		// percent sign
 		finished = TexMan["WIF"];		// "finished"
@@ -1801,17 +1955,23 @@
 			num[i] = TexMan[name];
 		}
 	}
-	else
+	else if (gameinfo.gametype & GAME_Raven)
 	{
-		yah[0] =
-		yah[1] = TexMan["IN_YAH"];
-		splat = TexMan["IN_X"];
 		wiminus = TexMan["FONTB13"];
 		percent = TexMan["FONTB05"];
 		colon = TexMan["FONTB26"];
 		slash = TexMan["FONTB15"];
-		star = TexMan["FACEA0"];
-		bstar = TexMan["FACEB0"];
+		if (gameinfo.gametype==GAME_Heretic)
+		{
+			star = TexMan["FACEA0"];
+			bstar = TexMan["FACEB0"];
+		}
+		else
+		{
+			int dummywidth;
+			star = BigFont->GetChar('*', &dummywidth);	// just a dummy to avoid an error if it is being used
+			bstar = star;
+		}
 
 		for (i = 0; i < 10; i++)
 		{
@@ -1819,6 +1979,29 @@
 			num[i] = TexMan[name];
 		}
 	}
+	else // Strife needs some handling, too!
+	{
+		int dummywidth;
+		wiminus = BigFont->GetChar('-', &dummywidth);
+		percent = BigFont->GetChar('%', &dummywidth);
+		colon = BigFont->GetChar(':', &dummywidth);
+		slash = BigFont->GetChar('/', &dummywidth);
+		star = BigFont->GetChar('*', &dummywidth);	// just a dummy to avoid an error if it is being used
+		bstar = star;
+		for (i = 0; i < 10; i++)
+		{
+			num[i] = BigFont->GetChar('0'+i, &dummywidth);
+		}
+	}
+
+	// Use the local level structure which can be overridden by hubs if they eventually get names!
+	lnametexts[0] = level.level_name;		
+
+	level_info_t * li = FindLevelInfo(wbs->next);
+	if (li) lnametexts[1] = G_MaybeLookupLevelName(li);
+	else lnametexts[1]=NULL;
+
+	WI_LoadBackground(false);
 }
 
 void WI_unloadData ()
@@ -1857,13 +2040,10 @@
 void WI_initVariables (wbstartstruct_t *wbstartstruct)
 {
 	wbs = wbstartstruct;
-
 	acceleratestage = 0;
 	cnt = bcnt = 0;
 	me = wbs->pnum;
 	plrs = wbs->plyr;
-	epsd = wbs->finished_ep;
-	DrawFoM = false;
 }
 
 void WI_Start (wbstartstruct_t *wbstartstruct)
diff -durN 96/src/wi_stuff.h 96x/src/wi_stuff.h
--- 96/src/wi_stuff.h	2004-11-02 22:36:21.000000000 +0100
+++ 96x/src/wi_stuff.h	2005-06-22 15:18:34.000000000 +0200
@@ -60,6 +60,9 @@
 
 	// the par time
 	int			partime;
+
+	// total time for the entire current game
+	int			totaltime;
 	
 	// index of this player in game
 	int			pnum;	
diff -durN 96/src/win32/i_input.cpp 96x/src/win32/i_input.cpp
--- 96/src/win32/i_input.cpp	2004-12-10 21:32:10.000000000 +0100
+++ 96x/src/win32/i_input.cpp	2005-07-24 20:12:09.000000000 +0200
@@ -308,6 +308,10 @@
 CVAR (Float, joy_sidespeed,		 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
 CVAR (Float, joy_upspeed,		-1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
 
+// set these to false to make keypad-enter a usable separate key!
+CVAR (Bool, k_mergekeys, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
+CVAR (Bool, k_allowfullscreentoggle, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
+
 static FBaseCVar * const JoyConfigVars[] =
 {
 	&joy_xaxis, &joy_yaxis, &joy_zaxis, &joy_xrot, &joy_yrot, &joy_zrot, &joy_slider, &joy_dial,
@@ -650,7 +654,7 @@
 			event.data2 = 1;
 			D_PostEvent (&event);
 		}
-		if (wParam == '\r')
+		if (wParam == '\r' && k_allowfullscreentoggle)
 		{
 			ToggleFullscreen = !ToggleFullscreen;
 		}
@@ -1847,18 +1851,21 @@
 	toState[DIK_TAB] = fromState[DIK_TAB];
 	toState[DIK_NUMLOCK] = fromState[DIK_NUMLOCK];
 
-	// "Merge" multiple keys that are considered to be the same.
-	// Also clear out the alternate versions after merging.
-	toState[DIK_RETURN]		|= toState[DIK_NUMPADENTER];
-	toState[DIK_LMENU]		|= toState[DIK_RMENU];
-	toState[DIK_LCONTROL]	|= toState[DIK_RCONTROL];
-	toState[DIK_LSHIFT]		|= toState[DIK_RSHIFT];
-
-	toState[DIK_NUMPADENTER] = 0;
-	toState[DIK_RMENU]		 = 0;
-	toState[DIK_RCONTROL]	 = 0;
-	toState[DIK_RSHIFT]		 = 0;
-
+	if (k_mergekeys)
+	{
+		// "Merge" multiple keys that are considered to be the same.
+		// Also clear out the alternate versions after merging.
+		toState[DIK_RETURN]		|= toState[DIK_NUMPADENTER];
+		toState[DIK_LMENU]		|= toState[DIK_RMENU];
+		toState[DIK_LCONTROL]	|= toState[DIK_RCONTROL];
+		toState[DIK_LSHIFT]		|= toState[DIK_RSHIFT];
+	
+		toState[DIK_NUMPADENTER] = 0;
+		toState[DIK_RMENU]		 = 0;
+		toState[DIK_RCONTROL]	 = 0;
+		toState[DIK_RSHIFT]		 = 0;
+	}
+	
 	// Now generate events for any keys that differ between the states
 	if (!GUICapture)
 	{
diff -durN 96/src/win32/i_main.cpp 96x/src/win32/i_main.cpp
--- 96/src/win32/i_main.cpp	2004-12-24 16:42:11.000000000 +0100
+++ 96x/src/win32/i_main.cpp	2005-07-29 12:26:12.000000000 +0200
@@ -404,7 +404,7 @@
 	const char *arg;
 	int i;
 
-	text += wsprintf (text, "\r\nZDoom version " DOTVERSIONSTR "\r\n");
+	text += wsprintf (text, "\r\nZDoom version " DOTVERSIONSTR " (" __DATE__ ")\r\n");
 
 	text += wsprintf (text, "\r\nCommand line:\r\n");
 	for (i = 0; i < Args.NumArgs(); ++i)
Binární soubory 96/src/win32/zdoom.aps a 96x/src/win32/zdoom.aps jsou rùzné
